/*****************************************************************************/
/*                                                                           */
/*                                                                           */
/* Program name:  completeusersproof.c                                       */
/*                                                                           */
/* Copyright (C) 2017 NORMAN MEGILL nm at alum.mit.edu http://metamath.org   */
/* (for the metamath.c base code)and ALAN SARE  alansare at alumni.cmu.edu   */
/* (for the completeusersproof() function and functions it                   */
/* (directly) calls).                                                        */
/*                                                                           */
/* License terms: GNU General Public License                                 */
/*                                                                           */
/* completeusersproof is a proof assistant designed to automatically         */
/* complete Virtual Deduction Users' Proofs. A Virtual Deduction proof is    */
/* a Metamath-specific Natural Deduction proof. A Virtual Deduction User's   */
/* Proof uses Virtual Deduction Notation. Generally, a Virtual Deduction     */
/* proof lacks some of the detail needed for it to be (after translation     */
/* into conventional notation) a complete Metamath proof for which a         */
/* Metamath RPN proof may be generated. completeusersproof automatically     */
/* generates these missing details. For example,                             */
/* https://www.virtualdeduction.com/chordthmaltvd.html is a Virtual          */
/* Deduction User's Proof. From it, completeusersproof has automatically     */
/* generated chordthmALT , a completed Metamath Proof.                       */
/*                                                                           */
/* completeusersproof may be run without GUIs or through a GUI run in        */
/* parallel with two loaded instances of mmj2, each with a GUI. One of these */
/* two instances of mmj2 may be called as a service by completeusersproof    */
/* when the mmj2Unify() function of completeusersproof is called.            */
/*                                                                           */
/* Running through the completeusersproof GUI may not be possible using a    */
/* non-Windows operating system incompatible with AutoHotkey scripts.        */
/*                                                                           */
/* If completeusersproof is run by pressing the GUI button                   */
/* "Completeusersproof", it completes each subproof completable by the mmj2  */
/* unification means by that means. It attempts to complete each remaining   */
/* subproof by the unification theorem means, completing those which are     */
/* completable. If the User presses the GUI button "Completeusersproof smv", */
/* it completes each subproof completable by the mmj2 unification means by   */
/* that means. It completes each remaining subproof completable by the       */
/* single metavariable deduction unification means by that means. It atempts */
/* to complete each remaining subproof by the unification theorem means,     */
/* completing those which are completable. If the User presses the GUI       */
/* button "Completeusersproof mv", it completes each subproof completable by */
/* the mmj2 unification means by that means. It completes each remaining     */
/* subproof completable by the metavariable deduction unification means by   */
/* that means. It attempts to complete each remaining subproof by the        */
/* unification theorem means, completing those which are completable. For    */
/* the definitions of the mmj2 unification means, the unification theorem    */
/* means, the single metavariable deduction unification means, and the       */
/* metavariable deduction unification means, see the comments at the top of  */
/* completeusersproof().                                                     */
/*                                                                           */
/* completeusersproof makes the stepprover() function available to the User  */
/* as a stand-alone function. The User may open a User's Proof in the mmj2   */
/* GUI and apply the "Stepprover" command by pressing the "Stepprover"       */
/* button. completeusersproof will then attempt to 2-step prove any          */
/* 0-hypothesis proof steps which do not unify with a theorem in set.mm. If  */
/* there exists in set.mm some theorem which is a semantic variation of such */
/* a step and if there exists in set.mm some 1-hypothesis deduction which    */
/* deduces the proof step from that semantic variation, then                 */
/* completeusersproof will 2-step prove that proof step. This stand-alone    */
/* stepprover capability of completeusersproof is intended to be used for    */
/* non-Virtual Deduction User's Proofs. For Virtual Deduction proofs, the    */
/* stepprover() function is utilized along with other functions upon         */
/* application of either the "Completeusersproof" command, the               */
/* "Completeusersproof smv" command, or the "Completeusersproof mv" command. */
/* If the "Completeusersproof" command, the "Completeusersproof smv"         */
/* command, or the "Completeusersproof mv" command is applied to a User's    */
/* Proof not written as a Virtual Deduction Proof, then useless steps may be */
/* generated. Applying the "Stepprover" command to a non-Virtual Deduction   */
/* proof may generate useful 2-step subproofs without also generating        */
/* useless steps which would have been useful only if the User's Proof was   */
/* written as a Virtual Deduction Proof.                                     */
/*                                                                           */
/* When completeusersproof is run the Proof Worksheet input by the User      */
/* and the text files derived from that Proof Worksheet are edited by        */
/* completeusersproof. Some of these text files are also edited by one or    */
/* two instances of mmj2. One instance of mmj2 is loaded with both the       */
/* set.mm and fd.txt files. The other instance is loaded with only the       */
/* set.mm file. completeusersproof is a procedural program which             */
/* sequentially edits text files. At certain points during the execution of  */
/* completeusersproof the function mmj2Unify() is called. If run through the */
/* GUI, mmj2Unify() "presses" buttons in the GUI of one of the running       */
/* instances of mmj2. These button presses unify the text file passed to     */
/* mmj2Unify(). Which instance of mmj2 is invoked depends on the value of    */
/* the mmj2BatFile argument passed to mmj2Unify(). The button presses are    */
/* executed by AutoHotkey scripts (see: https://autohotkey.com )invoked by   */
/* mmj2Unify(). These scripts were compiled using the AutoHotkey program.    */
/* It is not necessary to install the AutoHotkey program in order to use the */
/* compiled scripts included in the completeusersproof download.             */
/*                                                                           */
/* After downloading all needed files, to run completeusersproof through a   */
/* GUI, double-click on the c:\mmj2\mmj2jar\completeusersproofGUI.exe file   */
/* or run it from the Command Prompt. Three GUIs will open, one for          */
/* completeusersproof and one for each of two instances of mmj2. Open the    */
/* User's Proof to be processed by completeusersproof in the GUI of the      */
/* instance of mmj2 not loaded with fd.txt and press either the              */
/* "Completeusersproof" button, the "Completeusersproof smv" button, the     */
/* "Completeusersproof mv" button, or the "Stepprover" button. As long as    */
/* the three GUI's are open they remain available for processing User's      */
/* Proofs on demand. Close the completeusersproof GUI by pressing the        */
/* "Exit/Quit" button. Close each mmj2 GUI by pressing its "Exit/Quit"       */
/* button.                                                                   */
/*                                                                           */
/* Sometimes a script will press buttons or send keystrokes before a mmj2    */
/* process has completed. This may cause mmj2 to freeze and/or may corrupt a */
/* text file, usually the duplicateUnificationsThm.txt file. Or,             */
/* c:\mmj2\mmj2jar\zz~tools<i>.tmp files may be generated by                 */
/* completeusersproof. When any of these errors occur the input User's       */
/* Proof will not be correctly processed. Corrupted text files should be     */
/* restored by over-writing them with a fresh copy.  completeusersproof      */
/* will not run correctly again unless this is done. If any                  */
/* c:\mmj2\mmj2jar\zz~tools<i>.tmp files are generated by a faulty run of    */
/* completeusersproof, completeusersproof will not run correctly again       */
/* until these files are deleted. If a faulty run of completeusersproof      */
/* occurs, depending on the cause, in addition to the above corrective       */
/* measures, completeusersproof may not run correctly again until the three  */
/* programs are re-booted. Most often, no corrective measures need to be     */
/* taken after a failed attempt to process a User's Proof except to try      */
/* again using a fresh copy of the original User's Proof. If the User uses   */
/* the mouse or keyboard while completeusersproof is processing, these       */
/* actions may sometimes interfere with the processing and cause errors.     */
/*                                                                           */
/* If completeusersproof is run on a computer which runs relatively slowly   */
/* or if it is run on long proofs, then it is possible the AutoHotkey        */
/* scripts included in the download may have Sleep statements of             */
/* insufficient duration to permit some mmj2 processes to complete. The User */
/* may try to correct this by renaming unifyLD.exe, unifylongLD.exe, and     */
/* unifyFFLD.exe, respectively, unify.exe, unifylong.exe, and unifyFF.exe.   */
/* If this is done, it is recommended that copies of the original unify.exe, */
/* unifylong.exe, and unifyFF.exe files are saved before overwriting them.   */
/* Each Sleep statement of the "LD" verison of a script has a duration of    */
/* twice the duration of the non-LD version. "LD" is an abbreviation         */
/* for "Long Duration". If even the LD versions of the scripts               */
/* execute too quickly, the User has the option of increasing the Sleep      */
/* statement durations further. This would require downloading the           */
/* AutoHotkey program from the AutoHotkey website in order to compile the    */
/* revised .ahk files.  Of course, the longer the Sleep durations the longer */
/* the runtime of completeusersproof. The Sleep statement durations of       */
/* unify.exe, unifylong.exe, and unifyFF.exe are usually sufficient for the  */
/* successful application of "Completeusersproof mv" to a9e2ndeqALTUP of     */
/* VirtualDeductionProofs.txt, which is the longest proof contained in that  */
/* file. Of all of completeusersproof's commands, "Completeusersproof mv"    */
/* requires the longest Sleep durations.                                     */
/*                                                                           */
/* The primary advantage of using AutoHotkey scripts for mmj2 unifying       */
/* proofs is that the duration for mmj2 unifying shorter proofs is           */
/* less than using the mmj2 batch test run parm. For proofs of more          */
/* than on the order of 700 steps, mmj2 unification using the mmj2 batch     */
/* test run parm may be preferable because the processing duration is not    */
/* much longer and because pressing buttons in an mmj2 GUI is sometimes      */
/* unreliable. The User has the option of mmj2 unifying longer proofs using  */
/* the mmj2 batch test run parm by assigning the second command line         */
/* argument the value "onlyShortScripts". This is valuable for mmj2          */
/* unification of the duplicateUnificationThms.txt proofs, which are long    */
/* for values of utmax greater than 1. If argv2 is assigned the default      */
/* value, all mmj2 unifications are by the mmj2 batch test run parm. This    */
/* allows completeusersproof to be run on a non-Windows system for which     */
/* AutoHotkey scripts are incompatible.                                      */
/*                                                                           */
/* stepprover() attempts to 2-step prove multiple 0-hypothesis steps in      */
/* parallel, placing them and their numerous duplicates in a single Proof    */
/* Worksheet, duplicateUnificationThms.txt. With the third command line      */
/* argument, the User may specify the maximum number of 0-hypothesis steps   */
/* to attempt to 2-step prove in parallel. A long User's Proof with more     */
/* than eight 0-hypothesis steps to attempt to 2-step prove generates at     */
/* least one duplicateUnificationThms.txt Proof Worksheet having more than   */
/* 3000 steps. If this is too long, it may be avoided by partitioning the    */
/* collection of 0-hypothesis steps to 2-step prove into cells of less than  */
/* 8.                                                                        */
/*                                                                           */
/* The first command line argument default value specifies that the attempt  */
/* to complete the User's Proof is by the unification theorem means. The     */
/* value "runCompleteusersproofsmv" specifies processing by the single       */
/* metavariable deduction unification means. The value                       */
/* "runcompleteusersproofmv" specifies processing by the metavariable        */
/* deduction unification means. The value "runStepprover" specifies only     */
/* running the stepprover() function.                                        */
/*                                                                           */
/* If completeusersproof is not run through a GUI, if the User's Proof is    */
/* copied onto the file c:\mmj2\mmj2jar\proofWorksheet.txt, the User's Proof */
/* will be processed by double-clicking on the completeusersproof.exe file.  */
/* The default values of the command line arguments apply: the unification   */
/* theorem means with mmj2 unificiations by the batch test run parm with a   */
/* maximum of 8 0-hypothesis steps in-parallel 2-step proof attempts. If a   */
/* .bat file is used or completeusersproof.exe is run through the command    */
/* prompt, the User may specify the value of each of the three command line  */
/* arguments. The three command line arguments afford the User some choices  */
/* as to how completeusersproof processes the input User's Proof.            */
/*                                                                           */
/* Files of the download applicable to completeusersproof:                   */
/*                                                                           */
/*  completeusersproofp.c        The source code. Unless otherwise           */
/*                               noted, this and all other downloaded        */
/*                               files should be placed in the               */
/*                               c:\mmj2\mmj2jar folder. The mmj2 program    */
/*                               files must be downloaded into their         */
/*                               default folders for completeusersproof to   */
/*                               run.                                        */
/*                                                                           */
/*  completeusersproofp.exe      completeusersproofp.c compiled.             */
/*                                                                           */
/*  <User's Proof file>          <User's Proof file> is the full path and    */
/*                               filename of the mmj2 Proof Worksheet opened */
/*                               by the User in the mmj2 GUI of the mmj2     */
/*                               instance which loads set.mm and not fd.txt. */
/*                               Any path may be used. Of course, this file  */
/*                               is provided by the User and is not included */
/*                               in the completeusersproof download          */
/*                               package.                                    */
/*                                                                           */
/*  labels.txt                   This text file is a list of 1-hypothesis    */
/*                               set.mm labels, each with the potential to   */
/*                               to deduce some step in the Proof Worksheet  */
/*                               from some theorem in set.mm.                */
/*                                                                           */
/*  fd.txt                       An mmj2 input file of "false deductions".   */
/*                               This file should be placed in the           */
/*                               c:\metamath folder. A false eel* deduction  */
/*                               is a true deduction for which one           */
/*                               hypothesis, a "unification theorem", is     */
/*                               implicit. completeusersproof explicates the */
/*                               implicit hypothesis and 2-step proves it if */
/*                               it is not in set.mm and a semantic          */
/*                               variation of it is. For example, syl is the */
/*                               true deduction corresponding to             */
/*                               the false deduction ff1,                    */
/*                               |- (. ph ->. ps ). infers |- (. ph ->. ch ).*/
/*                               The implicit hypothesis is ( ps -> ch ) .   */
/*                               A deduction with an implicit unification    */
/*                               theorem as a hypothesis which is true from  */
/*                               the User's perspective is false from        */
/*                               Metamath's perspective because Metamath     */
/*                               is implicit-hypothesis-blind.               */
/*                                                                           */
/*                               A false ffsmv* deduction is used to         */
/*                               construct a subproof which may unify with a */
/*                               single metavariable deduction in set.mm. A  */
/*                               false ffTmv* deduction is used for a        */
/*                               subproof for which each original step has   */
/*                               no virtual hypothesis collection. The       */
/*                               ffTmv* deduction puts each of these steps   */
/*                               into standard T form. It is used to         */
/*                               construct a subproof which may unify with a */
/*                               single metavariable deduction in set.mm.    */
/*                               Each ffTmv* deduction is, strictly          */
/*                               speaking, a true deduction because its      */
/*                               assertion is deducible from its last        */
/*                               hypothesis.                                 */
/*                                                                           */
/* mapFfToEelUunNumPermFfsmv.txt This file specifies the one-one             */
/*                               correspondence between each false deduction */
/*                               in fd.txt and its true counterpart in       */
/*                               set.mm. Column one contains the false       */
/*                               deduction labels and column two contains    */
/*                               the true deduction labels.                  */
/*                                                                           */
/*                               This file also specifies the mapping of     */
/*                               each ff* false deduction label into its     */
/*                               corresponding 0th premutation uun* label.   */
/*                               That label is in the 3rd column of the same */
/*                               row. In the 4th column of the same row is   */
/*                               the number of permutations of uun* labels   */
/*                               corresponding to the ff* label. The uun*    */
/*                               deductions are described at the beginning   */
/*                               of the completeusersproof() function        */
/*                               definition.                                 */
/*                                                                           */
/*                               This file also specifies the mapping of     */
/*                               each ff* false deduction label into it      */
/*                               corresponding ffsmv* or ffTmv* label, which */
/*                               is in the 5th column of the same row.       */
/*                                                                           */
/*  RunParmsFalseDeductionsG.txt File of mmj2 run parameters referenced by   */
/*                               mmj2FalseDeductionsG.bat. This includes     */
/*                               two Loadfile run parms, one for set.mm and  */
/*                               one for fd.txt. The "G" indicates that the  */
/*                               RunProofAsstGUI is an included run parm.    */
/*                                                                           */
/*  mmj2FalseDeductionsG.bat     Executes mmj2 using the run parameters      */
/*                               specified by RunParmsFalseDeductionsG.txt.  */
/*                               Subproofs having at least 1 hypothesis      */
/*                               step not unifying with a deduction in       */
/*                               set.mm unify with some ff* false deduction  */
/*                               in fd.txt. Runs with a GUI.                 */
/*                                                                           */
/*  RunParmsStepProverG.txt      File of mmj2 run parameters referenced by   */
/*                               mmj2StepProverG.bat. This includes a set.mm */
/*                               Loadfile run parm. It does not include an   */
/*                               fd.txt Loadfile run parm. RunProofAsstGUI   */
/*                               run parm included.                          */
/*                                                                           */
/*  mmj2StepProverG.bat          Executes mmj2 using the run parameters      */
/*                               specified by RunParmsStepProverG.txt. Runs  */
/*                               with a GUI.                                 */
/*                                                                           */
/*  RunParmsFalseDeductions.txt  Similar to "G" counterpart (above), except  */
/*                               without RunProofAsstGUI run parm and with   */
/*                               ProofAsstBatchTest run parm.                */
/*                                                                           */
/*  mmj2FalseDeductions.bat      Executes mmj2 using the run parameters      */
/*                               specified by RunParmsFalseDeductions.txt.   */
/*                                                                           */
/*  RunParmsStepProver.txt       Similar to "G" counterpart (above), except  */
/*                               without RunProofAsstGUI run parm and with   */
/*                               ProofAsstBatchTest run parm.                */
/*                                                                           */
/*  mmj2StepProver.bat           Executes mmj2 using the run parameters      */
/*                               specified by RunParmsStepProver.txt.        */
/*                                                                           */
/*  completeusersproofGUI.exe    This file runs an AutoHotkey script. The    */
/*                               script first loads two instances of the     */
/*                               mmj2 program (with their GUIs) by executing */
/*                               mmj2FalseDeductionsG.bat and                */
/*                               mmj2StepProverG.bat. It then opens a third  */
/*                               GUI window which contains the buttons       */
/*                               "Completeusersproof",                       */
/*                               "Completeusersproof smv",                   */
/*                               "Completeusersproof mv", "Stepprover", and  */
/*                               "Exit/Quit". Pressing one of the first      */
/*                               four buttons runs completeusersproof.       */
/*                                                                           */
/*  completeusersproofGUI.ahk    AutoHotkey source code of                   */
/*                               completeusersproofGUI.exe.                  */
/*                                                                           */
/*  Document-margins.ico         Icon in the Titlebar of the                 */
/*                               completeusersproof GUI.                     */
/*                                                                           */
/*  completeusersproof.bat       Executes completeusersproof.exe             */
/*                               specifying the value of command line        */
/*                               argument argv1 to be the default value      */
/*                               """".                                       */
/*                                                                           */
/*  completeusersproofSmv.bat    Executes completeusersproof.exe             */
/*                               specifying the value of command line        */
/*                               argument argv1                              */
/*                               "runCompleteusersproofsmv".                 */
/*                                                                           */
/*  completeusersproofMv.bat     Executes completeusersproof.exe             */
/*                               specifying the value of command line        */
/*                               argument argv1                              */
/*                               "runCompleteusersproofmv".                  */
/*                                                                           */
/*  completeusersproofSp.bat     Executes completeusersproof.exe             */
/*                               specifying the value of command line        */
/*                               argument argv1 "runStepprover".             */
/*                                                                           */
/*  unify.exe                    Executable AutoHotkey script file invoked   */
/*                               by a call of mmj2Unify() with the value     */
/*                               "unify.exe" for the mmj2BatFile argument.   */
/*                               It "presses" the buttons of the mmj2        */
/*                               instance without fd.txt loaded to open,     */
/*                               unify, and save the file which is the       */
/*                               value of the proofWorksheet argument of     */
/*                               the mmj2Unify() call.                       */
/*                                                                           */
/*  unify.ahk                    Source code for unify.exe.                  */
/*                                                                           */
/*  unifylong.exe                Similar to unify.exe. "unifylong.exe" is    */
/*                               passed by a mmj2Unify() call when           */
/*                               proofWorksheet has the value                */
/*                               "duplicateUnificationThms.txt", which may   */
/*                               be a very long Proof Worksheet requiring    */
/*                               longer-than-normal pauses between button    */
/*                               presses to allow mmj2 processes to          */
/*                               complete.                                   */
/*                                                                           */
/*  unifylong.ahk                Source code for unifylong.exe.              */
/*                                                                           */
/*  unifyFF.exe                  Similar to unify.exe except it presses the  */
/*                               buttons of the mmj2 instance with fd.txt    */
/*                               loaded.                                     */
/*                                                                           */
/*  unifyFF.ahk                  Source code for unifyFF.exe.                */
/*                                                                           */
/*  unifyLD.exe                  Long Duration version of unify.exe. Each    */
/*                               Sleep statement has a duration of twice the */
/*                               duration of the non-LD version. The User    */
/*                               has the option to rename this file          */
/*                               unify.exe if the Sleep statement durations  */
/*                               of unify.exe are too short to process a     */
/*                               long proof or to process proofs on a slow   */
/*                               computer.                                   */
/*                                                                           */
/*  unifyLD.ahk                  Source code for unifyLD.exe.                */
/*                                                                           */
/*  unifylongLD.exe              Long Duration version of unifylong.exe.     */
/*                                                                           */
/*  unifylongLD.ahk              Source code for unifylongLD.exe.            */
/*                                                                           */
/*  unifyFFLD.exe                Long Duration version of unifyFF.exe.       */
/*                                                                           */
/*  unifyFFLD.ahk                Source code for unifyFFLD.exe.              */
/*                                                                           */
/*  proofWorksheet.mmp           This file is to be placed in the            */
/*                               c:\mmj2\mmj2jar\myproofs folder. It is the  */
/*                               placeholder file used for all mmj2          */
/*                               unifications of the instance of mmj2 for    */
/*                               which fd.txt is not loaded.                 */
/*                                                                           */
/*  proofWorksheetFF.mmp         This file is to be placed in the            */
/*                               c:\mmj2\mmj2jar\myproofs folder. It is the  */
/*                               placeholder file used for all mmj2          */
/*                               unifications of the instance of mmj2 for    */
/*                               which fd.txt is loaded.                     */
/*                                                                           */
/*  proofWorksheet0.mmp          This file is to be placed in the            */
/*                               c:\mmj2\mmj2jar\myproofs folder. It is a    */
/*                               copy of proofWorksheet.mmp. Over-write      */
/*                               proofWorksheet.mmp or                       */
/*                               duplicateUnificationThms.txt with this      */
/*                               file if either becomes corrupted.           */
/*                                                                           */
/*  proofWorksheetFF0.mmp        This file is to be placed in the            */
/*                               c:\mmj2\mmj2jar\myproofs folder. It is a    */
/*                               copy of proofWorksheetFF.mmp. Over-write    */
/*                               proofWorksheetFF.mmp with this file if it   */
/*                               becomes corrupted.                          */
/*                                                                           */
/*                                                                           */
/* The completeusersproof output file:                                       */
/*                                                                           */
/*  c:\mmj2\mmj2jar\myproofs\<t> The User's Proof is edited by               */
/*                               completeusersproof. If the User's Proof     */
/*                               is correct and sufficiently detailed, the   */
/*                               output file may contain a                   */
/*                               Metamath-generated RPN proof. Otherwise,    */
/*                               as many subproofs of the User's Proof       */
/*                               will be completed as possible. The output   */
/*                               filename is <t>, where <t> is the theorem   */
/*                               label specified in the header of the Proof  */
/*                               Worksheet input by the User. Another file   */
/*                               which holds the same output proof           */
/*                               temporarily is                              */
/*                               c:\mmj2\mmj2jar\proofWorksheet.txt. This    */
/*                               file is overwritten with each sucessive     */
/*                               application of completeusersproof.          */
/*                                                                           */
/*                                                                           */
/* Primary Functions:                                                        */
/*                                                                           */
/*                           completeusersproof()                            */
/*                           addMvSubtheoremTs()                             */
/*                           addMvAssertionSteps()                           */
/*                           completeusersproofmv()                          */
/*                           addUnionizedAssertionPermutations()             */
/*                           pickAPermutationOfEachUnionizedAssertion()      */
/*                           pickALabelPermutation()                         */
/*                           completeTheoremSteps()                          */
/*                           pickUnifThmPermutations()                       */
/*                           removeDecoupledSteps()                          */
/*                           substituteLabels()                              */
/*                           stepprover()                                    */
/*                           removeUnneededFfLabels()                        */
/*                           nameUsersProofFile()                            */
/*                           editHypFieldOfHypSteps()                        */
/*                           editHypFieldOfTAssertions()                     */
/*                           editHypFieldOfAssertions()                      */
/*                                                                           */
/* Some Utility Functions:                                                   */
/*                                                                           */
/*                           deleteStepsWithWorkVariables()                  */
/*                           mmj2Unify()                                     */
/*                           translate()                                     */
/*                           tagLabeledSteps()                               */
/*                           endTag()                                        */
/*                           addMTLast()                                     */
/*                           addMTMT()                                       */
/*                           addMTMF()                                       */
/*                           addMT()                                         */
/*                           substituteMTMT()                                */
/*                           deleteLabels()                                  */
/*                           removeATypeOfLabel()                            */
/*                                                                           */
/* note: To find a function definition, e.g., search for "df-stepprover()"   */
/*                                                                           */
/*                                                                           */
/*****************************************************************************/







/*****************************************************************************/
/* Program name:  metamath                                                   */
/* Copyright (C) 2015 NORMAN MEGILL  nm at alum.mit.edu  http://metamath.org */
/* License terms:  GNU General Public License Version 2 or any later version */
/*****************************************************************************/
/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/

#define MVERSION "0.118 18-Jul-2015"
/* 0.118 18-Jul-2015 nm metamath.c, mmcmds.h, mmcmds.c, mmcmdl.c, mmhlpb.h,
   mmhlpb.c - added /TO qualifier to SHOW TRACE_BACK.  See
   HELP SHOW TRACE_BACK. */
/* 0.117 30-May-2015
   1. nm mmwtex.c - move <A NAME... tag to math symbol cell in proof pages so
       hyperlink will jump to top of cell (reported by Alan Sare)
   2. daw mmpfas.c - add INLINE speedup if compiler permits
   3. nm metamath.c, mminou.c, mmwtex.c, mmpfas.c - fix clang -Wall warnings
       (reported by David A. Wheeler) */
/* 0.116 9-May-2015 nm mmwtex.c - adjust paragraph break in 'write th';
   Statement List renamed Theorem List;  prevent space in after paragraph
   in Theorem List; remove stray "(";  put header and header comment
   in same table cell; fix <TITLE> of Theorem List pages */
/* 0.115 8-May-2015 nm mmwtex.c - added section header comments to
       WRITE THEOREM_LIST and broke out Table of Contents page
   24-Apr-2015 nm metamath.c - add # bytes to end of "---Clip out the proof";
       reverted to no blank lines there (see 0.113 item 3) */
/* 0.114 22-Apr-2015 nm mmcmds.c - put [-1], [-2],... offsets on 'show
   new_proof/unknown' */
/* 0.113 19-Apr-2015 so, nm metamath.c, mmdata.c
   1. SHOW LABEL % will show statements with changed proofs
   2. SHOW LABEL = will show the statement being proved in MM-PA
   3. Added blank lines before, after "---------Clip out the proof" proof
   4. Generate date only if the proof is complete */
/* 0.112 15-Apr-2015 nm metamath.c - fix bug 1121 (reported by S. O'Rear);
   mwtex.c - add "img { margin-bottom: -4px }" to CSS to align symbol GIFs;
   mwtex.c - remove some hard coding for set.mm, for use with new nf.mm;
   metamath.c - fix comment parsing in WRITE BIBLIOGRAPHY to ignore
   math symbols  */
/* 0.111 22-Nov-2014 nm metamath.c, mmcmds.c, mmcmdl.c, mmhlpb.c - added
   /NO_NEW_AXIOMS_FROM qualifier to MINIMIZE_WITH; see HELP MINIMIZE_WITH.
   21-Nov-2014 Stepan O'Rear mmdata.c, mmhlpb.c - added ~ label range specifier
   to wildcards; see HELP SEARCH */
/* 0.110 2-Nov-2014 nm mmcmds.c - fixed bug 1114 (reported by Stefan O'Rear);
   metamath.c, mmhlpb.c - added "SHOW STATEMENT =" to show the statement
   being proved in MM-PA */
/* 0.109 20-Aug-2014 nm mmwtex.c - fix corrupted HTML caused by misinterpreting
   math symbols as comment markup (math symbols with _ [ ] or ~).  Also,
   allow https:// as well as http:// in ~ label markup.
   11-Jul-2014 wl mmdata.c - fix obscure crash in debugging mode db9 */
/* 0.108 25-Jun-2014 nm
   (1) metamath.c, mmcmdl.c, mmhlpb.c - MINIMIZE_WITH now checks the size
   of the compressed proof, prevents $d violations, and tries forward and
   reverse statment scanning order; /NO_DISTINCT, /BRIEF, /REVERSE
   qualifiers were removed.
   (2) mminou.c - prevent hard breaks (in the middle of a word) in too-long
   lines (e.g. long URLs) in WRITE SOURCE .../REWRAP; just overflow the
   screen width instead.
   (3) mmpfas.c - fixed memory leak in replaceStatement()
   (4) mmpfas.c - suppress inf. growth with MINIMIZE_WITH idi/ALLOW_GROWTH */
/* 0.107 21-Jun-2014 nm metamath.c, mmcmdl.c, mmhlpb.c - added /SIZE qualifier
   to SHOW PROOF; added SHOW ELAPSED_TIME; mmwtex.c - reformatted WRITE
   THEOREM_LIST output; now "$(", newline, "######" starts a "part" */
/* 0.106 30-Mar-2014 nm mmwtex.c - fix bug introduced by 0.105 that disabled
   hyperlinks on literature refs in HTML comment.  metamath.c - improve
   messages */
/* 0.105 15-Feb-2014 nm mmwtex.c - prevented illegal LaTeX output for certain
   special characters in comments. */
/* 0.104 14-Feb-2014 nm mmwtex.c - fixed bug 2312, mmcmds.c - enhanced ASSIGN
   error message. */
/* 0.103 4-Jan-2014 nm mmcmds.c,h - added "Allowed substitution hints" below
   the "Distinct variable groups" list on generated web pages
   mmwtex.c - added "*" to indicate DV's occur in Statement List entries. */
/* 0.102 2-Jan-2014 nm mminou.c - made compressed proof line wrapping more
   uniform at start of compressed part of proof */
/* 0.101 27-Dec-2013 nm mmdata.h,c, mminou.c, mmcmdl.c, mmhlpb.c, mmvstr.c -
   Improved storage efficiency of /COMPRESSED proofs (but with 20% slower run
   time); added /FAST_COMPRESSION to specify old algorithm; removed end-of-line
   space after label list in old algorithm; fixed linput() bug */
/* 0.100 30-Nov-2013 nm mmpfas.c - reversed statement scan order in
   proveFloating(), to speed up SHOW STATEMENT df-* /HTML; metamath.c - remove
   the unknown date place holder in SAVE NEW_PROOF; Wolf Lammen mmvstr.c -
   some cleanup */
/* 0.07.99 1-Nov-2013 nm metamath.c, mmpfas.h,c, mmcmdl.h,c, mmhlpa.c,
   mmhlpb.c - added UNDO, REDO, SET UNDO commands (see HELP UNDO) */
/* 0.07.98 30-Oct-2013 Wolf Lammen mmvstr.c,h, mmiou.c, mmpars.c,
   mmdata.c  - improve code style and program structure */
/* 0.07.97 20-Oct-2013 Wolf Lammen mmvstr.c,h, metamath.c - improved linput();
   nm mmcmds.c, mmdata.c - tolerate bad proofs in SHOW TRACE_BACK etc. */
/* 0.07.96 20-Sep-2013 Wolf Lammen mmvstr.c - revised cat();
   nm mmwtex.c, mminou.c - change a print2 to printLongLine to fix bug 1150 */
/* 0.07.95 18-Sep-2013 Wolf Lammen mmvstr.c - optimized cat();
   nm metamath.c, mmcmds.c, mmdata.c, mmpars.c, mmpfas.c, mmvstr.c,
   mmwtex.c - suppress some clang warnings */
/* 0.07.94 28-Aug-2013 Alexey Merkulov mmcmds.c, mmpars.c - fixed several
   memory leaks found by valgrind --leak-check=full --show-possibly-lost=no */
/* 0.07.93 8-Jul-2013 Wolf Lammen mmvstr.c - simplified let() function;
   also many minor changes in m*.c and m*.h to assist future refactoring */
/* 0.07.92 28-Jun-2013 nm metamath.c mmcmds.c,h mmcmdl.c mmhlpb.c- added
   /NO_REPEATED_STEPS for /LEMMON mode of SHOW PROOF, SHOW NEW_PROOF.
   This reverts the /LEMMON mode default display change of 31-Jan-2010
   and invokes it when desired via /NO_REPEATED_STEPS. */
/* 0.07.91 20-May-2013 nm metamath.c mmpfas.c,h mmcmds.c,h mmcmdl.c
   mmhlpb.c- added /FORBID qualifier to MINIMIZE_WITH */
/* 0.07.90 19-May-2013 nm metamath.c mmcmds.c mmcmdl.c mmhlpb.c - added /MATCH
   qualifier to SHOW TRACE_BACK */
/* 0.07.88 18-Nov-2012 nm mmcmds.c - fixed bug 243 */
/* 0.07.87 17-Nov-2012 nm mmwtex.c - fixed formatting problem when label
   markup ends a comment in SHOW PROOF ... /HTML */
/* 0.07.86 27-Oct-2012 nm mmcmds.c, mmwtex.c, mmwtex.h - fixed ERASE bug
   caused by imperfect re-initialization; reported by Wolf Lammen */
/* 0.07.85 10-Oct-2012 nm metamath.c, mmcmdl.c, mmwtex.c, mmwtex.h, mmhlpb.c -
   added /SHOW_LEMMAS to WRITE THEOREM_LIST to bypass lemma math suppression */
/* 0.07.84 9-Oct-2012 nm mmcmds.c - fixed bug in getStatementNum() */
/* 0.07.83 19-Sep-2012 nm mmwtex.c - fixed bug reported by Wolf Lammen */
/* 0.07.82 16-Sep-2012 nm metamath.c, mmpfas.c - fixed REPLACE infinite loop;
   improved REPLACE message for shared dummy variables */
/* 0.07.81 14-Sep-2012 nm metamath.c, mmcmds.c, mmcmds.h, mmcmdl.c, mmhlpb.c
   - added FIRST, LAST, +nn, -nn where missing from ASSIGN, REPLACE,
   IMPROVE, LET STEP.  Wildcards are allowed for PROVE, ASSIGN, REPLACE
   labels provided there is a unique match. */
/* 0.07.80 4-Sep-2012 nm metamath.c, mmpfas.c, mmpfas.h, mmcmdl.c, mmhlpb.c
   - added / 1, / 2, / 3, / SUBPROOFS to IMPROVE to specify search level */
/* 0.07.79 31-Aug-2012 nm m*.c - clean up some gcc warnings */
/* 0.07.78 28-Aug-2012 nm mmpfas.c - fix bug in 0.07.77. */
/* 0.07.77 25-Aug-2012 nm metamath.c, mmpfas.c - Enhanced IMPROVE algorithm to
   allow non-shared dummy variables in unknown steps */
/* 0.07.76 22-Aug-2012 nm metamath.c, mmpfas.c, mmcmdl.c, mmhlpb.c -
   Enhanced IMPROVE algorithm to also try REPLACE algorithm */
/* 0.07.75 14-Aug-2012 nm metamath.c - MINIMIZE_WITH now checks current
   mathbox (but not other mathboxes) even if /INCLUDE_MATHBOXES is omitted */
/* 0.07.74 18-Mar-2012 nm mmwtex.c, mmcmds.c, metamath.c - improved texToken()
   error message */
/* 0.07.73 26-Dec-2011 nm mmwtex.c, mmpars.c - added <HTML>...</HTML> in
   comments for passing through raw HTML code into HTML files generated with
   SHOw STATEMENT xxx / HTML */
/* 0.07.72 25-Dec-2011 nm (obsolete) */
/* 0.07.71 10-Nov-2011 nm metamath.c, mmcmdl.c - added /REV to MINIMIZE_WITH */
/* 0.07.70 6-Aug-2011 nm mmwtex.c - fix handling of double quotes inside
   of htmldef strings to match spec in Metamath book Appendix A p. 156 */
/* 0.07.69 9-Jul-2011 nm mmpars.c, mmvstr.c - Untab file in WRITE SOURCE
   ... /REWRAP */
/* 0.07.68 3-Jul-2011 nm metamath.c, mminou.h, mminou.c - Nested SUBMIT calls
   (SUBMIT calls inside of a SUBMIT command file) are now allowed.
   Also, mmdata.c - fix memory leak. */
/* 0.07.67 2-Jul-2011 nm metamath.c, mmcmdl.c, mmhlpa.c - Added TAG command
   to TOOLS.  See HELP TAG under TOOLS.  (The old special-purpose TAG command
   was renamed to UPDATE.) */
/* 0.07.66 1-Jul-2011 nm metamath.c, mmcmds.c, mmpars.c, mmpars.h - Added code
   to strip spurious "$( [?] $)" in WRITE SOURCE ... /CLEAN output */
/* 0.07.65 30-Jun-2011 nm mmwtex.c - Prevent processing [...] bibliography
   brackets inside of `...` math strings in comments. */
/* 0.07.64 28-Jun-2011 nm metamath.c, mmcmdl.c - Added /INCLUDE_MATHBOXES
   qualifier to MINIMIZE_WITH; without it, MINIMIZE_WITH * will skip
   checking user mathboxes. */
/* 0.07.63 26-Jun-2011 nm mmwtex.c - check that .gifs exist for htmldefs */
/* 0.07.62 18-Jun-2011 nm mmpars.c - fixed bug where redeclaration of active
   $v was not detected */
/* 0.07.61 12-Jun-2011 nm mmpfas.c, mmcmds.c, metamath.c, mmhlpb.c - added
   /FORMAT and /REWRAP qualifiers to WRITE SOURCE to format according to set.mm
   conventions - set HELP WRITE SOURCE */
/* 0.07.60 7-Jun-2011 nm mmpfas.c - fixed bug 1805 which occurred when doing
   MINIMIZE_WITH weq/ALLOW_GROWTH after DELETE DELETE FLOATING_HYPOTHESES */
/* 0.07.59 11-Dec-2010 nm mmpfas.c - increased default SET SEARCH_LIMIT from
   10000 to 25000 to accomodate df-plig web page in set.mm */
/* 0.07.58 9-Dec-2010 nm mmpars.c - detect if same symbol is used with both
   $c and $v, in order to conform with Metamath spec */
/* 0.07.57 19-Oct-2010 nm mmpars.c - fix bug causing incorrect line count
   for error messages when non-ASCII character was found; mminou.h -
   increase SET WIDTH maximum from 999 to 9999 */
/* 0.07.56 27-Sep-2010 nm mmpars.c, mmpfas.c - check for $a's with
   one token e.g. "$a wff $."; if found, turn SET EMPTY_SUBSTITUTION ON
   automatically.  (Suggested by Mel O'Cat; patent pending.) */
/* 0.07.55 26-Sep-2010 nm mmunif.c, mmcmds.c, mmunif.h - check for mismatched
   brackets in all $a's, so that if there are any, the bracket matching
   algorithm (for fewer ambiguous unifications) in mmunif.c will be turned
   off. */
/* 0.07.54 25-Sep-2010 nm mmpars.c - added $f checking to conform to the
   current Metamath spec, so footnote 2 on p. 92 of Metamath book is
   no longer applicable. */
/* 0.07.53 24-Sep-2010 nm mmveri.c - fixed bug(2106), reported by Michal
   Burger */
/* 0.07.52 14-Sep-2010 nm metamath.c, mmwtex.h, mmwtex.c, mmcmds.c,
   mmcmdl.c, mmhlpb.c - rewrote the LaTeX output for easier hand-editing
   and embedding in LaTeX documents.  The old LaTeX output is still
   available with /OLD_TEX on OPEN TEX, SHOW STATEMENT, and SHOW PROOF,
   but it is obsolete and will be deleted eventually if no one objects.  The
   new /TEX output also replaces the old /SIMPLE_TEX, which was removed. */
/* 0.07.51 9-Sep-2010 Stefan Allen mmwtex.c - put hyperlinks on hypothesis
   label references in SHOW STATEMENT * /HTML, ALT_HTML output */
/* 0.07.50 21-Feb-2010 nm mminou.c - "./metamath < empty", where "empty" is a
   0-byte file, now exits metamath instead of producing an infinite loop.
   Also, ^D now exits metamath.  Reported by Cai Yufei */
/* 0.07.49 31-Jan-2010 nm mmcmds.c - Lemmon-style proofs (SHOW PROOF xxx
   /LEMON/RENUMBER) no longer have steps with dummy labels; instead, steps
   are now the same as in HTML page proofs.  (There is a line to comment
   out if you want to revert to old behavior.) */
/* 0.07.48 11-Sep-2009 nm mmpars.c, mm, mmvstr.c, mmdata.c - Added detection of
   non-whitespace around keywords (mmpars.c); small changes to eliminate
   warnings in gcc 3.4.4 (mmvstr.c, mmdata.c) */
/* 0.07.47 2-Aug-2009 nm mmwtex.c, mmwtex.h - added user name to mathbox
   pages */
/* 0.07.46 24-Jul-2009 nm metamath.c, mmwtex.c - changed name of sandbox
   to "mathbox" */
/* 0.07.45 15-Jun-2009 nm metamath.c, mmhlpb.c - put "!" before each line of
   SET ECHO ON output to make them easy to identity for creating scripts */
/* 0.07.44 12-May-2009 Stefan Allan, nm metamath.c, mmcmdl.c, mmmmwtex.c -
   added SHOW STATEMENT / MNEMONICS - see HELP SHOW STATEMENT */
/* 0.07.43 29-Aug-2008 nm mmwtex.c - workaround for Unicode huge font bug in
   FireFox 3 */
/* 0.07.42 8-Aug-2008 nm metamath.c - added sandbox, Hilbert Space colors to
   Definition List */
/* 0.07.41 29-Jul-2008 nm metamath.c, mmwtex.h, mmwtex.c - Added handling of
   sandbox section of Metamath Proof Explorer web pages */
/* 0.07.40 6-Jul-2008 nm metamath.c, mmcmdl.c, mmhlpa.c, mmhlpb.c - Added
   / NO_VERSIONING qualifier for SHOW STATEMENT, so website can be regenerated
   in place with less temporary space required.  Also, the wildcard trigger
   for mmdefinitions.html, etc. is more flexible (see HELP HTML). */
/* 0.07.39 21-May-2008 nm metamath.c, mmhlpb.c - Added wildcard handling to
   statement label in SHOW TRACE_BACK.  All wildcards now allow
   comma-separated lists [i.e. matchesList() instead of matches()] */
/* 0.07.38 26-Apr-2008 nm metamath.c, mmdata.h, mmdata.c, mmvstr.c, mmhlpb.c -
   Enhanced / EXCEPT qualifier for MINIMIZE_WITH to handle comma-separated
   wildcard list. */
/* 0.07.37 14-Apr-2008 nm metamath.c, mmcmdl.c, mmhlpb.c - Added / JOIN
   qualifier to SEARCH. */
/* 0.07.36 7-Jan-2008 nm metamath.c, mmcmdl.c, mmhlpb.c - Added wildcard
   handling for labels in SHOW USAGE. */
/* 0.07.35 2-Jan-2008 nm mmcmdl.c, metamath.c, mmhlpb.c - Changed keywords
   COMPACT to PACKED and COLUMN to START_COLUMN so that SAVE/SHOW proof can use
   C to abbreviate COMPRESSED.  (PACKED format is supported but "unofficial,"
   used mainly for debugging purposes, and is not listed in HELP SAVE
   PROOF.) */
/* 0.07.34 19-Nov-2007 nm mmwtex.c, mminou.c - Added tooltips to proof step
   hyperlinks in SHOW STATEMENT.../HTML,ALT_HTML output (suggested by Reinder
   Verlinde) */
/* 0.07.33 19-Jul-2007 nm mminou.c, mmvstr.c, mmdata.c, mmword.c - added fflush
   after each printf() call for proper behavior inside emacs (suggested by
   Frederic Line) */
/* 0.07.32 29-Apr-2007 nm mminou.c - fSafeOpen now stops at gap; e.g. if ~2
   doesn't exist, ~1 will be renamed to ~2, but any ~3, etc. are not touched */
/* 0.07.31 5-Apr-2007 nm mmwtex.c - Don't make "_" in hyperlink a subscript */
/* 0.07.30 8-Feb-2007 nm mmcmds.c, mmwtex.c Added HTML statement number info to
   SHOW STATEMENT.../FULL; friendlier "Contents+1" link in mmtheorems*.html */
/* 0.07.29 6-Feb-2007 Jason Orendorff mmpfas.c - Patch to eliminate the
   duplicate "Exceeded trial limit at step n" messages */
/* 0.07.28 22-Dec-2006 nm mmhlpb.c - Added info on quotes to HELP LET */
/* 0.07.27 23-Oct-2006 nm metamath.c, mminou.c, mmhlpa.c, mmhlpb.c - Added
   / SILENT qualifier to SUBMIT command */
/* 0.07.26 12-Oct-2006 nm mminou.c - Fixed bug when SUBMIT file was missing
   a new-line at end of file (reported by Marnix Klooster) */
/* 0.07.25 10-Oct-2006 nm metamath.c - Fixed bug invoking TOOLS as a ./metamath
   command-line argument */
/* 0.07.24 25-Sep-2006 nm mmcmdl.c Fixed bug in
   SHOW NEW_PROOF/START_COLUMN nn/LEM */
/* 0.07.23 31-Aug-2006 nm mmwtex.c - Added Home and Contents links to bottom of
   WRITE THEOREM_LIST pages */
/* 0.07.22 26-Aug-2006 nm metamath.c, mmcmdl.c, mmhlpb.c - Changed "IMPROVE
   STEP <step>" to "IMPROVE <step>" for user convenience and to be consistent
   with ASSIGN <step> */
/* 0.07.21 20-Aug-2006 nm mmwtex.c - Revised small colored numbers so that all
   colors have the same grayscale brightness.. */
/* 0.07.20 19-Aug-2006 nm mmpars.c - Made the error "Required hypotheses may
   not be explicitly declared" in a compressed proof non-severe, so that we
   can still SAVE the proof to reformat and recover it. */
/* 0.07.19 11-Aug-06 nm mmcmds.c - "Distinct variable group(s)" is now
   "group" or "groups" as appropriate. */
/* 0.07.18 31-Jul-06 nm mmwtex.c - added table to contents to p.1 of output of
   WRITE THEOREM_LIST command. */
/* 0.07.17 4-Jun-06 nm mmpars.c - do not allow labels to match math symbols
   (new spec proposed by O'Cat).   mmwtex.c - made theorem name 1st in title,
   for readability in Firefox tabs. */
/* 0.07.16 16-Apr-06 nm metamath.c, mmcmdl.c, mmpfas.c, mmhlpb.c - allow step
   to be negative (relative to end of proof) for ASSIGN, UNIFY, and LET STEP
   (see their HELPs).  Added INITIALIZE USER to delete LET STEP assignments
   (see HELP INITIALIZE).  Fixed bug in LET STEP (mmpfas.c). */
/* 0.07.15 10-Apr-06 nm metamath.c, mmvstr.c - change dates from 2-digit to
   4-digit year; make compatible with older 2-digit year. */
/* 0.07.14 21-Mar-06 nm mmpars.c - fix bug 1722 when compressed proof has
   "Z" at beginning of proof instead of after a proof step. */
/* 0.07.13 3-Feb-06 nm mmpfas.c - minor improvement to MINIMIZE_WITH */
/* 0.07.12 30-Jan-06 nm metamath.c, mmcmds.c, mmdata.c, mmdata.h, mmhlpa.c,
   mmhlpb.c - added "?" wildcard to match single character. See HELP SEARCH. */
/* 0.07.11 7-Jan-06 nm metamath.c, mmcmdl.c, mmhlpb.c - added EXCEPT qualifier
   to MINIMIZE_WITH */
/* 0.07.10 28-Dec-05 nm metamath.c, mmcmds.c - cosmetic tweaks */
/* 0.07.10 11-Dec-05 nm metamath.c, mmcmdl.c, mmhlpb.c - added ASSIGN FIRST
   and IMPROVE FIRST commands.  Also enhanced READ error message */
/* 0.07.9 1-Dec-05 nm mmvstr.c - added comment on how to make portable */
/* 0.07.9 18-Nov-05 nm metamath.c, mminou.c, mminou.h, mmcmdl.c, mmhlpb.c -
   added SET HEIGHT command; changed SET SCREEN_WIDTH to SET WIDTH; changed
   SET HENTY_FILTER to SET JEREMY_HENTY_FILTER (to make H for HEIGHT
   unambiguous); added HELP for SET JEREMY_HENTY_FILTER */
/* 0.07.8 15-Nov-05 nm mmunif.c - now detects wrong order in bracket matching
   heuristic to further reduce ambiguous unifications in Proof Assistant */
/* 0.07.7 12-Nov-05 nm mmunif.c - add "[","]" and "[_","]_" bracket matching
   heuristic to reduce ambiguous unifications in Proof Assistant.
   mmwtex.c - added heuristic for HTML spacing after "sum_" token. */
/* 0.07.6 15-Oct-05 nm mmcmds.c,mmpars.c - fixed compressed proof algorithm
   to match spec in book (with new algorithm due to Marnix Klooster).
   Users are warned to convert proofs when the old compression is found. */
/* 0.07.5 6-Oct-05 nm mmpars.c - fixed bug that reset "severe error in
   proof" flag when a proof with severe error also had unknown steps */
/* 0.07.4 1-Oct-05 nm mmcmds.c - ignored bug 235, which could happen for
   non-standard logics */
/* 0.07.3 17-Sep-05 nm mmpars.c - reinstated duplicate local label checking to
   conform to strict spec */
/* 0.07.2 19-Aug-05 nm mmwtex.c - suppressed math content for lemmas in
   WRITE THEOREMS output */
/* 0.07.1 28-Jul-05 nm Added SIMPLE_TEX qualifier to SHOW STATEMENT */
/* 0.07:  Official 0.07 22-Jun-05 corresponding to Metamath book */
/* 0.07x:  Fixed to work with AMD64 with 64-bit longs by
   Waldek Hebisch; deleted unused stuff in mmdata.c */
/* 0.07w:  .mm date format like "$( [7-Sep-04] $)" is now
   generated and permitted (old one is tolerated too for compatibility) */
/* Metamath Proof Verifier - main program */
/* See the book "Metamath" for description of Metamath and run instructions */

/* The overall functionality of the modules is as follows:
    metamath.c - Contains main(); executes or calls commands
    mmcmdl.c - Command line interpreter
    mmcmds.c - Extends metamath.c command() to execute SHOW and other
               commands; added after command() became too bloated (still is:)
    mmdata.c - Defines global data structures and manipulates arrays
               with functions similar to BASIC string functions;
               memory management; converts between proof formats
    mmhlpa.c - The help file, part 1.
    mmhlpb.c - The help file, part 2.
    mminou.c - Basic input and output interface
    mmmaci.c - THINK C Macintosh interface (probably obsolete now)
    mmpars.c - Parses the source file
    mmpfas.c - Proof Assistant
    mmunif.c - Unification algorithm for Proof Assistant
    mmutil.c - Miscellaneous I/O utilities for non-ANSI compilers (has become
               obsolete and is now an empty shell)
    mmveri.c - Proof verifier for source file
    mmvstr.c - BASIC-like string functions
    mmwtex.c - LaTeX/HTML source generation
    mmword.c - File revision utility (for TOOLS> UPDATE) (not generally useful)
*/

/*****************************************************************************/
/* ------------- Compilation Instructions ---------------------------------- */
/*****************************************************************************/

/* These are the instructions for the gcc compiler (standard in Linux and
   Cygwin for Windows).
   1. Make sure each .c file above is present in the compilation directory and
      that each .c file (except metamath.c) has its corresponding .h file
      present.
   2. In the directory where these files are present, type:
         gcc metamath.c m*.c -o metamath
   3. For better speed and error checking, use these gcc options:
         gcc m*.c -o metamath -O3 -funroll-loops -finline-functions \
             -fomit-frame-pointer -Wall -ansi -pedantic
   4. The Windows version in the download was compiled with LCC-Win32:
         lc -O m*.c -o metamath.exe
*/


/*****************************************************************************/


/*----------------------------------------------------------------------*/


#include <string.h>
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdarg.h>
#include <time.h>  /* 21-Jun-2014 nm For ELAPSED_TIME */
#ifdef THINK_C
#include <console.h>
#endif
#include "mmutil.h"
#include "mmvstr.h"
#include "mmdata.h"
#include "mmcmdl.h"
#include "mmcmds.h"
#include "mmhlpa.h"
#include "mmhlpb.h"
#include "mminou.h"
#include "mmpars.h"
#include "mmveri.h"
#include "mmpfas.h"
#include "mmunif.h"
#include "mmword.h"
#include "mmwtex.h"
#ifdef THINK_C
#include "mmmaci.h"
#endif

int qsortStringCmp(const void *p1, const void *p2);
vstring qsortKey; /* Pointer only; do not deallocate */

char* argv1;
char* argv2;
char* argv3;
/* Declare the global variables to be assigned the values argv[1], argv[2], and
   argv[3]. */

int main(int argc, char *argv[])
{

/* argc is the number of arguments; argv points to an array containing them */
#ifdef THINK_C
/* Set console attributes */
console_options.pause_atexit = 0; /* No pause at exit */
console_options.title = (unsigned char*)"\pMetamath";
#endif

#ifdef THINK_C
  /* The standard stream triggers the console package to initialize the
     Macintosh Toolbox managers and use the console interface.  cshow must
     be called before using our own window to prevent crashing (THINK C
     Standard Library Reference p. 43). */
  cshow(stdout);
  /* Initialize MacIntosh interface */
  /*ToolBoxInit(); */ /* cshow did this automatically */
  /* Display opening window */
  /*
  WindowInit();
  DrawMyPicture();
  */
  /* Wait for mouse click or key */
  /*while (!Button());*/
#endif


  /****** If listMode is set to 1 here, the startup will be Text Tools
          utilities, and Metamath will be disabled ***************************/
  /* (Historically, this mode was used for the creation of a stand-alone
     "TOOLS>" utility for people not interested in Metamath.  This utility
     was named "LIST.EXE", "tools.exe", and "tools" on VMS, DOS, and Unix
     platforms respectively.  The UPDATE command of TOOLS (mmword.c) was
     custom-written in accordance with the version control requirements of a
     company that used it; it documents the differences between two versions
     of a program as C-style comments embedded in the newer version.) */
  listMode = 1; /* Force Metamath mode as startup */


  toolsMode = listMode;

  if (!listMode) {
    /*print2("Metamath - Version %s\n", MVERSION);*/
    /* print2("Metamath - Version %s%s", MVERSION, space(27 - (long)strlen(MVERSION))); */
  }
   /* if (argc < 2) */ /* print2("Type HELP for help, EXIT to exit.\n"); */

  /* Allocate big arrays */
  initBigArrays();

  void stepprover(char* proofWorksheet, char* labels);
  void nameUsersProofFile(char* proofWorksheet);
  void completeusersproof(char* proofWorksheet, char* mapFfToEel, char* labels);
  /* Declare called functions. */

  argv1 = "";
  argv2 = "";
  argv3 = "";
  /* Initialize to "" the global variables argv1, argv2, and argv3. */

  if (argc > 1) argv1 = argv[1];
  if (argc > 2) argv2 = argv[2];
  if (argc > 3) argv3 = argv[3];
  /* If command line to run completeusersproof has no command line arguments
     other than the .exe file itself, which is the value of argv[0]), then
	 argc = 1 and argv1, argv2, and argv3 will each retain its initial value of
     "". argv<i> cannot be assigned the value of argv[i] unless argc > i
	 because otherwise argv[i] does not exist.

	 The following are the values which argv<i> may have.

	 argv<i> VALUE OF argv<i>            DESCRIPTION

	 argv1   "runCompleteusersproofsmv"  Complete each subproof completable by
	                                     the mmj2 unification means by that
										 means. Complete each remaining
										 subproof completable by the single
										 metavariable deduction means by that
										 means. Attempt to complete each
										 remaining subproof by the unification
										 theorem means. Complete each subproof
										 completable by that means.


	         "runCompleteusersproofmv"   Complete each subproof completable by
	                                     the mmj2 unification means by that
										 means. Complete each remaining
										 subproof completable by the
										 metavariable deduction means by that
										 means. Attempt to complete each
										 remaining subproof by the unification
										 theorem means. Complete each subproof
										 completable by that means.

             "runStepprover"             Run only the stepprover() function on
			                             the User's Proof to try to 2-step
										 prove each 0-hypothesis step. Complete
										 each 2-step provable 0-hypothesis step.


	         <any character string not   Complete each subproof completable by
	          one of the above>          the mmj2 unification means by that
										 means. Attempt to complete each
										 remaining subproof by the unification
										 theorem means. Complete each subproof
										 completable by that means. This is the
										 default value. By convention, the
										 string """" may be used for this
										 value.

	 argv2   "onlyShortScripts"          mmj2 unify using the AutoHotkey
	                                     scripts unify.exe or unifyFF.exe

	         "scripts"                   mmj2 unify using the AutoHotkey
                                         scripts unify.exe, unifylong.exe, or
	                                     unifyFF.exe

		     <any character string not   mmj2 unify using the mmj2 batch test
              one of the above>          run parm. This is the default value
			                             of argv2. The character string """"
										 may be used.

     argv3   "<i>", where i = 1 to 7,    <i> is the maximum number of
	                inclusive            0-hypothesis steps to attempt to
                                         2-step prove in parallel, with all
                                         duplicates in a single
                                         duplicateUnificationThms.txt text
                                         file. mmj2 unifications using
										 unifylong.exe may not run properly
										 for larger values of i.

	         <any character string not   The maximum number of 0-hypothesis
              one of the above>          steps to attempt to 2-step prove in
                                         parallel is 8, the default value.
                                         The character string """" or "8"
                                         may be used.
  */

  if ( strcmp(argv1,"runStepprover") == 0 )

    {
      /* If command line argument 1 is "runStepprover", then run the
	     stepprover() function on the Proof Worksheet. Execution terminates
		 after running stepprover(). completeusersproof() is not executed.

         completeusersproof attempts to 2-step prove any 0-hypothesis proof
		 steps which do not unify with a theorem in set.mm. If there exists in
		 set.mm some theorem which is a semantic variation of such a step and
		 if there exists in set.mm some 1-hypothesis deduction which deduces
		 the proof step from that semantic variation, then completeusersproof
		 will 2-step prove that proof step. This stand-alone stepprover
		 capability of completeusersproof is intended to be used for
		 non-Virtual Deduction Users' Proofs. For Virtual Deduction proofs, the
		 stepprover() function is utilized along with other functions upon
		 application of either the "Completeusersproof" command, the
		 "Completeusersproof smv" command, or the "Completeusersproof mv"
		 command. If the "Completeusersproof" command, the
		 "Completeusersproof smv" command, or the "Completeusersproof mv"
		 command is appplied to a User's Proof not written as a Virtual
		 Deduction Proof, then useless steps may be generated. Applying the
		 "Stepprover" command to a non-Virtual Deduction proof may generate
		 useful 2-step subproofs without also generating useless steps which
		 would have been useful only if the User's Proof was written as a
		 Virtual Deduction Proof.

		 When the value of argv[1] is "runStepprover", there are no calls of
		 mmj2Unify() loaded with fd.txt and therefore there is never a need
		 to remove any unwanted type ff labels. None are generated.

		 There is no need to call completeTheoremSteps() when the value of
		 argv[1] is "runStepprover" because no unificiation permutations
		 would be generated because every theorem step to be attempted to
		 be 2-step proved is not a generated step. A theorem step must
		 be generated to qualify as one for which unification theorem
		 permutations are generated. */

      stepprover("proofWorksheet.txt", "labels.txt");
      /* The first mmj2Unify() function call of stepprover() unifies the Proof
	     Worksheet before attempting to 2-step prove all 0-hypothesis steps not
		 proven with the initial unification. */

      nameUsersProofFile("proofWorksheet.txt");
      /* Prior executing this statement the sole output file containing the User's
         Proof after processing by stepprover() is
	     c:\mmj2\mmj2jar\proofWorksheet.txt. This statement creates a second output
	     file with identical content. That output file is
	     c:\mmj2\mmj2jar\myproofs\<theorem>.mmp, where <theorem> is the theorem
	     name in the header of the User's Proof. */

      if (listMode && listFile_fp != NULL)
	    {
          fclose(listFile_fp);
	      /* Close logging command file */
        }

      return 0;

    }

  completeusersproof("proofWorksheet.txt", "mapFfToEel.txt", "labels.txt");
  /* Call completeusersproof() to attempt to complete the User's Proof.
	 completeusersproofSmv.bat passes the value "runCompleteusersproofsmv" to
	 argv[1] and to the global variable argv1. completeusersproofMv.bat passes
	 the value "runCompleteusersproofmv" to argv[1] and to the global variable
	 argv1. completeusersproofSp.bat passes the value "runStepprover" to
	 argv[1] and argv1. Otherwise, no value is passed to argv[1] and argv1 and
	 the default applies. See above for additional information pertaining to
	 these values. */

  if (listMode && listFile_fp != NULL)
    {
      fclose(listFile_fp);
	  /* Close logging command file */
    }

  return 0;

}


/* df-completeusersproof() */
void completeusersproof(char* proofWorksheet,
                        char* mapFfToEel, char* labels)
{

  /* MOTIVATION FOR COMPLETEUSERSPROOF. As explained in the description of
     wvd1, Natural Deduction is a powerful strategy for proving theorems
	 and deductions. Natural Deduction uses a Gentzen-type system. We are
	 motivated to write Natural Deduction proofs (for the power of proving
	 using a Gentzen-type system) and verify them using Metamath's
	 proofchecking capability. Metamath uses a Hilbert-type deductive system.
	 As explained in wvd1, the virtual deduction notation has been added to
	 set.mm to have a language of a Hilbert-type deductive system which
	 can be used like a Gentzen-type system. Using the virtual deduction
	 notation of set.mm, one can effectively write Natural Deduction Proofs
	 in the virtual deduction language of Metamath. We call these proofs
	 Virtual Deduction proofs. A Virtual Deduction proof is the
	 Metamath-specific version of a Natural Deduction Proof. An example of a
     Virtual Deduction User's Proof is
	 https://www.virtualdeduction.com/chordthmaltvd.html .

	 A Virtual Deduction proof generally cannot be directly input on a mmj2
	 Proof Worksheet and, after being translated into conventional notation,
	 completed by the mmj2 tool because it is usually missing some proof steps
	 which are not part of the Virtual Deduction proof but are necessary for a
	 complete Metamath Proof. These missing steps may be automatically added
	 by an automated proof assistant. completeusersproof is such a proof
	 assistant. For some subproofs, completeusersproof may automatically
	 generate one or more additional steps and may edit the hypothesis field of
	 some steps. The mmj2 unify command labels assertion steps of the proof,
	 unifying them with reference theorems or deductions in set.mm.

	 The User may write a Virtual Deduction proof and automatically transform
	 it into a complete Metamath proof using the completeusersproof tool. The
	 completed proof has been checked by the Metamath program. The task of
	 writing a complete Metamath proof is reduced to writing what is
	 essentially a Natural Deduction Proof.

	 Generally, proving a Virtual Deduction User's Proof with the assistance of
	 the completeusersproof tool reduces the amount Metamath-specific knowledge
	 required by the User. Often, no knowledge of the specific theorems and
	 deductions in set.mm is required to write some of the subproofs of a
	 Virtual Deduction proof. Often, no knowledge of the Metamath-specific
	 names of reference theorems and deductions in set.mm is required for
	 writing some of the subproofs of a User's Proof. Often, the User may write
	 subproofs of a proof using theorems or deductions commonly used in
	 mathematics and correctly assume that some form of each is contained in
	 set.mm and that completeusersproof will automatically generate the steps
	 necessary to utilize them to complete the subproofs. Often, the fraction
	 of the work which may be considered tedious is reduced and the total
	 amount of work is reduced.

	                           *  *  *

     completeusersproof() is the single primary function of the
	 completeusersproof program. All other functions used by the
	 completeusersproof program are called by completeusersproof().

	 The input Proof Worksheet is the "User's Proof". completeusersproof
	 completes each subproof of the translated User's Proof it is capable of
	 completing.

	 What do we mean by the term "subproof"? A subproof of the User's
	 Proof might have been called simply a deduction within the User's Proof.
	 We do not call it a deduction because a step or steps automatically added
	 to the proof which are associated with the deduction may change the
	 deduction into a deduction with more steps or may create an additional
	 deduction associated with the modified or unmodified original deduction.
	 The new collection of associated steps may not be a single deduction and
	 therefore cannot properly be called a deduction. Therefore, we call the
	 original deduction and this new collection of steps arising from the
	 original deduction a "subproof". Our narrow definition differs from the
	 usual definition of subproof. For the usual definition of subproof, a
	 subproof may include many deductions of a proof. For our narrow
	 definition, a subproof arises from and is associated with a single
	 deduction of the original User's Proof. Generally, each original
	 n-hypothesis deduction of the User's Proof, where n is any positive
	 integer including 0, is a subproof, and may be modified by
	 completeusersproof to be a different subproof which is the original
	 subproof with the "details" necessary for a complete Metamath subproof
	 added.

	 DEFINITION OF A VIRTUAL DEDUCTION.

	                    A Theorem fine is Deduction,
					    For it allows work-reduction:
						   To show "A implies B",
						   Assume A and prove B;
					  Quite often a simpler production.

						-- Stefan Bilaniuk

	 For the conventional notation virtual deduction ( ( ph /\ ps ) -> ch ) ,
	 each conjunct of the antecedent is thought of as a hypothesis and the
	 consequent is thought of as an assertion of the deduction ph and ps infers
	 ch. ph and ps are not actual hypotheses and ch is not an actual assertion.
	 We call ph a "virtual hypothesis" because it is not an actual hypothesis.
	 ch is a "virtual conclusion" because it is not an actual assertion. Just
	 as the virtual image seen in a plane mirror is not actual or real because
	 it is formed in a location light does not actually reach,
	 ( ( ph /\ ps ) -> ch ) is a "virtual deduction" because it is not an
	 actual or real deduction. We are motivated to think in terms of virtual
	 objects because "it allows work-reduction" and because proving is "Quite
	 often a simpler production." We call the proving style where one
	 interprets the well-formed formula of each proof step to be a virtual
	 deduction "Virtual Deduction" instead of "Natural Deduction" because
	 Virtual Deduction may be given the above meaning and because Natural
	 Deduction has a broader meaning than this Metamath-specific style with its
	 own special notation.

	 In the written description of the set.mm syntax definition wvd1 , a
	 virtual deduction is defined to be the analog in H of a sequent in G1. The
	 connective ->. (a mediator connective) separates the virtual hypothesis
	 collection on the left side of ->. from the virtual conclusion on the
	 right side of ->. . A virtual deduction with no ->. connective is a
	 virtual deduction with no virtual hypotheses. A virtual deduction with no
	 virtual hypotheses may be alternatively written in "standard T form". The
	 empty virtual hypothesis collection is defined to be T. . |- <wff> and
	 |- (. T. ->. <wff> ). denote equivalent virtual deductions, the latter
	 being the former in standard T form. More about the standard T form may be
	 found below.

	 Only the User's Proof is written using Virtual Deduction notation. The
	 User's Proof with as many subproofs as possible completed by
	 completeusersproof is in conventional notation. This conventional
	 notation complete or partially complete proof may be interpreted as a
	 conventional notation virtual deduction proof.

	 DEFINITION OF A CONVENTIONAL NOTATION VIRTUAL DEDUCTION. A conventional
	 notation virtual deduction is a virtual deduction with conventional
	 notation symbols substituted for virtual deduction symbols. If a
	 conventional notation virtual deduction has no -> connective, then it
	 has no virtual hypotheses. If one or more -> connectives occur in it,
	 then the outermost -> connective is the mediator connective or(excl.)
	 none of the -> connectives is the mediator connective and there are no
	 virtual hypotheses.

	 Some conventional notation virtual deductions are susceptible to more than
	 one interpretation. The virtual deduction's context usually fully
	 determines which interpretation is the correct one. As an example of
	 ambiguity, the conventional notation virtual deduction
	 |- ( x e. A -> x C_ B ) may correspond to the virtual deduction
	 |- (. x e. A ->. x C_ B ). or(excl.) to the no-virtual-hypotheses
	 virtual deduction |- ( x e. A -> x C_ B ) .

	 The reason each step of a User's Proof is specified to be a virtual
	 deduction with virtual deduction notation is to assure that each
	 step of the proof has a unique interpretation. After the proof is
	 translated, the correct interpretation of any original step, now a
	 conventional notation virtual deduction, may be determined by its
	 interpretation prior to translation if context alone is insufficient for
	 disambiguation.

	 The completeusersproof program relies on the Virtual Deduction notation
	 because there is no ambiguity. Therefore, it is necessary for the User's
	 Proof to be in Virtual Deduction notation in order to utilize the full
	 potential of completeusersproof to complete as many of the subproofs of
	 the User's Proof as possible. If a User's Proof is not written as a
	 Virtual Deduction proof, then completeusersproof will interpret each step
	 of the proof to be a virtual deduction with no virtual hypotheses.

	 One sufficient reason why initial virtual deductions in Virtual Deduction
	 notations are translated into conventional notation virtual deductions is
	 because the theorems and deductions in set.mm are in conventional notation
	 and may be utilized if and only if the steps of the User's Proof are
	 translated into the same conventional notation after the disambiguation
	 information contained in the original Virtual Deduction proof is extracted
	 and saved. It is saved in the ff* labels, which may also be referred to as
	 labels of type ff. A virtual deduction subproof cannot unify with a
	 theorem or deduction in the main set.mm unless it is in conventional
	 notation.

	 Natural Deduction which is not Virtual Deduction may be and has been used
	 as a way to simplify proving in Metamath. Some Users may find that even
	 greater simplicity is achieved by constructing their Users Proofs to
	 be compatible with the completeusersproof tool. That is, by writing Users
	 Proofs as Virtual Deduction proofs in Virtual Deduction notation. If a
	 User's Proof is not a Virtual Deduction proof in Virtual Deduction
	 notation, the completeusersproof tool will generally not be useful. An
	 exception to this is that the completeusersproof tool will attempt to
	 2-step prove each 0-hypothesis step (each theorem) of the User's Proof and
	 will complete any of these steps which are completable. If the argument
	 completeusersproof tool is used with the value of the command line
	 argv[1] equal to "runStepprover", then only the stepprover() function of
	 completeusersproof will process the User's Proof and the User's Proof
	 should not use Virtual Deduction notation.

	 A Virtual Deduction proof in Virtual Deduction notation is written in what
	 may be thought of as sort of a "higher level language", permitting the
	 omission of some of the detail required by a conventional notation
	 Metamath proof. The omitted details are the steps automatically generated
	 by the completeusersproof proof assistant. These steps may be considered
	 "technical steps" and the steps of the Virtual Deduction User's Proof may
	 be considered to be the more creative steps of the proof.

                           *  *  *

     We will sometimes refer to a conventional notation virtual deduction as
	 a "virtual deduction". Context usually determines whether "virtual
	 deduction" means a virtual deduction in non-conventional notation or a
	 conventional notation virtual deduction.

	 A proof is a Virtual Deduction proof in non-conventional notation if
	 every step of the proof is a virtual deduction in non-conventional
	 notation. A virtual deduction with no virtual hypotheses and no empty
	 virtual hypothesis collection is a virtual deduction in Virtual Deduction
	 notation (non-conventional notation) and a virtual deduction in
	 conventional notation.

	 Early in the execution of completeusersproof the original Virtual
	 Deduction proof in non-conventional notation is translated into
	 a conventional notation Virtual Deduction proof. Of the steps of the
	 proof automatically generated by completeusersproof towards the goal
	 of completing the original proof, with the exception of non-unionized
	 assertion steps (used only in attempting to complete subproofs by the
	 metavariable deduction unification means), each added step is a
	 conventional notation virtual deduction. The wff of a non-unionized
	 assertion step is not a virtual deduction unless every element of its
	 collection of virtual hypothesis collections is a virtual hypothesis.
	 Many of the terms used above are defined below.

	 The virtual deduction

	     |- (. (. Tr A ,. ( z e. y /\ y e. A ) ). ->. z e. A ).  (wff1)

	 is automatically translated by completeusersproof to be

         |- (  (  Tr A /\ ( z e. y /\ y e. A ) )  ->  z e. A )   (wff2)

     wff2 is the conventional notation virtual deduction counterpart of the
	 virtual deduction wff1. There are two virtual hypotheses. One is Tr A .
	 The other is ( z e. y /\ y e. A ) . The virtual conclusion is z e. A .
	 Every virtual deduction has one and only one virtual conclusion. For a
	 virtual deduction in Virtual Deduction notation, " ,. " separates pairs of
	 adjoining virtual hypotheses. In a conventional notation virtual deduction
	 " /\ " may have the role of separating a pair of adjoining virtual
	 hypotheses or(excl.) may be a component symbol (the conjunction symbol) of
	 a virtual hypothesis or a virtual conclusion.
	 ( Tr A /\ ( z e. y /\ y e. A ) ) is the virtual hypothesis collection of
	 wff2.

	 With translation, " ,. " becomes " /\ ", " ->. " becomes " -> ", " (. "
	 becomes " ( ", and " ), " becomes " ) ".

	 There are three possible interpretations of wff2. The correct
	 interpretation is the same interpretation as wff1. One incorrect
	 interpretation interprets wff2 to have a single virtual hypothesis,
	 ( Tr A /\ ( z e. y /\ y e. A ) ) . The other incorrect interpretation is
	 that wff2 has no virtual hypotheses, the entire wff being a virtual
	 conclusion.

	 A metavariable deduction is a deduction for which each hypothesis step may
	 be interpreted to be a conventional notation virtual deduction having a
	 virtual hypothesis collection which is a wff variable. We use the term
	 "metavariable" for the wff variable as Mario Carneiro has in his
	 presentation "Natural Deduction in the Metamath Proof Language" presented
	 in the summer of 2014. We say a metavariable deduction is in standard form
	 if the elements of the collection on the left side of the mediator -> of
	 its assertion are exactly the virtual hypothesis collections of the
	 hypothesis steps. Unless a standard form metavariable deduction has only
	 one hypothesis, its assertion is not a virtual deduction because its
	 collection of virtual hypothesis collections is not a virtual hypothesis
	 collection. We could have defined this collection to be a virtual
	 hypothesis collection on the ground that a wff variable is an single
	 variable. We don't use that definition because a wff variable is a
	 schemata for a virtual hypothesis collection. An example of a standard
	 form metavariable deduction in set.mm is trelded.

	 We say that the collection of virtual hypothesis collections of the
	 assertion of a standard form metavariable deduction with more than one
	 hypothesis is "non-unionized" and that the assertion is a non-unionized
	 assertion. We use the term "union" because unionizing a collection of
	 virtual hypothesis collections to obtain a virtual hypothesis collection
	 is analogous to taking the union of a class. We define a "unionized
	 assertion" to be an assertion derivable from a non-unionized assertion by
	 unionizing its collection of virtual hypothesis collections. The
	 unionized collection is a virtual hypothesis collection consisting of the
	 elements of the elements of the collection of virtual hypothesis
	 collections of the non-unionized assertion. We adopt the convention that
	 the virtual hypothesis collection of a unionized assertion contains no
	 duplicate elements. It is undesirable for a virtual hypothesis collection
	 to contain duplicate virtual hypotheses because the virtual hypothesis
	 collection is denoted by a longer and more complex character string which
	 does not contain more information. More importantly, if the operation of
	 unionizing a non-unionized collection of virtual hypothesis collections
	 did not include the removal of duplicate virtual hypotheses, then there
	 would exist a multiplier effect whereby proof steps with virtual
	 hypothesis collections containing duplicate virtual hypotheses which are
	 hypothesis steps of other proof steps would spawn more duplicate virtual
	 hypotheses.

	 One possible way a subproof may be completed is by unifying it with a
	 standard form metavariable deduction in set.mm. If the unifying
	 standard form metavariable deduction in set.mm is a 1-hypothesis
	 deduction, then it will unify with the original deduction of the subproof.
	 If the unifying standard form metavariable deduction in set.mm has more
	 than one hypothesis, then it will not unify with the original deduction of
	 the subproof because the assertion of the original deduction is unionized
	 whereas the assertion of the metavariable deduction in set.mm is not.
	 completeusersproof generates a non-unionized assertion. The deduction of
	 that non-unionized assertion, which has the same hypotheses as the
	 original deduction, unifies with the metavariable deduction in set.mm.
	 The assertion of the original deduction is deduced from the non-unionized
	 assertion with a 1-hypothesis uun* deduction. That deduction is the second
	 deduction of the subproof. The number of steps of the subproof
	 increases by one step. That added step is the non-unionized assertion.
	 The non-unionized assertion immediately precedes the unionized assertion,
	 which is the original assertion of the subproof. completeusersproof
	 automatically generates the contents of the hypothesis field of the
	 non-unionized assertion step to be the same as the original contents of
	 the hypothesis field of the original assertion. completeusersproof deletes
	 the original contents of the original assertion's hypothesis field and
	 inserts into that hypothesis field the step number of the non-unionized
	 assertion step.

     Each metavariable deduction in standard form in set.mm has a particular
	 ordering of metavariable virtual hypothesis collections within the
	 collection of virtual hypothesis collections of its assertion. The
	 ordering of the virtual hypothesis collections of the collection of
	 virtual hypothesis collections of the generated non-unionized assertion of
	 a deduction of a subproof of a Proof Worksheet to be unified with a
	 metavariable deduction in set.mm must match the ordering of the collection
	 of the assertion of the metavariable deduction in set.mm. Therefore, all
	 possible permutations of the ordering of the virtual hypothesis
	 collections within the collection of virtual hypothesis collections of the
	 non-unionized assertion must be tried. completeusersproof generates one
	 non-unionized assertion for each permutation. The hypothesis steps of each
	 deduction corresponding to each non-unionized assertion permutation are
	 the same. They are the hypothesis steps of the original subproof. If one
	 or more of these deductions unifies with one or more standard form
	 metavariable deductions in set.mm, then one of the unifying deductions is
	 picked by completeusersproof to be that metavariable deduction in set.mm
	 to complete the non-unionized asssertion's deduction of the subproof. The
	 remaining non-unionized assertion permutations are deleted. If no
	 deduction permutation unifies with a standard form metavariable deduction
	 in set.mm, then all non-unionized assertion permutations are deleted and
	 the subtheorem is not completed.

     For each ff* false deduction corresponding to a subproof of the User's
	 Proof there corresponds a collection of uun* labels which contains all
	 possible permutations of uun* deductions which deduce the unionized
	 assertion from the non-unionized assertion permutations. For example, the
	 virtual hypothesis collections for the two hypotheses of a subproof of a
	 User's Proof may be ( <wff1> /\ <wff2> ) and <wff2> . Then there are a
	 total of two uun* permutations, each deducing the unionized assertion from
	 one of the two non-unionized assertion permutations. One has as its
	 hypothesis an assertion with the collection of virtual hypothesis
	 collections ( ( <wff1> /\ <wff2> ) /\ <wff2> ) and the other
	 ( <wff2> /\ ( <wff1> /\ <wff2> ) ) . The unionized assertion for both
	 deductions is the same and is the assertion of the original subproof. Its
	 virtual hypothesis collection is either ( <wff1> /\ <wff2> ) or(excl.)
	 ( <wff2> /\ <wff1> ) . If there exists a non-unionized assertion deduction
	 permutation which unfies with a standard form metavariable deduction in
	 set.mm and that deduction is picked by completeusersproof, then the uun*
	 deduction corresponding to the former deduction's non-unionized assertion
	 deduces the unionized assertion of the original subproof from that
	 non-unionized assertion. Both deductions of the subproof are completed and
	 the subproof is completed. This means by which an attempt is made to
	 complete a subproof is called the metavariable deduction unification
	 means. Included in this means is putting in standard T form any
	 no-virtual-hypothesis steps of the original subproof.

	 If the virtual hypothesis collections of the hypotheses of a metavariable
	 deduction in standard form in set.mm are identical, then the union of the
	 the collection of virtual hypothesis collections of its hypotheses is
	 identical to the virtual hypothesis collection of each hypothesis. Any
	 metavariable deduction in set.mm having the same virtual hypothesis
	 collection metavariable for each hypothesis may have as its assertion a
	 virtual deduction having the same virtual hypothesis collection as the
	 hypotheses. A metavariable deduction in this form will unify with a
	 subproof in a Proof Worksheet using mmj2 alone. Mario Carneiro has
	 employed this metavariable deduction form for many deductions he has
	 added to set.mm. He discussed it in his presentation "Natural Deduction
	 in the Metamath Proof Language". We shall call a metavariable deduction in
	 this form a single metavariable deduction.

	 Some steps of a User's Proof may have no virtual hypotheses. For example,

	     |- A C_ suc A    (wff3)

	 may be such a step in a User's Proof. It is originally a virtual deduction
	 in non-conventional notation. Upon translation of the proof, it becomes a
     conventional notation virtual deduction. Because it has no virtual
	 hypotheses, the syntax of wff3 is the same whether it is interpreted as a
     virtual deduction in non-conventional notation or a conventional notation
     virtual deduction. wff3 may be put in "standard T form" so that it is a
     virtual deduction having a virtual hypothesis collection which is empty.

         |- (. T. ->. A C_ suc A ).     (wff4)

	 wff4 is wff3 in standard T form. wff4 translated is

         |- (  T. ->  A C_ suc A )      (wff5)

	 Both wff4 and wff5 are virtual deductions. wff4 is a virtual deduction in
	 non-conventional notation and wff5 is a conventional notation virtual
	 deduction. Both have the same interpretation.

	 In order for completeusersproof to attempt to complete a subproof
	 of the User's Proof using the metavariable deduction unification means, it
	 puts each no-virtual-hypotheses step of the subproof into standard T form.
	 This is necessary because every hypothesis of a metavariable deduction in
	 set.mm has a metavariable virtual hypothesis collection.

	 The union of a singleton is its element. U. { A } = A . A virtual
	 hypothesis collection differs from a class in that, among other things, a
	 singleton virtual hypothesis collection is identical to the virtual
	 hypothesis it contains. If x e. A is the virtual hypothesis of a singleton
	 virtual hypothesis collection, then that collection must be denoted by
	 x e. A because ( x e. A ) is syntactically invalid.

	 If the virtual hypothesis collection of each hypothesis of a subproof is
	 a singleton and these virtual hypothesis collections are distinct, then
	 the collection of these virtual hypothesis collections is a virtual
	 hypothesis collection. This collection may be denoted by each one of n
	 possible wffs, one wff for each virtual hypothesis ordering permutation,
	 where n is the number of virtual hypothesis ordering permutations. One of
	 those permutations is the virtual hypothesis collection of the unionized
	 assertion of the original subproof. If there exists a standard form
	 metavariable deduction in set.mm whose assertion has a collection of
	 virtual hypothesis collections with a virtual hypothesis collection
	 ordering matching that of the assertion of the original subproof, then it
	 will unify with the original subproof and the subproof will be completed
	 by mmj2 unification alone. No non-unionized assertion will be generated.
	 If the standard form metavariable deduction in set.mm which completes the
	 subproof has an assertion with a collection of virtual hypothesis
	 collections with a different ordering, then completeuserproof will
     find the non-unionized assertion permutation which matches the permutation
     of the standard form metavariable deduction in set.mm and the subproof
     will be completed by adding that non-unionized assertion permutation step.

     ( <wff6> /\ ( <wff6> /\ <wff7> ) /\ T. ) is a collection of virtual
	 hypothesis collections occuring in a completeusersproof-generated
	 non-unionized assertion of a subproof of a Proof Worksheet. <wff6> is
	 the virtual hypothesis collection of one hypothesis of that
	 subproof, ( <wff6> /\ <wff7> ) is another, and T. is the third.  The
	 collection of the unionized assertion for this subproof, which is a step
	 of the original subproof, is ( <wff6> /\ <wff7> ) . The collection of the
	 non-unionized assertion is not a virtual hypothesis collection because the
	 element T. is not a virtual hypothesis and the element
	 ( <wff6> /\ <wff7> ) is not a virtual hypothesis. The latter element is
	 not a virtual hypothesis because it is not atomic - it contains two
	 virtual hypotheses. Even if this element was divided to create
	 ( <wff6> /\ <wff6> /\ <wff7> /\ T. ) , the resultant collection is
	 not a valid virtual hypothesis collection because it contains two <wff6>
	 virtual hypotheses.

	 Any subproof of a Proof Worksheet for which the virtual hypothesis
	 collection of the original assertion is identical to each virtual
	 hypothesis collection of each hypothesis of the subproof does not
	 need a non-unionized assertion and will be completed by mmj2
	 unification alone if a unifying single metavariable deduction exists in
	 set.mm. If the only unifying metavariable deduction in set.mm is in
	 standard form, then a non-unionized assertion step must be added for
	 completion of the subproof.

	 A subproof of a translated User's Proof may unify with a deduction in
	 set.mm without any steps added to the original subproof. Such subproofs
	 are said to be completed by the mmj2 unification means. This is the sole
	 means by which the mmj2 program completes subproofs. completeusersproof
	 is designed to first attempt to complete each subproof of the User's
	 Proof by this means. If the value of argv1 is "runCompleteusersproofmv",
	 if a subproof is not completed by the mmj2 unification means, then an
	 attempt is made to complete it by the metavariable deduction unification
	 means. All subproofs completable by this means and not completable by the
	 mmj2 unification means are completed by the metavariable deduction
	 unification means. If a subproof is not completable by either of these two
	 means, then completeusersproof attempts to complete it by adding a
	 unification theorem. If the unification theorem does not unify with a
	 theorem in set.mm, then completeusersproof will attempt to 2-step prove
	 the unification theorem in order to complete the subproof. We call the
	 means of adding a unification theorem and, if it does not unify, trying to
	 2-step prove it the unification theorem means. For an argv1 value of
	 "runCompleteusersproofmv", if a subproof does not complete by any of these
	 three means, the subproof will not be completed by completeusersproof.
	 Generally, some subproofs of a proof will complete upon application of the
	 mmj2 unification means, some will complete upon application of the
	 metavariable deduction unification means, some will complete upon
	 application of the unification theorem means, and some will not complete.
	 If all subproofs are completed, an RPN proof will be generated.

	 If a subproof unifies with a single metavariable deduction in set.mm, it is
	 completed by the mmj2 unification means, not by the metavariable deduction
	 unification means. If a non-unionized assertion's deduction of a subproof
	 is unified with a metavariable deduction in set.mm by applying the
	 metavariable deduction unification means, the metavariable deduction with
	 which it unifies is in standard form and has more than one hypothesis.
	 Every 1-hypothesis metavariable deduction is both a single metavariable
	 deduction and is a metavariable deduction in standard form. If a subproof
	 unifies with such a metavariable deduction, it completes by the mmj2
	 unification means.

	 Not all subproofs which complete by the mmj2 unification means unify with
	 a single metavariable deduction. Generally, a subproof completing by the
	 mmj2 unification means often does not unify with a single metavariable
	 deduction. For example, the subproof

	     5::          |- ( x e. A -> x e. suc A )
	     9:2:         |- x e. A
	     15:5,9:ax-mp |- x e. suc A

	 completes by the mmj2 unification means by unifying with the deduction
	 ax-mp . ax-mp is not a single metavariable deduction. Neither its
	 hypotheses or its assertion have a virtual hypothesis collection.

	 Any subproof completable by the mmj2 unification means is completable
	 using the mmj2 program alone. completeusersrpoof also has this same
	 capability, relying exclusively on invocation of the mmj2 program.
	 None of the steps of a Virtual Deduction User's Proof should be labeled.
	 Given this restriction, the mmj2 unification means can only complete a
	 subproof of a Virtual Deduction User's Proof if that subproof, after
	 translation, without any steps automatically added and without the
	 hypothesis field of any step edited, unifies with a theorem or deduction
	 in set.mm. If a translated subproof of a User's Proof, unmodified, does
	 not unify with a theorem or deduction in set.mm, then it may be
	 automatically completable using the additional capabilities of
	 completeusersproof, by automatically generating an additional step or
	 steps, or by adding steps and editing the hypothesis field of some steps.
	 These additional capabilities provide greater Virtual Deduction User's
	 Proof subproof completing power than available using the mmj2 program
	 alone.

	 We now discuss in greater detail the unification theorem means of
	 completing a subproof. A subproof having more than one step unifies with
	 either a deduction in set.mm or(excl.) with a ff* false deduction in
	 fd.txt. 1-step subproofs unify with a theorem in set.mm or(excl.) do not
	 unify. There are no 0-hypothesis ff* deductions in fd.txt. Each ff* false
	 deduction corresponds to an eel* deduction which is the same as the ff*
	 deduction, except it is in conventional notation and has an additional
	 hypothesis, a "unification theorem". Presuming the User wrote a correct
	 subproof, that subproof is true before a unification theorem is added to
	 it. If the translated subproof did not complete upon application of the
	 mmj2 unification means, the subproof does not unify with any deduction in
	 set.mm. It does unify with a ff* false deduction in fd.txt. The translated
	 ff* deduction is false only because it is missing the unification theorem
	 hypothesis step which its corresponding eel* deduction has. The eel*
	 deduction is contained in set.mm. completeusersproof adds the unification
	 theorem to the subproof by replacing the ff* label with its corresponding
	 eel* label and applying the mmj2 unify command. The modified subproof
	 unifies with the eel* deduction.

	 The unification theorem of a virtual deduction deduction is that unique
	 virtual deduction whose virtual hypothesis collection is the collection of
	 the virtual conclusions of the hypotheses of the virtual deduction
	 deduction and whose virtual conclusion is the virtual conclusion of the
	 assertion of the virtual deduction deduction. Every unification theorem is
	 a theorem which may or may not unify with a theorem in set.mm.

	 If set.mm is sufficiently rich with respect to the mathematics which is
	 the subject of the User's Proof, then it is likely that the unification
	 theorem of a subproof or a semantic variation of it will be a theorem in
	 set.mm. If the unification theorem unifies with a theorem in set.mm, then
	 completeusersproof completes the subproof by adding it alone. If the
	 unification theorem does not unify with a theorem in set.mm, then the
	 stepprover() function of completeusersproof() will attempt to 2-step prove
	 the unification theorem. If there exists in set.mm at least one semantic
	 variation of the unification theorem and if there exists in set.mm at
	 least one 1-hypothesis deduction which deduces the unification theorem
	 from one of its semantic variations in set.mm, then stepprover()
	 automatically generates an additional step which is a semantic variation
	 of the unification theorem and which unifies with a theorem in set.mm and
	 completes the subproof. Otherwise, the added unification theorem remains
	 unproven and the subproof, although proveable (assuming the original
	 subproof is correct), is not completed.

	 Let's illustrate use of the unification theorem means by way of an
	 example. The subproof of suctrALT4UP whose assertion is step 15 is shown
	 in its original form below. The proof of suctrALT4UP is contained in
	 the VirtualDeductionProofs.txt file included in the completeusersproof
	 download.

	   8:7:                  |- (. (. Tr A ,. ( z e. y /\ y e. suc A ) ). ->. ( y e. A -> z e. suc A ) ).
       ...
       ...
       12:11:                |- (.            ( z e. y /\ y e. suc A )    ->. ( y = A -> z e. suc A ) ).
       ...
       14:13:                |- (.            ( z e. y /\ y e. suc A )    ->. ( y e. A \/ y = A ) ).
       15:8,12,14:           |- (. (. Tr A ,. ( z e. y /\ y e. suc A ) ). ->. z e. suc A ).

	 The "..." lines represent other steps of the User's Proof which are not
	 hypothesis steps of the example subproof which are interspersed between
	 hypothesis steps of the example subproof. The subproof translated into
	 conventional notation is

       8:7:                  |- (  (  Tr A /\ ( z e. y /\ y e. suc A ) )  ->  ( y e. A -> z e. suc A ) )
       ...
       ...
       12:11:                |- (             ( z e. y /\ y e. suc A )    ->  ( y = A -> z e. suc A ) )
       ...
       14:13:                |- (             ( z e. y /\ y e. suc A )    ->  ( y e. A \/ y = A ) )
       15:14,12,8:           |- (  (  Tr A /\ ( z e. y /\ y e. suc A ) )  ->  z e. suc A )

	 completeusersproof automatically finds and adds to the assertion step the
	 label, eel2221, of the deduction in set.mm which deduces the assertion
	 step from the original hypotheses and the unification theorem, step d3.

       8:7:                  |- (  (  Tr A /\ ( z e. y /\ y e. suc A ) )  ->  ( y e. A -> z e. suc A ) )
       ...
       ...
       12:11:                |- (            ( z e. y /\ y e. suc A )     ->  ( y = A -> z e. suc A ) )
       ...
       14:13:                |- (            ( z e. y /\ y e. suc A )     ->  ( y e. A \/ y = A ) )
       d3::                  |- (  (  ( y e. A \/ y = A ) /\ ( y = A -> z e. suc A ) /\ ( y e. A -> z e. suc A ) ) -> z e. suc A )
       15:14,12,8,d3:eel2221 |- (  (  Tr A /\ ( z e. y /\ y e. suc A ) )  ->  z e. suc A )

	 This particular unification theorem, although a true theorem, does not
	 unify with any theorem in set.mm. If it did, the subproof would have
	 been completed at this point. But there does exist a 1-hypothesis
	 deduction in set.mm which deduces the unification theorem from a semantic
	 variation of it, a semantic variation which is in set.mm. That deduction
	 is 3imp31. The semantic variation is jao. completeusersproof automatically
	 finds these and 2-step proves the unification theorem using the
	 stepprover() function. This completes the subproof. The completed subproof
	 is

       8:7:                  |- (  (  Tr A /\ ( z e. y /\ y e. suc A ) )  ->  ( y e. A -> z e. suc A ) )
       ...
       ...
       12:11:                |- (            ( z e. y /\ y e. suc A )     ->  ( y = A -> z e. suc A ) )
       ...
       14:13:                |- (            ( z e. y /\ y e. suc A )     ->  ( y e. A \/ y = A ) )
       d6::jao               |- (  (  y e. A -> z e. suc A ) -> ( ( y = A -> z e. suc A ) -> ( ( y e. A \/ y = A ) -> z e. suc A ) ) )
       d3:d6:3imp31          |- (  (  ( y e. A \/ y = A ) /\ ( y = A -> z e. suc A ) /\ ( y e. A -> z e. suc A ) ) -> z e. suc A )
       15:14,12,8,d3:eel2221 |- (  (  Tr A /\ ( z e. y /\ y e. suc A ) )  ->  z e. suc A )

	 Note that each of steps 8, 12, and 14 is the (translated)
	 assertion of another subproof of the (translated) User's Proof. Those
	 subproofs also complete. The labels of the set.mm deductions which
	 complete these other subproofs are not shown only because those subproofs
	 are not being discussed here.

	 If ff2221 was not in fd.txt or(incl.) eel2221 was not in set.mm the above
	 subproof wouldn't complete by the unification theorem means for labels of
	 type eel and not of type ffeel. The type ffeel label is a subtype of type
	 eel. A label of type ffeel is of type eel. Not all type eel labels are of
	 type ffeel. Labels of type ffeel are more general than eel labels not of
	 type ffeel. Type ffeel deductions are false deductions but are not of
	 type ff. The type ff label corresponding to a type ffeel label is of the
	 form ff<i>h<j>, where <i> is the number of hypothesis steps of the type
	 ff false deduction and <j> is number of hypothesis steps of the ff
	 deduction having no virtual hypotheses and no virtual hypothesis
	 collection.

	 The above subproof of the suctrALT4UP User's Proof has 3 hypothesis steps,
	 none of which have no virtual deductions. The type ff labels for this
	 subproof are ff2221, corresponding to the more specialized
	 type eel label eel2221, and ff3h0, corresponding to the label of types
	 eel and ffeel, ffeel3h0. When both of these type ff labels occur in fd.txt
	 the more specialized, ff2221, is picked. If ff2221 is temporarily removed
	 from fd.txt, ff3h0 is picked. Given that ff2221 does not occur in fd.txt,
	 the present subproof subproof will unify with ff3h0 which will be replaced
	 with ffeel3h0 by substituteLabels(). The unification theorem means
	 completes type ffeel subproofs differently than type eel subproofs not of
	 type ffeel. We'll illustrate how the unification theorem means completes
	 type ffeel deductions using the subproof whose assertion is step 15 of
	 suctrALT4UP. This subproof of the User's Proof is

       8:7:                  |- (. (. Tr A ,. ( z e. y /\ y e. suc A ) ). ->. ( y e. A -> z e. suc A ) ).
       ...
       ...
       12:11:                |- (.            ( z e. y /\ y e. suc A )    ->. ( y = A -> z e. suc A ) ).
       ...
       14:13:                |- (.            ( z e. y /\ y e. suc A )    ->. ( y e. A \/ y = A ) ).
       15:8,12,14:           |- (. (. Tr A ,. ( z e. y /\ y e. suc A ) ). ->. z e. suc A ).

	  Upon mmj2 unification with fd.txt and set.mm loaded this subproof
	  becomes

       8:7:int3              |- (. (. Tr A ,. ( z e. y /\ y e. suc A ) ). ->. ( y e. A -> z e. suc A ) ).
       ...
       ...
       12:11:int2            |- (.            ( z e. y /\ y e. suc A )    ->. ( y = A -> z e. suc A ) ).
       ...
       14:13:ff1             |- (.            ( z e. y /\ y e. suc A )    ->. ( y e. A \/ y = A ) ).
       15:8,12,14:ff3h0      |- (. (. Tr A ,. ( z e. y /\ y e. suc A ) ). ->. z e. suc A ).

	 The subproof translated into conventional notation after the
	 substitutelabels() is called is

       8:7:                  |- (  (  Tr A /\ ( z e. y /\ y e. suc A ) )  ->  ( y e. A -> z e. suc A ) )
       ...
       ...
       12:11:                |- (             ( z e. y /\ y e. suc A )    ->  ( y = A -> z e. suc A ) )
       ...
       14:13:syl             |- (             ( z e. y /\ y e. suc A )    ->  ( y e. A \/ y = A ) )
       15:14,12,8:ffeel3h0   |- (  (  Tr A /\ ( z e. y /\ y e. suc A ) )  ->  z e. suc A )

	 The subproof after another mmj2 unification is

       8:7:3expia            |- ( ( Tr A /\ ( z e. y /\ y e. suc A ) ) -> ( y e. A -> z e. suc A ) )
       ...
	   ...
       12:11:ex              |- (            ( z e. y /\ y e. suc A )    -> ( y = A -> z e. suc A ) )
       ...
       14:13,d2:syl          |- (            ( z e. y /\ y e. suc A )    -> ( y e. A \/ y = A ) )
       d3::                  |- ( ( Tr A /\ ( z e. y /\ y e. suc A ) ) -> ( y = A -> z e. suc A ) )
       d4::                  |- ( ( Tr A /\ ( z e. y /\ y e. suc A ) ) -> ( y e. A \/ y = A ) )
       d5::                  |- ( &W5 -> ( y e. A -> z e. suc A ) )
       d6::                  |- ( ( ( y = A -> z e. suc A ) /\ ( y e. A \/ y = A ) /\ ( y e. A -> z e. suc A ) ) -> z e. suc A )
       15:12,d3,14,d4,d5,8,d6:ffeel3h0 |- ( ( Tr A /\ ( z e. y /\ y e. suc A ) ) -> z e. suc A )

	 Steps d3,d4, and d5 correspond, respectively, to the original hypothesis
	 steps 12,14, and 8. Because the virtual hypothesis collection of step 8 is
	 equal to that of the assertion step, the generation of an intermediate
	 hypothesis step is not required. Step d5 is not needed. The existence of
	 a work variable in d5 implies that that step is not needed.
	 completeusersproof deletes each generated step in which at least one work
	 variable occurs. completeusersproof deletes step d5.

	 The subproof after the hypothesis fields of the hypothesis steps have been edited is

       8:7:3expia            |- ( ( Tr A /\ ( z e. y /\ y e. suc A ) ) -> ( y e. A -> z e. suc A ) )
       ...
	   ...
       12:11:ex              |- (            ( z e. y /\ y e. suc A )    -> ( y = A -> z e. suc A ) )
       ...
       14:13,d2:syl          |- (            ( z e. y /\ y e. suc A )    -> ( y e. A \/ y = A ) )
       d3:12:                |- ( ( Tr A /\ ( z e. y /\ y e. suc A ) ) -> ( y = A -> z e. suc A ) )
       d4:14:                |- ( ( Tr A /\ ( z e. y /\ y e. suc A ) ) -> ( y e. A \/ y = A ) )
       d5::                  |- ( &W5 -> ( y e. A -> z e. suc A ) )
       d6::                  |- ( ( ( y = A -> z e. suc A ) /\ ( y e. A \/ y = A ) /\ ( y e. A -> z e. suc A ) ) -> z e. suc A )
       15:12,d3,14,d4,d5,8,d6:ffeel3h0 |- ( ( Tr A /\ ( z e. y /\ y e. suc A ) ) -> z e. suc A )

     Step d3 is deducible from step 12 and step d4 is deducible from
	 step 14. Step d6 is the unification theorem. The subproof after the
	 hypothesis field of the assertion step has been edited, step d5 has
	 been eliminated, all existing labels have been removed, and after mmj2
	 unifying again is

	   8:7:3expia            |- ( ( Tr A /\ ( z e. y /\ y e. suc A ) ) -> ( y e. A -> z e. suc A ) )
	   ...
	   ...
       12:11:ex              |- (            ( z e. y /\ y e. suc A )    -> ( y = A -> z e. suc A ) )
	   ...
       14:13,d2:syl          |- (            ( z e. y /\ y e. suc A )    -> ( y e. A \/ y = A ) )
       d3:12:adant1          |- ( ( Tr A /\ ( z e. y /\ y e. suc A ) ) -> ( y = A -> z e. suc A ) )
       d4:14:adant1          |- ( ( Tr A /\ ( z e. y /\ y e. suc A ) ) -> ( y e. A \/ y = A ) )
       d6::                  |- ( ( ( y = A -> z e. suc A ) /\ ( y e. A \/ y = A ) /\ ( y e. A -> z e. suc A ) ) -> z e. suc A )
       15:d3,d4,8,d6:syl3anc |- ( ( Tr A /\ ( z e. y /\ y e. suc A ) ) -> z e. suc A )

     d6, the unification theorem, does not unify with a theorem in set.mm, but
	 is deduced from jao by 3imp231. After the stepprover() function does
	 this the completed subproof is

	   8:7:3expia            |- ( ( Tr A /\ ( z e. y /\ y e. suc A ) ) -> ( y e. A -> z e. suc A ) )
	   ...
	   ...
       12:11:ex              |- (            ( z e. y /\ y e. suc A )    -> ( y = A -> z e. suc A ) )
	   ...
       14:13,d2:syl          |- (            ( z e. y /\ y e. suc A )    -> ( y e. A \/ y = A ) )
       d3:12:adant1          |- ( ( Tr A /\ ( z e. y /\ y e. suc A ) ) -> ( y = A -> z e. suc A ) )
       d4:14:adant1          |- ( ( Tr A /\ ( z e. y /\ y e. suc A ) ) -> ( y e. A \/ y = A ) )
       d9::jao               |- ( ( y e. A -> z e. suc A ) -> ( ( y = A -> z e. suc A ) -> ( ( y e. A \/ y = A ) -> z e. suc A ) ) )
       d6:d9:3imp231         |- ( ( ( y = A -> z e. suc A ) /\ ( y e. A \/ y = A ) /\ ( y e. A -> z e. suc A ) ) -> z e. suc A )
       15:d3,d4,8,d6:syl3anc |- ( ( Tr A /\ ( z e. y /\ y e. suc A ) ) -> z e. suc A )

     Note that syl3anc is the name of the deduction in set.mm which whould have
	 been named eel111 if the eel label naming convention was used. As with the
	 unification theorem means for labels of type eel and not of type ffeel,
	 the unification theorem means for labels of type eel and type ffeel
	 generates a unification theorem. Unlike the unification theorem means for
	 labels not of type ffeel, for each hypothesis step without a virtual
	 hypothesis collection equal to the virtual hypothesis collection of the
	 assertion step, the unfication theorem means generates an intermediate
	 hypothesis step having a virtual hypothesis collection equal to that of
	 the assertion's virtual hypothesis collection.  The virtual conclusion is
	 the same as that of the corresponding original hypothesis step. Or, if the
	 original hypothesis step has no virtual hypotheses, the virtual conclusion
	 of the generated intermediate hypothesis step is equal to the wff of the
	 original hypothesis step.

	 Each hypothesis step of the assertion's deduction has a virtual hypothesis
	 collection equal to the assertion's virtual hypothesis collection. It is a
	 single metavariable deduction. The deduction of the assertion step of
	 every n-hypothesis User's Subproof processed by the unification theorem
	 means for labels of type ffeel unfies with the unique label eel11...1,
	 where there are n "1"s. This single eel11...1 label applies for all
	 permutations of original hypothesis step virtual hypothesis collections.
	 There are many such permutations because, generally, the virtual
	 hypothesis collection of an original hypothesis step may be any virtual
	 hypothesis collection which is a subcollection of the assertion's virtual
	 hypothesis collection. By using eel labels of type ffeel it becomes
	 unnecessary for the many possible permutations of specialized eel
	 deductions to exist in set.mm. Those specialized eel deductions which are
	 included in set.mm are included because they are frequently used. The
	 ground for their existence in set.mm is to shorten frequently occurring
	 subproofs completed by the unification theorem means.

	 A Virtual Deduction User's Proof may have steps with many-element virtual
     hypothesis collections. Because conventional notation virtual deductions
     may not have more than 3-conjunct conjunctions, completeusersproof
     translates a more-than-3-element virtual hypothesis collection into a
     left-nested conjunction. For example, the conventional notation
     counterpart of the Virtual Deduction virtual hypothesis collection

       (. <wff1> ,. <wff2> ,. <wff3> ,. <wff4> ,. <wff5> ).

     is

       ( ( ( ( <wff1> /\ <wff2> ) /\ <wff3> ) /\ <wff4> ) /\ <wff5> )

     The unification theorem means using type ffeel labels is especially useful
     for Users' Proofs containing steps with virtual hypothesis collections
     having more than 3 elements because, otherwise, it would be necessary to
     have a large number of specialized eel permutations. Additional adant*
     deductions are required, but their number is much smaller than the number
     of specialized eel permutations that would otherwise be required.

	 If the value of argv1 is not specified by the User or the User presses the
	 "Completeusersproof" button in the completeusersproofGUI, then
	 completeusersproof completes each subproof of the input User's Proof
	 completable by the mmj2 unification means by that means. It attempts to
	 complete the remaining subproofs by the unification theorem means. All
	 subproofs completable by the unification theorem means are completed by
	 that means. If all subproofs are completed, the proof is completed and a
	 RPN proof is generated.

	 If argv1 is specified by the User to have the value
	 "runCompleteusersproofsmv", then completeusersproof completes each
	 subproof competable by the mmj2 unification means by that means. It
	 completes the remaining subproofs which are completable by the single
	 metavariable deduction unification means by that means. It attempts to
	 complete the remaining subproofs by the unification theorem means. Each
	 remaining subproof completable by the unification theorem means is
	 completed by that means. If all subproofs are completed, a RPN proof is
	 generated. Otherwise, each remaining subproof not a single step has a
	 generated unification theorem, but is not completed because the unification
	 theorem does not unify with a theorem in set.mm and is not deducible by a
	 2-step proof from a theorem in set.mm which is a semantic variation of the
	 unification theorem. Each remaining single step subproof, a theorem, does
	 not unify with a theorem in set.mm and is not deducible by a 2-step proof
	 from a theorem in set.mm which is a semantic variation of the theorem of
	 the subproof.

	 Earlier, we described the metavariable deduction unification means. By this
	 means, a subproof unifies with a standard form metavariable deduction in
	 set.mm. Here we describe the single metavariable deduction unification
	 means, which is distinct from the metavariable deduction unification means.
	 If a subproof of the original User's Proof, without modification, unifies
	 with a single metavariable deduction in set.mm, then it is completable by
	 the mmj2 unification means, as explained above. The virtual hypothesis
	 collection of each hypothesis step of the original subproof is identical
	 to the virtual hypothesis collection of the assertion. When some of the
	 original hypothesis steps have a virtual hypothesis collection which is a
	 proper subcollection of the virtual hypothesis collection of the assertion,
	 for each such hypothesis step, in implementing the single metavariable
	 unification deduction means, completeusersproof automatically generates
	 a corresponding hypothesis step which has the same virtual conclusion as
	 the original hypothesis step and whose virtual hypothesis collection is
	 identical to the virtual hypothesis collection of the assertion step.
	 Each generated hypothesis step is deducible from its original hypothesis
	 step by a 1-hypothesis deduction in set.mm. After these additional
	 hypothesis steps are generated the subproof may then unify with a a single
	 metavariable deduction in set.mm, completing the subproof. We illustrate
	 the completion of a subproof by the single metavariable deduction
	 unification means with an example. Again, we use the subproof of
	 suctrALT4UP whose assertion step is step 15. This subproof happens to be
	 completable by both the unification theorem means and the single
	 metavariable deduction unification means. The subproof of the original
	 User's Proof is

	   8:7:             |- (. (. Tr A ,. ( z e. y /\ y e. suc A ) ). ->. ( y e. A -> z e. suc A ) ).
       ...
       ...
       12:11:           |- (.            ( z e. y /\ y e. suc A )    ->. ( y = A -> z e. suc A ) ).
       ...
       14:13:           |- (.            ( z e. y /\ y e. suc A )    ->. ( y e. A \/ y = A ) ).
       15:8,12,14:      |- (. (. Tr A ,. ( z e. y /\ y e. suc A ) ). ->. z e. suc A ).

	 The subproof translated into conventional notation is

       8:7:             |- (  (  Tr A /\ ( z e. y /\ y e. suc A ) )  ->  ( y e. A -> z e. suc A ) )
       ...
       ...
       12:11:           |- (             ( z e. y /\ y e. suc A )    ->  ( y = A -> z e. suc A ) )
       ...
       14:13:           |- (             ( z e. y /\ y e. suc A )    ->  ( y e. A \/ y = A ) )
       15:14,12,8:      |- (  (  Tr A /\ ( z e. y /\ y e. suc A ) )  ->  z e. suc A )

	 The virtual hypothesis collection of step 8 (in conventional notation),
	 (  Tr A /\ ( z e. y /\ y e. suc A ) ), is identical to the virtual
	 hypothesis collection of the assertion step, step 15. It is in single
	 metavariable deduction form. An additional step is not required. The
	 virtual hypothesis collection for each of steps 12 and 14 is not
	 identical to the assertion's virtual hypothesis collection. For step
	 12, step d3 is generated. Step d3 is deducible from step 12 and its
	 virtual hypothesis collection is identical to the assertion's
	 virtual hypothesis collection. Similarly, step 14's virtual
	 hypothesis collection is not identical to step 15's, so step d4 is
	 generated. We say that, with these steps added, the subproof will complete
	 if there exists a single metavariable deduction in set.mm which unifies
	 with it. In saying this, it is implicitly assumed that a deduction exists
	 in set.mm which unifies with the deduction whose assertion is step d3 and
	 that a deduction exists in set.mm which unifies with the deduction whose
	 assertion is step d4. More precisely stated, the subproof will complete if
	 there exists a single metavariable deduction in set.mm which unifies with
	 the deduction whose assertion is step 15 and whose hypotheses are steps 8,
	 d3, and d4. In addition to generating the steps d3 and d4,
	 completeusersproof also fills their hypothesis fields and edits the
	 hypothesis field of the assertion step, step 15, as required. The subproof
	 becomes

	   8:7:              |- (  (  Tr A /\ ( z e. y /\ y e. suc A ) )  ->  ( y e. A -> z e. suc A ) )
       ...
	   ...
       12:11:            |- (             ( z e. y /\ y e. suc A )    ->  ( y = A -> z e. suc A ) )
       ...
       14:13:            |- (             ( z e. y /\ y e. suc A )    ->  ( y e. A \/ y = A ) )
       d3:12:            |- (  (  Tr A /\ ( z e. y /\ y e. suc A ) )  ->  ( y = A -> z e. suc A ) )
       d4:14:            |- (  (  Tr A /\ ( z e. y /\ y e. suc A ) )  ->  ( y e. A \/ y = A ) )
       15:d3,d4,8:       |- (  (  Tr A /\ ( z e. y /\ y e. suc A ) )  ->  z e. suc A )

	 In fact, there does exist a single metavariable deduction in set.mm which
	 unifies with the deduction whose assertion is step 15, there exists a
	 deduction in set.mm which unifies with the deduction whose assertion is
	 step d4, and there exists a deduction in set.mm which unifies with the
	 deduction whose assertion is step d3. Upon mmj2 unification the subproof
	 completes. The completed subproof is

	   8:7:              |- (  (  Tr A /\ ( z e. y /\ y e. suc A ) )  ->  ( y e. A -> z e. suc A ) )
       ...
        ...
       12:11:            |- (             ( z e. y /\ y e. suc A )    ->  ( y = A -> z e. suc A ) )
       ...
       14:13:            |- (             ( z e. y /\ y e. suc A )    ->  ( y e. A \/ y = A ) )
       d3:12:adantl      |- (  (  Tr A /\ ( z e. y /\ y e. suc A ) )  ->  ( y = A -> z e. suc A ) )
       d4:14:adantl      |- (  (  Tr A /\ ( z e. y /\ y e. suc A ) )  ->  ( y e. A \/ y = A ) )
       15:8,d3,d4:mpjaod |- (  (  Tr A /\ ( z e. y /\ y e. suc A ) )  ->  z e. suc A )

     mpjaod is the single metavariable deduction in set.mm that unfies with the
	 subproof.

	 Where the assertion of a subproof of a User's Proof has no virtual
	 hypotheses that assertion should not have a virtual hypothesis collection
	 because there is no need for it. Similarly, the hypotheses of the subproof
	 have no virtual hypotheses and should not be in standard T form. An
	 example of such a subproof is the subproof whose assertion is step 43 of
	 the User's Proof a9e2ndeqALTUP, contained in VirtualDeductionProofs.txt.
	 It is

       39::                   |- ( A. x x = y -> ( u = v -> E. x E. y ( x = u /\ y = v ) ) )
       ...
       41:40:                 |- ( -. A. x x = y -> ( u = v -> E. x E. y ( x = u /\ y = v ) ) )
       42::                   |- ( A. x x = y \/ -. A. x x = y )
       43:39,41,42:           |- ( u = v -> E. x E. y ( x = u /\ y = v ) )

     It is completable by the single metavariable deduction unification means.
	 In order to unify with a single metavariable deduction in set.mm, step d13
	 is generated. It is the original assertion step in standard T form. For
	 each hypothesis step a step deducible from it with the same empty virtual
	 hypothesis collection is generated. The contents of the hypothesis field
	 for each of these generated steps is automatically generated by
	 completeusersproof. Below is the completed subproof. The modifications to
	 the original subproof are similar to the subproof of the last example,
	 except there is an extra generated step, a step from which the original
	 assertion step is deduced by the set.mm deduction trud. This extra step is
	 needed to complete a subproof by the single metavariable deduction
	 unification means only when the assertion has no virtual hypotheses and no
	 empty virtual hypothesis collection.

       39::                   |- ( A. x x = y -> ( u = v -> E. x E. y ( x = u /\ y = v ) ) )
       ...
       41:40:                 |- ( -. A. x x = y -> ( u = v -> E. x E. y ( x = u /\ y = v ) ) )
       42::                   |- ( A. x x = y \/ -. A. x x = y )
       d10:41:a1i             |- ( T. -> ( -. A. x x = y -> ( u = v -> E. x E. y ( x = u /\ y = v ) ) ) )
       d11:39:a1i             |- ( T. -> ( A. x x = y -> ( u = v -> E. x E. y ( x = u /\ y = v ) ) ) )
       d12:42:a1i             |- ( T. -> ( A. x x = y \/ -. A. x x = y ) )
       d13:d11,d10,d12:mpjaod |- ( T. -> ( u = v -> E. x E. y ( x = u /\ y = v ) ) )
       43:d13:trud            |- ( u = v -> E. x E. y ( x = u /\ y = v ) )

     */

  void addMvSubtheoremTs(char* proofWorksheet, char* mapFfToUun);
  void completeusersproofmv(char* proofWorksheet, char* mapFfToUun);
  void removeUnneededFfLabels(char* proofWorksheet, char* proofWorksheet0);
  void addMvAssertionSteps(char* proofWorksheet, char* proofWorksheetmv);
  void mmj2Unify(char* proofWorksheet, char* mmj2BatFile);
  void translate(char* proofWorksheet);
  void tagLabeledSteps(char* proofWorksheet, char* tag);
  void endTag(char* iofile, char* endstr, char* matchstring);
  void tag(char* iofile, char* begstr, char* endstr, char* startmatch,
           char* sn, char* endmatch, char* en);
  void match(char* iofile, char* matchstr, char* YorN);
  void substituteLabels(char* proofWorksheet, char* labelMapping, int c);
  void add(char* iofile, char* begstr, char* endstr);
  void addMTMF(char* iofile, char* begstr, char* endstr, char* matchstringT,
               char* matchstringF);
  void substitute(char* iofile, char* oldstring, char* newstring,
                  char* occurrence, char* matchstring);
  void substituteMTMT(char* iofile, char* oldstring, char* newstring,
                      char* occurrence, char* matchstring1, char* matchstring2);
  void delete(char* iofile, char* startstr, char* endstr);
  void copy(char* inpfiles, char* outfile);
  void parallel(char* inpfile1, char* inpfile2, char* outfile, char* btwnstr);
  void reverse(char* iofile);
  void editHypFieldOfHypSteps(char* proofWorksheet, char* labeltype);
  void editHypFieldOfTAssertions(char* proofWorksheet, char* labeltype);
  void editHypFieldOfAssertions(char* proofWorksheet, char* labeltype);
  void deleteStepsWithWorkVariables(char* proofWorksheet);
  void deleteLabels(char* iofile, char* matchstring);
  void completeTheoremSteps(char* proofWorksheet);
  void stepprover(char* proofWorksheet, char* labels);
  void nameUsersProofFile(char* proofWorksheet);
  void removeATypeOfLabel(char* proofWorksheet, char* colonlabeltype);
  /* Declare functions used by completeusersproof(). */

  copy(proofWorksheet, "proofWorksheet0.txt");
  /* Make a copy of proofWorksheet for use by removeUneededFfLabels().
     If addMvSubtheorems() is executed, this copy, proofWorksheet.txt, will be
	 overwritten. */

  if ( strcmp(argv1,"runCompleteusersproofmv") == 0 ) addMvSubtheoremTs(proofWorksheet, "mapFfToUun.txt");
  /* Make each step of the User's Proof (proofWorksheet) with no virtual
     hypothesis collection which is a step of an mv-completable subproof into a
	 virtual deduction step with an empty virtual hypothesis collection.

	 The addMvSubtheoremTs() and addMvAssertionSteps() are not called unless
	 the value of argv1 is "runCompleteusersproofmv". If addMvSubtheoremTs()
	 and addMvAssertionSteps() are called, then each subproof not completable
	 by the mmj2 unification means is attempted to be completed by the
	 metavariable deduction unification means. Each subproof completable by
	 this means is completed by this means for the final proof. The remaining
	 subproofs, not completable by the mmj2 unificiation means or the
	 metavariable deduction unification means, is later attempted to be
     completed by the unification theorem means. */

  if ( strcmp(argv2,"onlyShortScripts") == 0 || strcmp(argv2,"scripts") == 0 ) mmj2Unify(proofWorksheet, "unifyFF.exe");
  else mmj2Unify(proofWorksheet, "mmj2FalseDeductions.bat");
  /* mmj2-unify the input proofWorksheet to obtain false deduction unifications
     False deductions are identified by their ff* labels. It is intended that
	 each subproof of the Proof Worksheet either unifies with a deduction in
	 set.mm or unifies with a false deduction. A false deduction has an
	 implicit unification theorem hypothesis which is made explicit
	 automatically, making the deduction true. A unification theorem is a step
	 to be automatically generated by completeusersproof(). If a subproof of
	 the Proof Worksheet does not unify with a true deduction, then it will
	 unify with a false deduction. Each false deduction one-one corresponds to
	 a true deduction in conventional notation (an eel* deduction) and all
	 permutations of another true deduction which deduce a unionized assertion
	 from a non-unionized assertion (uun* deductions).

	 The second completeusersproof command line argument, argv2, may be
	 assigned the value "onlyShortScripts", "scripts", or the default value,
	 which is any character string other than "onlyShortScripts" or "scripts".
	 By convention, """" may be used as any value other than "onlyShortScripts"
	 or "scripts". If the value is "onlyShortScripts", then the mmj2
	 unification will be by using the AutoHotkey script unify.exe or
	 unifyFF.exe. If the value is "scripts", then mmj2 unification is by using
	 unify.exe, unifylong.exe, or unifyFF.exe. If the value is anything other
	 than "onlyShortScripts" or "scripts", then mmj2 unification is by the
	 mmj2 batch test run parm. */

  removeATypeOfLabel(proofWorksheet, ":ffunifthm");
  /* Any theorem step of the original User's Proof may get a type
     ffunifthm label with the last mmj2Unify() application, which occurred with
	 execution of the last statement. Best to remove them immediately to avoid
	 any harm they may cause after being replaced by type ffutperm labels. */

  substitute(proofWorksheet, "$", "@!", "ALL", "* ");
  tag(proofWorksheet, "", "@@@@@@", "$= ", "1", " $.", "1");
  match(proofWorksheet, "@@@@@@", "N");
  match(proofWorksheet, "$d ", "N");
  substitute(proofWorksheet, "@!", "$", "ALL", "* ");
  match(proofWorksheet, "I-PA-0119", "N");
  /* Delete any RPN proof which may be generated by the mmj2 unification.
     Usually a false RPN proof will be generated due to the existence of the
	 false deductions in fd.txt. After any "$" which may occur in comment lines
	 (in which the matchstring "* " occurs) are temporarily changed to "@!",
	 "$= " uniquely occurs in the Proof Worksheet in the first line of an RPN
	 proof. " $." uniquely occurs in the Proof Worksheet in the last line of an
	 RPN proof. After any "$" which may occur in comment lines are temporarily
	 changed to "@!", the "$d " matchstring uniquely identifies any distinct
	 variable requirements line accompanying an RPN proof. Delete any such
	 lines. Afterwards, restore any "@!" to "$" in comment lines.

	 The deletion of any RPN proof is performed outside of mmj2Unify() because
	 any RPN proof generated by the last call of mmj2Unify is the primary
	 product of completeusersproof.c and should not be deleted. */

  removeUnneededFfLabels(proofWorksheet, "proofWorksheet0.txt");
  /* removeUnneededFfLabels() deletes the ff* label from each assertion step of
     proofWorksheet if its subproof is completable by the mmj2 unification
	 means.

	 The value of the argument proofWorksheet0 immediately before
	 removeUnneededFfLabels() is executed is the original unprocessed User's
	 Proof. The value of the argument proofWorksheet immediately before
	 removeUnneededFfLabels() is executed is the original User's Proof after
	 mmj2 unification with fd.txt and set.mm files loaded. It contains
	 subproofs completed by unification with ff* deductions and subproofs
	 completed by unification with theorems and deductions in set.mm. It is a
	 virtual deduction proof with VD notation. Any RPN proof and generated
	 messages are removed. */

  if ( strcmp(argv1,"runCompleteusersproofmv") == 0 ) addMvAssertionSteps(proofWorksheet, "proofWorksheetmv.txt");
  /* Replace with the unionized and non-unionized assertion steps of subproofs
     completed by the metavariable deduction unification means (completed by
	 completeusersrproofmv()) with their corresponding steps in proofWorksheet.

	 The addMvSubtheoremTs() and addMvAssertionSteps() are not called unless
	 the value of argv1 is "runCompleteusersproofmv". */

  translate(proofWorksheet);
  /* Translate each proof step, a Virtual Deduction notation virtual deduction,
     into a conventional notation virtual deduction.

	 Note that the hypothesis field of the assertion of each eel* deduction or
	 uun* deduction does not contain a question mark. No question mark is
	 necessary. mmj2 adds a d<i> step to the hypothesis field even without the
	 question mark.

	 We wish to translate from VD notation to conventional notation as soon as
	 possible, which is as soon as the User's Proof has been mmj2-unified. The
	 sole purpose of the virtual deduction notation is allow for the automatic
	 generation of ff* (type ff) assertion step labels. These labels contain
	 the information completeusersproof needs to attempt to complete subproofs
	 by either the unification theorem means, the metavariable deduction
	 unification means, or the single metavariable deduction unification means.
	 The labels of type ff map into labels of type ffeel, eel, ffsmv, ffTmv, or
	 type uun. This mapping is defined by the file
	 mapFfToEelUunNumPermFfsmv.txt . */

  copy(proofWorksheet, "proofWorksheetTrFfLabeled.txt");
  /* At this point proofWorksheet is the translated User's Proof with the
     assertion of each non-zero hypothesis subproof which does not complete by
	 the mmj2 unification means labeled with a label of type ff. All other
	 steps are unlabeled. It is saved as proofWorksheetTrFfLabeled.txt because,
	 with argv1 == "runCompleteusersproofsmv", when it later becomes known which
	 subproofs are completable by the single metavariable deduction unification
	 means, it will be needed. Better to save and reuse this result than to
	 re-process the User's Proof from the beginning. */

  copy(proofWorksheet, "tags.txt");
  match("tags.txt", "", "N");
  /* Each line of tags.txt is either blank or is a sequence of tags
     corresponding to means by which the subproof of the assertion on that line
	 is completable. The line number of each assertion of proofWorksheet whose
	 subproof is to be processed by the single metavariable deduction
	 unification means is a "!!!ffsmv" line of tags.txt. completeusersproof()
	 will "!!!ffsmv"-end-tag each such assertion of proofWorksheet using
	 parallel(proofWorksheet, "tags.txt", proofWorksheet, "");. The instant two
	 statements create tags.txt and initialize it to the empty text file. For
	 the initialized file, attempts to complete each subproof are by either the
	 mmj2 unification means, the unificiation theorem means, or by stepprover().
	 No subproofs of proofWorksheet will be processed by the single
	 metavariable deduction unification means. tags.txt will remain empty for
	 the default value of argv1. It will be edited for
	 argv1 == "runCompleteusersproofsmv". It is not used for
	 argv1 == "runStepprover" and argv1 == "runCompleteusersproofmv". */

  vstring labelType = (vstring)malloc(100);
  vstring labelTypeTag = (vstring)malloc(100);
  vstring colonLabelType = (vstring)malloc(100);
  let(&labelType,"");
  /* Because labelType is an argument for cat() calls below it must of type
     vstring. Otherwise values are incorrectly or not assigned to it.
	 labelTypeTag and colonLabelType, being derived from labelType, must also
	 be of type vstring. Assign to these vstring variables must be made using
	 let(). Memory must deallocated for these variable after they are no
	 longer used. These variables may be used as arguments of functions even
	 if the function definition designates the arguments to be of type
	 char*.

	 As is done here, initializing a vstring variable to "" using let is
	 allowed even though the same assignment is used for memory
	 deallocation. */

  int k;
  int n = 0;
  /* The default value of n is 0. */

  if ( strcmp(argv1,"runCompleteusersproofsmv") == 0 ) n = 1;
  /* If the value of argv1 is "runCompleteusersproofsmv", then the loop
     starting with the next statement iterates once. This iteration identifies
	 each assertion whose subproof is completable by the single metavariable
	 deduction unification means and is not completable by the mmj2 unification
	 means. tags.txt is edited to add the "!!!ffsmv" tag to each such
	 assertion. */

  for( k = 1 ; k <= n ; k = k + 1 )

  {

      copy("proofWorksheetTrFfLabeled.txt", proofWorksheet);
	  /* In general, if k > 1 proofWorksheet and proofWorksheetTrFfLabeled.txt
	     are distinct. Re-initialize proofWorksheet to
		 proofWorksheetTrFfLabeled.txt. This statement has no efffect for
		 k == 1. */

	  if( k == 1 ) let(&labelType,"ffsmv");
      /* We extend the notation of "type" to labels. The ffsmv* label ffsmv12
	     is of type ffsmv. We also say that if labelTypeTag = "!!!ffsmv", then
		 labelTypeTag is of type ffsmv. labelTypeTag is the contatenation of
		 "!!!" and labelType. labelTypeTag !!!ffsmv corresponds to the single
		 metavariable deduction unification means, which may also be called the
		 ffsmv means.

		 ffTmv is a subtype of ffsmv. Being a subtype of ffsmv, a label of type
		 ffTmv is also of type ffsmv. A subproof whose assertion has a label of
		 type ffTmv and type ffsmv is attempted to be completed by the means of
		 the same type, which is the type ffsmv means (the single metavariable
		 deduction unification means). Note the each label in the ffsmv column
		 of mapFfToEelUunNumPermFfsmv.txt is a type ffsmv label. Some labels in
		 this column are of type ffsmv and type ffTmv. Those labels include the
		 character string "ffTmv" and do not include the character strong
		 "ffsmv". Labels of type ffsmv and not ffTmv include the character
		 string "ffsmv" and do not include the character string "ffTmv".

		 If the labeltype argument of the function editHypFieldOfHypSteps(),
		 editHypFieldOfTAssertions(), or editHypFieldOfAssertions() has the
		 value "ffTmv", then the passed label is of type ffTmv. If labeltype
		 has the value "ffsmv", then the passed label is of type ffsmv and not
		 of type ffTmv.

		 We also say that the instant statement implicitly defines iteration 1
		 of the present loop to correspond to the ffsmv means. Iteration 1 will
		 relabel each ff*-labeled assertion step whose subproof is not
		 completable by the mmj2 unification means with a type ffsmv label.
		 Labels also of type ffTmv will include the character string "ffTmv"
		 and not include the character string "ffsmv" . Labels not of type
		 ffTmv will include the character string "ffsmv" and not include the
		 character string "ffTmv". */

	  let(&labelTypeTag,cat("!!!", labelType, NULL));
	  let(&colonLabelType,cat(":", labelType, NULL));

	  tagLabeledSteps(proofWorksheet, labelTypeTag);
	  /* In order to attempt to complete its subproof by the means of the type
	     of labelTypeTag corresponding to k == 1, substituteLabels() will
		 re-label each assertion step labeled with an ff* label with a label
		 corresponding to the type of labelTypeTag. For
		 labelTypeTag == !!!ffsmv , the substituted label is either of type
		 ffsmv and not of type ffTmv, or is of type ffsmv and of type ffTmv. */

	  copy(proofWorksheet, "tags.txt");
	  addMTMF("tags.txt", "", "@@@", "", "!!!");
      substitute("tags.txt", "!!!", "@@@!!!", "1", "");
	  delete("tags.txt", "", "@@@");
	  /* After these statements are executed, tags.txt contains the
	     labelTypeTag end tag for each assertion step of proofWorksheet whose
		 subproof is not a 0-hypothesis subproof and not completable by the
		 mmj2 unification means. */

      substituteLabels(proofWorksheet, "mapFfToEelUunNumPermFfsmv.txt", 125);
      /* Replace the label of each step of proofWorksheet having a ff* false
         deduction label with its corresponding eel* label or(excl.), if it is
		 end-tagged, replace it with the type labelType label corresponding to
		 the step's particular ff* label.

	     proofWorksheet is the translated User's Proof with the assertion of
		 each non-zero hypothesis subproof which does not unify by the mmj2
		 unification means with a deduction in set.mm labeled with an ff* label.
         All other steps are unlabeled. */

	  substitute(proofWorksheet, labelTypeTag, "", "1", "");
	  /* Remove the end tags from proofWorksheet. */


      if ( strcmp(argv2,"onlyShortScripts") == 0 || strcmp(argv2,"scripts") == 0 ) mmj2Unify(proofWorksheet, "unifyFF.exe");
      else mmj2Unify(proofWorksheet, "mmj2FalseDeductions.bat");
      /* mmj2-unify proofWorksheet. Immediately before this statement is
         executed each assertion step whose subproof has at least one
		 hypothesis step and is not completable by the mmj2 unfication means
		 is labeled with either a label of type eel and not of type ffeel, of
		 type eel and type ffeel, of type ffsmv and not of type ffTmv, or of
		 type ffsmv and type ffTmv. Upon execution of this statement, these
		 labels unify with their corresponding deductions in set.mm and one or
		 more additional steps are generated for each subproof having a labeled
		 assertion step.

		 This mmj2 unification may generate type ffunifthm labels, but nothing
		 need be done to remove any type ff labels because prior to the next
		 mmj2Unify() call deleteLabels() is called, which removes all labels in
		 proofWorksheet. */

     editHypFieldOfTAssertions(proofWorksheet, "ffTmv");
	 editHypFieldOfHypSteps(proofWorksheet, "ffsmv");
	 editHypFieldOfHypSteps(proofWorksheet, "ffTmv");
	 editHypFieldOfAssertions(proofWorksheet, "ffsmv");
	 editHypFieldOfAssertions(proofWorksheet, "ffTmv");
	 /* Edit all hypothesis fields requiring editing of each subproof with an
	    assertion step labeled with a label of type ffsmv. */

     deleteStepsWithWorkVariables(proofWorksheet);
     /* Delete each d<i> step with an empty hypothesis field and a wwf in which
        at least one work variable occurs. */

     deleteLabels(proofWorksheet, "");
	 /* Now that some hypothesis fields have been edited, some of the existing
	    labels have become incorrect. Delete the label of each labeled step of
	    proofWorksheet. With this statement, all labels, including incorrect
		labels, have been purged from proofWorksheet. */

     if ( strcmp(argv2,"onlyShortScripts") == 0 || strcmp(argv2,"scripts") == 0 ) mmj2Unify(proofWorksheet, "unify.exe");
     else mmj2Unify(proofWorksheet, "mmj2StepProver.bat");
     /* mmj2-unify proofWorksheet. All subproofs completable by the mmj2
        unification means are completed. An attempt is made to complete all
	    remaining subproofs by the single metavariable deduction unification
	    means. All subproofs completable by this means are completed.

	    This mmj2Unify() call can generate no type ff labels because fd.txt
	    is not loaded. Therefore, there is no need to remove any type ff labels
	    prior to the next mmj2Unify() call. */

	 reverse(proofWorksheet);
	 substitute(proofWorksheet, "\\n", "$$$", "1", ":trud ");
	 reverse(proofWorksheet);
	 /* Join each assertion step of type ffTmv with its corresponding assertion
	    step in standard T form. For each joined pair, the assertion step in
	    standard T form is on the right side of the "$$$" joint. */

     add(proofWorksheet, "@@@", "");
	 match(proofWorksheet, "@@@d", "N");
	 substitute(proofWorksheet, "@@@", "", "1", "");
	 /* Remove the steps of the proof which were generated by
	    completeusersproof. The remaining steps are the steps of the original
		User's Proof with the hypothesis field of some steps altered. The
	    resulting Proof Worksheet is not valid because steps are missing.
	    Note that the joined steps are remaining steps with the original proof
	    step on the left side of the joint. */

     delete(proofWorksheet, "", "$$$");
	 /* Remove the left side of the joint and the joint itself from each joined
        step. */

	 parallel(proofWorksheet, "tags.txt", proofWorksheet, "");
	 substitute(proofWorksheet, ":", "%:%", "2", "!!!");
	 substitute(proofWorksheet, "!!!", "$$$", "1", "%:% ");
	 substitute(proofWorksheet, "!!!", "^^^!!!", "1", "");
	 addMTMF(proofWorksheet, "", "^^^", "", "!!!");
	 delete(proofWorksheet, "", "^^^");
	 copy(proofWorksheet, "tags.txt");
	 /* After these statements have been executed tags.txt contains the
	    labelTypeTag end tag for each line which is an assertion of the User's
	    Proof whose subproof is completable by the labelType means. All other
	    lines are blank.

	    This is the last statement of the loop. The instant version of
	    completeusersproof iterates this loop a single time for
	    argv1 == "runCompleteusersproofsmv" and zero times otherwise. The
	    purpose of this single iteration is to identify each subproof
	    completable by the ffmv means (the single metavariable deduction
	    unification means) and not completable by the mmj2 unification means.
	    It identifies each of these subproofs by a "!!!ffsmv" tag in each line
	    number of tags.txt corresponding to the same line number of
		proofWorksheet. The remaining lines of tags.txt are blank. */

  }

  let(&labelType,"");
  let(&labelTypeTag,"");
  let(&colonLabelType,"");
  /* Deallocate memory for these three variables. */

  copy("proofWorksheetTrFfLabeled.txt", proofWorksheet);
  /* Re-initialize proofWorksheet to proofWorksheetTrFfLabeled.txt.
     proofWorksheetTrFrLabeled.txt is the translated proofWorksheet for which
     the assertion of each subproof not completable by the mmj2 unification
     means and not a single step subproof is labeled with a type ff label.

	 If argv1 == "runCompleteusersproofmv", then the non-unionized assertion
	 step of each subproof completable by the metavariable deduction
	 unification means has already been generated and hypothesis fields edited.
     Because these subproofs have been modified, they have become subproofs
	 completable by the mmj2 unification means and are therefore neither the
	 unionized assertion step or the non-unionized assertion step of each of
	 these subproofs has a label of type ff. */

  parallel(proofWorksheet, "tags.txt", proofWorksheet, "");
  /* Add the end tags of tags.txt to proofWorksheet. Each tagged step is an
     assertion whose subproof is completable by the means corresponding to its
	 tag, which, for argv1 == "runCompleteusersproofsmv", is completable by the
	 single metavariable deduction unification means. If argv1 does not have the
	 value "runCompleteusersproofsmv", then tags.txt will be empty and this
	 statement will have no effect.

	 The statements below will complete each subproof of proofWorksheet
	 completable by the mmj2 unification means. If argv1 has the value
	 "runCompleteusersproofsmv", the the remaining subproofs which are
	 completable by the single metavariable deduction unification means will be
	 completed by that means. The assertion of each of these subproofs
	 is end-tagged with "!!!ffsmv". If argv1 has the default value, then no
	 steps will be end-tagged and no subproofs will be completed by the
	 single metavariable deduction unification means. An attempt is then made
	 to complete each remaining subproof by the unification theorem means. Each
	 of those subproofs completable by that means is completed. The remaining
	 subproofs, if any, are not completed. */

  substituteLabels(proofWorksheet, "mapFfToEelUunNumPermFfsmv.txt", 125);
  /* Replace the label of any step of the Proof Worksheet having a type ff
     false deduction label with its corresponding label of the type of the
	 step's tag. For each ff-labeled untagged step, replace its type ff label
	 with its corresponding type eel label. */

  delete(proofWorksheet, "!!!", "");
  /* Remove the end tag of each end-tagged step. */

  if ( strcmp(argv2,"onlyShortScripts") == 0 || strcmp(argv2,"scripts") == 0 ) mmj2Unify(proofWorksheet, "unifyFF.exe");
  else mmj2Unify(proofWorksheet, "mmj2FalseDeductions.bat");
  /* mmj2-unify proofWorksheet. */

  removeATypeOfLabel(proofWorksheet, ":ffunifthm");
  /* The last mmj2 unification generated unification theorems. Also, the type
     ffeel labels generated theorems which unify with labels of type ffunifthm.
	 Best to delete these many type ffunifthm labels now. */

  if ( strcmp(argv1,"runCompleteusersproofsmv") == 0 )
  {

     editHypFieldOfTAssertions(proofWorksheet, "ffTmv");
	 editHypFieldOfHypSteps(proofWorksheet, "ffsmv");
     editHypFieldOfHypSteps(proofWorksheet, "ffTmv");
	 editHypFieldOfAssertions(proofWorksheet, "ffsmv");
	 editHypFieldOfAssertions(proofWorksheet, "ffTmv");
	 /* If the value of argv1 is "runCompleteusersproofmv, edit the hypothesis
	    field of each step requiring the editing of its hypothesis field of
	    each subproof whose asssertion step has a label of type ffsmv.

	    These statements to edit subproofs of type ffsmv and ffTmv are not
	    needed if argv[1] has a value not "runCompleteusersproofsmv".
	    Therefore, they are skipped if this is not the value of argv[1]. */

  }

  editHypFieldOfHypSteps(proofWorksheet, "ffeel");
  editHypFieldOfAssertions(proofWorksheet, "ffeel");
  /* Edit the hypothesis field of the assertion step and each hypothesis
     step of each subproof whose assertion step has a label of type ffeel.

	 Completion by the unification theorem means for labels of type ffeel
	 requires editing of the hypothesis field of hypothesis steps and the
	 assertion step of a subproof. Unlike subproofs with type ffsmv labels,
	 the hypothesis field of the last hypothesis step does not requiring
	 editing. For ffeel that step is the unification theorem. For ffTmv, that
	 step is the step of the assertion in standard T form.

	 Whether argv[1] has the value "runCompleteusersproofsmv" or the
	 default value, the unification theorem means of subproof completion is
	 always attempted if no other means completes a subproof.

	 The editing of the hypothesis fields of steps of a subproof is required
	 for the unification theorem means for labels of type eel and ffeel. It is
	 not required for the unification theorem means for labels of type eel and
	 not of type ffeel. The above two statements edit the hypothesis fields. */

  deleteStepsWithWorkVariables(proofWorksheet);
  /* Delete each d<i> step with an empty hypothesis field and a wwf in which
     at least one work variable occurs. */

  deleteLabels(proofWorksheet, "");
  /* Now that some hypothesis fields may have been edited, some of the existing
	 labels may have become incorrect. Delete the label of each labeled step of
	 proofWorksheet. With this statement, all labels, including any incorrect
     labels, have been purged from proofWorksheet. */

  if ( strcmp(argv2,"onlyShortScripts") == 0 || strcmp(argv2,"scripts") == 0 ) mmj2Unify(proofWorksheet, "unify.exe");
  else mmj2Unify(proofWorksheet, "mmj2StepProver.bat");
  /* mmj2-unify the input proofWorksheet in case it was not unified. This
     cannot generate any type ff labels of an unwanted type because fd.txt
	 is not loaded. */

  completeTheoremSteps(proofWorksheet);
  /* Attempt to complete each theorem step in proofWorksheet which did not
     unify with a theorem in set.mm */

  nameUsersProofFile(proofWorksheet);
  /* Prior executing this statement the sole output file containing the User's
     Proof after processing by completeusersproof is
	 c:\mmj2\mmj2jar\proofWorksheet.txt. This statement creates a second output
	 file with identical content. That output file is
	 c:\mmj2\mmj2jar\myproofs\<theorem>.mmp, where <theorem> is the theorem
	 name in the header of the User's Proof. */

  return;

}

  /* df-addMvSubtheoremTs() */
  void addMvSubtheoremTs(char* proofWorksheet, char* mapFfToUun)
{

  /* Make each 0-virtual deduction step (each conventional notation step) of
     proofWorksheet which is a step of an mv-completable subproof into a
	 virtual deduction step with an empty virtual hypothesis collection.

	 Each step of an mv-completed subproof must be a virtual deduction in the
	 User's Proof. Every step of a metavariable deduction in set.mm has virtual
	 hypothesis collection if translated into VD notation. Every step of a
	 metavariable deduction with an empty virtual hypothesis collection if
	 translated into VD notation has the antecedent "T." in conventional
	 notation. Therefore, each step of each subproof which shall be
	 mv-completed which is not a virtual deduction in the User's Proof must be
	 made into a virtual deduction with an empty hypothesis collection.
	 addMvSubtheoremTs() does this.

	 proofWorksheetmv.txt contains the information of which subproofs are
	 mv-completable. */

  void completeusersproofmv(char* proofWorksheet,
                            char* mapFfToUun);
  void copy(char* inpfiles, char* outfile);
  void tagLabeledSteps(char* proofWorksheet, char* tag);
  void match(char* iofile, char* matchstr, char* YorN);
  void add(char* iofile, char* begstr, char* endstr);
  void substitute(char* iofile, char* oldstring, char* newstring,
                  char* occurrence, char* matchstring);
  void delete(char* iofile, char* startstr, char* endstr);
  void endTag(char* iofile, char* endstr, char* matchstring);
  void unduplicate(char* iofile);
  /* Declare functions used by addMvSubtheoremTs(). */

  copy(proofWorksheet, "proofWorksheetmv.txt");
  completeusersproofmv("proofWorksheetmv.txt", mapFfToUun);
  /* completeusersproofmv() completes any subproof in proofWorksheetmv.txt
     which unifies with a metavariable deduction in set.mm. The remaining
	 subproofs are not altered. The output file is proofWorksheetmv.txt. */

  copy("proofWorksheetmv.txt", "hypStepNos.txt");
  /* "proofWorksheetmv.txt" is the mv-completed proof completed by
     completeusersproofmv(). This shall be reduced to only mvSteps. The file
	 containing these steps is mvSteps.txt. */

  tagLabeledSteps("hypStepNos.txt", "!!!");
  substitute("hypStepNos.txt", "!!!", "", "1", ":a1i ");
  /* Except for steps labeled a1i, end-tag each labeled step of hypStepNos.txt
     with "!!!". Each labeled step is a unionized assertion or a non-unionized
	 assertion of an mv-completed subproof. The step of a single-step
	 subproof (a theorem) is considered to be a unionized assertion.

	 For subproofs with a non-unionized assertion, the non-unionized
	 assertion step contains within its hypothesis field the step numbers of
	 the hypotheses of the subproof and the unionized assertion step contains
	 its own step number. For subproofs without a non-unionized assertion,
	 the unionized assertion step contains both its own step number and the
	 step numbers of the hypotheses of the subproof.

	 The reason for not end-tagging steps labeled a1i is because a subproof
	 unifying with a1i is a discharge subproof. Although a discharge
	 subproof may be mv-completable, it is not an actual metavariable
	 subproof. The unionized and non-unionized assertions of a metavariable
	 subproof each have a virtual hypothesis collection containing every
	 virtual hypothesis of every hypothesis step of the subproof. A
	 discharge subproof does not have this property. For a discharge
	 subproof, one or more of the virtual hypotheses of the hypothesis steps
	 are discharged. A discharged virtual hypothesis one which the discharge
	 subproof changes to an antecedent. For example, the set.mm deduction in1
	 discharges the single virtual hypothesis of a singleton virtual hypothesis
	 collection. iin1 is in1 translated into conventional notation.
	 completeusersproof() adds the empty virtual hypothesis collection to the
	 assertion of a subproof unifying with iin1 to transform it into a
	 subproof unifying with a1i. Other transformed discharge subproofs
	 common in Virtual Deduction User's Proofs also unify with a1i. These
	 subproofs are eliminated by the second statement. Discharge subproofs
	 which will, after being transformed, unify with deductions other than a1i
	 may be added to set.mm in the future. Then, it will be necessary to add
	 additional statements similar to the second statement which remove those
	 other labels.

	 Other than discharge subproofs, all other subproofs of a valid Virtual
	 Deduction User's Proof are true metavariable subproofs. This includes
	 metavariable subproofs of the form |- <wff> . These are 1-step
	 subproofs. They are theorems. These will have the form
	 |- (. T. -> <wff> ) in proofWorksheetmv.txt completed by
	 completeusersproofmv(). It is unlikely such a step will unify with a
	 theorem in set.mm. Theorems of the form |- ( ph -> <wff> ) ought not exist
	 in set.mm because the theorem |- <wff> is more laconic and contains the
	 same information.

	 Note that a step of the form |- ( T. -> <wff> ) is needed to mv-complete a
	 subproof which has the hypothesis step |- <wff> in the User's Proof.
	 mmj2 cannot directly unify with a metavariable deduction in set.mm the
	 translated form of such a subproof in a User's Proof.
	 completeusersproof() can because it automatically adds the empty virtual
	 hypothesis collection, T. . completeusersproof() saves the User the
	 work of: 1) adding a T. such a hypothesis, and 2) of 2-step completing the
	 subproof of that hypothesis step if <wff> is a theorem in set.mm
	 or(excl.) 3-step completing the subproof of that hypothesis step if
	 <wff> does not directly unify with a theorem in set.mm but is a semantic
	 variation of a theorem which does unify with a theorem in set.mm and for
	 which there exists a 1-hypothesis deduction in set.mm which unifies the
	 theorem and its semantic variation. */

  match("hypStepNos.txt", "!!!", "Y");
  /* The step numbers of the hypothesis steps and the unionized assertion step
     for each mv-completable subproof are needed. The step number of the
	 non-unionized assertion of any mv-completable subproof having a
	 non-unionized assertion is not needed because the User's Proof does not
	 contain non-unionized assertion steps.

	 The unionized assertion step number of each mv-completed subproof with a
	 non-unionized assertion is not needed in hypStepNos.txt because it must
	 already have a non-empty virtual hypothesis collection. If it had no
	 virtual hypotheses, then its subproof would not have a non-unionized
	 assertion. Because its hypothesis field contains the step number of the
	 non-unionized assertion, the information contained in its hypothesis field
	 is not needed either. Because the unionized assertion step of each
	 mv-completed subproof with a non-unionized assertion contains no needed
	 step number information, such unionized assertion steps are not needed in
	 hypStepNos.txt. However, putting the step number of such steps in
	 hypStepNos.txt is harmless.  An empty virtual hypothesis collection will
	 not be added to such steps each such step already has a virtual hypothesis
	 collection (a non-empty one).

	 Steps which are not steps of some mv-completable subproof do not contain
	 any step numbers of steps for which the empty virtual hypothesis
	 collection must be added. Therefore, such steps are deleted from
	 hypStepNos.txt. Being a ProofWorksheet, hypStepNos.txt contains lines
	 other than proof steps. These lines are also deleted.

	 Each hypothesis step of an mv-completable subproof which is not also a
	 unionized assertion step of some other mv-completable subproof is
	 deleted from hypStepNos.txt because it is an unlabeled step. While the
	 step number of each such step is needed (because such a step must have the
	 empty virtual hypothesis collection added if it does not already have a
	 non-empty virtual hypothesis collection), it is obtained from the
	 hypothesis field of an assertion step of its subproof, which is not
	 deleted from hypStepNos.txt.

	 In summary, each step number of a step for which an empty virtual
	 hypothesis collection must be added if that step does not already have a
	 non-empty virtual hypothesis collection is contained in some assertion
	 step of some mv-completable subproof.  Therefore, every assertion step
	 of some mv-completable subproof is retained in hypStepNos.txt and all
	 other steps are deleted. Each assertion step of some mv-completable
	 subproof has a label. All other steps are labeless. The matchstring
	 "!!!" identifies the labeled steps. All other steps are deleted from
	 hypStepNos.txt by match(). */

  substitute("hypStepNos.txt", ":", "@@@", "2", "");
  delete("hypStepNos.txt", "@@@", "");
  substitute("hypStepNos.txt", ":", " ", "1", "");
  substitute("hypStepNos.txt", ",", " ", "A", "");
  substitute("hypStepNos.txt", " ", "\\n", "A", "");
  /* For each step retained in hypStepNos.txt, all of the step number
     information is either the step number of the step itself or is contained
	 in the hypothesis field of the step. The first two statements eliminate
	 all information to the right of the hypothesis field,which is unneeded.
	 The third and fourth statements create a white space between the step
	 numbers. The fifth statement puts each step number on its own line.

	 Each step number containing the matchstring "d" other than the qed step is
	 a the step number of a step generated by completeusersproofmv(). Each such
	 step does not exist in the User's Proof. Since only a step which is in the
	 User's Proof may need an empty virtual hypothesis collection added, step
	 numbers containing the matchstring "d" other than step number qed need not
	 be in hypStepNos.txt. proofWorksheet at the point at which
	 addMvSubtheoremTs() is called is the User's Proof. Note that the User's
	 Proof is the input file of completeusersproof(). The step numbers
	 containing the matchstring "d" other than the qed step are harmless if
	 retained in hypStepNos.txt. They are removed (by a statement below) for
	 clarity. */

  unduplicate("hypStepNos.txt");
  add("hypStepNos.txt", "@@@", ":");
  match("hypStepNos.txt", "@@@d", "N");
  match("hypStepNos.txt", "@@@:", "N");
  add(proofWorksheet, "@@@", "");
  /* "26" does not uniquely identify a step number in proofWorksheet if
     proofWorksheet contains steps 26 and 326. "@@@26" does not uniquely
	 identify a step number in proofWorksheet if proofWorksheet contains steps
	 26 and 268. "@@@26:" uniquely identifies step number 26 in proofWorksheet.

	 The first statement is needed because hypStepNos.txt will contain
	 duplicate step numbers if a step is both a hypothesis step for a labeled
	 mv deduction and an assertion step for another mv deduction. unduplicate()
	 removes duplicate step numbers. If there were duplicates the end tag "!!!"
	 of the loop below would sometimes be added to some lines more than once,
	 causing incorrect behavior.

	 The fourth statement to remove the "@@@:" of lines without a step number.
	 Such lines occur for steps with empty hypothesis fields. This is done for
	 clarity as the "@@@:" are harmless.

	 The third statement removes automatically generated step numbers, uniquely
	 identified by the matchstring "@@@d".

	 The fifth statement adds the beginning tag "@@@" to each line of the
	 User's Proof so that the "@@@26:" value of stepNumber will match the
	 corresponding step number in the User's Proof.  */

    vstring stepNumber = (vstring)malloc(30);

  FILE* fp = fopen("hypStepNos.txt", "r");

  while(fscanf(fp, "%s", stepNumber) != EOF)
  /* This loop reads from hypStepNos.txt one stepNumber each pass and end-tags
     that step stepNumber with "!!!". The loop iterates until every stepNumber
	 in hypStepNos.txt has been read. */

  {

  endTag(proofWorksheet, "!!!", stepNumber);

  }

 fclose(fp);
 /* Close the file pointer for hypStepNos.txt. */

 let(&stepNumber, "");
  /* Having finished using stepNumber, the memory of this variable is
     deallocated to avoid the risk of possible memory leaks. */

  substitute(proofWorksheet, "@@@", "", "1", "");
  /* The beginning tags "@@@" of the User's Proof lines are no longer needed
     and are removed. */

  substitute(proofWorksheet, ").!!!", ").", "1", "");
  substitute(proofWorksheet, " |- ", " |- (. T. ->. ", "1", "!!!");
  substitute(proofWorksheet, "!!!", " ).", "1", "!!!");
  /* Make each end-tagged step of proofWorksheet not a virtual deduction (steps
     in conventional notation) into a virtual deduction with the empty virtual
	 hypothesis collection, " T. ". Every step of a mv-completable subproof
	 of the proof must be a virtual deduction to unify with a deduction in
	 set.mm of the form

	   ( ph -> <wff1> ) & ( ps -> <wff2> ) . . . & ( ch -> <wff<(n-1)>> )

	   infers

	   ( ( ph /\ ps /\ . . . /\ ch ) -> <wff<n>> )

     Such deductions are called "metavariable deductions".
	 ph, ps, . . . ch are the "metavariables". This form of deduction is a
	 variation of the single metavariable form conceived of and used by Mario
	 Carneiro.

	 The loop end-tags each step of each mv-completable subproof, whether
	 or not it is a virtual deduction. The first statement removes tags of
	 steps already a virtual deduction. Each remaining tagged step is in
	 conventional notation and is transformed into a virtual deduction with an
	 empty virtual hypothesis collection. */

  copy(proofWorksheet, "proofWorksheet0.txt");
  /* proofWorksheet0.txt is the User's Proof with Ts added to each
     mv-completable subproof. It is called by removeUnneededFfLabels(). */

  return;

}

  /* df-addMvAssertionSteps() */
  void addMvAssertionSteps(char* proofWorksheet, char* proofWorksheetmv)
{

  /* Create the file mvSteps.txt from proofWorksheetmv and replace each
     counterpart step in proofWorksheet of a step in mvSteps.txt with the
	 mvStep.txt step. mvSteps.txt contains all unionized and non-unionized
	 assertion steps of mv-completed subproofs of the proof. If it has a
	 non-unionized assertion, each unionized assertion contained in mvSteps.txt
	 is right-joined to its corresponding non-unionized assertion. An
	 "mv-completed subproof" is a subproof of the User's Proof completed by
	 completeusersproofmv(). */

  void copy(char* inpfiles, char* outfile);
  void add(char* iofile, char* begstr, char* endstr);
  void substitute(char* iofile, char* oldstring, char* newstring,
                  char* occurrence, char* matchstring);
  void delete(char* iofile, char* startstr, char* endstr);
  void endTag(char* iofile, char* endstr, char* matchstring);
  void parallel(char* inpfile1, char* inpfile2, char* outfile,
                char* btwnstr);
  /* Declare functions used by addMvAssertionSteps(). */

  copy(proofWorksheetmv, "mvSteps.txt");
  /* proofWorksheetmv is the mv-completed proof completed by
     completeusersproofmv(). This shall be reduced to only mvSteps. The file
	 containing these steps is mvSteps.txt. */

  add("mvSteps.txt", "@@@", "");
  substitute("mvSteps.txt", "\\n", "$$$$$$", "1", "@@@d");
  substitute("mvSteps.txt", "@@@", "", "A", "");
  /* Join each non-unionized assertion with its unionized assertion, with
     "$$$$$$" as a separator. The non-unionized assertion is on the left side.
     Each non-unionized assertion step is uniquely identified by "d" as the
	 first character of the line. Beginning-tag all lines with "@@@". Then
	 the matchstring "@@@d" uniquely identifies each non-unionized assertion
	 step. Next, join it with the step immediately above by deleting the
	 invisible carriage return "\\n", replacing it with the separator "$$$$$$".
	 After this is achieved, remove the "@@@" beginning tag. Upon completion of
	 these steps the proof and the original proof have the same number of lines
	 with corresponding steps in the same position on the Proof Worksheet. */

  substitute("mvSteps.txt", ": ", "@@@!!!", "1", ": ");
  substitute("mvSteps.txt", ":a1i ", "@@@!!!", "1", "");
  delete("mvSteps.txt", "", "@@@");
  delete("mvSteps.txt", "!!!", "");
  /* Replace each unlabeled step and each step labeled with a1i with "". This
     replaces with a blank line the unionized assertion of each subproof
	 which unifies with the iidn1 discharge subproof and the unionized
	 assertion line of each subproof which did not unify with a metavariable
	 deduction in set.mm (which was not completed by
	 completeusersproofmv()). */

  add("mvSteps.txt", "@@@", "");
  substitute("mvSteps.txt", "@@@", "!!!", "1", " |- ");
  substitute("mvSteps.txt", "!!!", "@@@", "1", " $p ");
  substitute("mvSteps.txt", "!!!", "@@@", "1", " $e ");
  delete("mvSteps.txt", "@@@", "");
  substitute("mvSteps.txt", "!!!", "", "1", "");
  /* Replace with "" (a blank line) any line of mvSteps.txt not already blank
     and not a proof step (or a joined non-unionized and unionized assertion).
	 At this point, each line remaining in mvSteps.txt is either the unionized
	 assertion step of a subproof completed by completeusersproofmv()
	 or(excl.) the joined non-unionized and unionized assertion steps of a
	 subproof completed by completeusersproofmv().

	 Only metavariable subproofs which mv-completed shall be retained in the
	 final Proof Worksheet. Discharge subproofs are not metavariable
	 subproofs because one or more virtual hypotheses of some hypotheses of a
	 discharge subproof are not virtual hypotheses of its unionized
	 assertion. */

  endTag("mvSteps.txt", "!!!", " |- ");
  /* End-tag each non-blank line.

     mvSteps.txt is now complete. It contains all unionized and non-unionized
	 assertion steps of mv-completed subproofs of the proof. If it has a
	 non-unionized assertion, each unionized assertion contained in mvSteps.txt
	 is right-joined to its corresponding non-unionized assertion. An
	 "mv-completed subproof" is a subproof of the User's Proof completed by
	 completeusersproofmv(). */

  parallel("mvSteps.txt", proofWorksheet, proofWorksheet, "");
  delete(proofWorksheet, "!!!", "");
  /* For each subproof completed by completeusersproofmv(), replace its
     assertion step in the original proofWorksheet by its counterpart unionized
	 assertion step or non-unionized assertion step joined with the unionized
	 assertion step of mvStep.txt. */

  substitute(proofWorksheet, "$$$$$$", "\\n", "1", "");
  /* For each pair of joined non-unionized and unionized assertion steps in
     proofWorksheet, replace the carriage return at the end of the
	 non-unionized assertion step to split the pair so each is on its own line,
	 as it was before the two steps were joined. */

  copy(proofWorksheet, "proofWorksheet2.txt");
  add("proofWorksheet2.txt", "@@@", "");
  substitute("proofWorksheet2.txt", "@@@", "$$$", "1", "qed:");
  substitute("proofWorksheet2.txt", "$$$", "!!!", "1", " |- ( T. -> ");
  delete("proofWorksheet2.txt", "@@@", "");
  delete("proofWorksheet2.txt", "$$$", "");
  substitute("proofWorksheet2.txt", ":", ":q:trud |- @@@", "1", "");
  substitute("proofWorksheet2.txt", " |- ( T. -> ", "$$$", "1", "");
  delete("proofWorksheet2.txt", "@@@", "$$$");
  endTag("proofWorksheet2.txt", "@@@", "!!!");
  substitute("proofWorksheet2.txt", " )@@@", "", "1", "");
  parallel(proofWorksheet, "proofWorksheet2.txt", proofWorksheet, "");
  substitute(proofWorksheet, "qed:", "q:", "1", "!!!");
  substitute(proofWorksheet, "!!!", "\\n", "1", "");
  /* If the qed step has the form |- ( T. -> <wff>) , make it the hypothesis of
     a deduction whose assertion is |- <wff> . That assertion becomes the qed
	 step. If the qed step does not contain the matchstring " |- ( T. -> ",
	 then no changes will be made. If the User's Proof has "QED" rather than
	 "qed" as the last step number this has no effect on this code because
	 a prior mmj2 unification changes "QED" to "qed". */

  return;

}

/* df-completeusersproofmv() */
void completeusersproofmv(char* proofWorksheetmv, char* mapFfToUun)
{

  /* The input Proof Worksheet is the "User's Proof". It is intended to be a
     correct Virtual Deduction proof. completeusersproofmv() completes any
	 subproof in proofWorksheet which unifies with a metavariable deduction
	 in set.mm. Metavariable deductions in set.mm in their most general form
	 have assertion steps with a collection of the virtual hypothesis
	 collections of the hypothesis steps. Such assertions are called
	 "non-unionized" assertions. The User's Proof is assumed to have only
	 "unionized" assertions. The virtual hypothesis collection of a unionized
	 assertion is is obtained by taking the union of the collection of virtual
	 hypothesis collections of the corresponding non-unionized assertion. If
	 the non-unionized virtual hypothesis collection would be identical to the
	 unionized virtual hypothesis collection, then the generating of a
	 non-unionized assertion is not necessary and is not generated.

	 The primary function of completeusersproofmv() is to automatically
	 construct the non-unionized assertion step of any subproof requiring
	 one in order to have the potential to unify with a metavariable
	 deduction in set.mm if one exists. In addition to generating
	 non-unionized assertion steps, completeusersproofmv() transfers the
	 hypothesis field of each unionized assertion having a corresponding
	 non-unionized assertion to the non-unionized assertion. The hypothesis
	 field of the unionized assertion becomes filled with the step number
	 of the non-unionized assertion. Each pair of unionized and non-unionized
	 assertions are linked by a uun* deduction.

	 completeusersproof() completes a subproof of the proof by generating
	 a unification theorem. completeusersproofmv() completes a subproof of
	 the proof by generating a non-unionized assertion which takes the
	 hypothesis field of its unionized assertion.

	 As explained in comments at the beginning of completeusersproof(), every
	 possible permutation of the collection of virual hypothesis collections
	 of the non-unionized assertion must be tried because completeusersproof()
	 does not know which permutation is used by, if it exists, the metavariable
	 deduction in set.mm which mv-completes the subproof.

	 Each hypothesis step and the assertion step of a metavariable deduction
	 must have a virtual hypothesis collection. T. represents the empty
	 virtual hypothesis collection and is used for steps with no virtual
	 hypotheses. It is assumed that each 0-virtual hypothesis step of the
	 original proof is in theorem form. Each such step is initially
	 transformed into the form of a virtual deduction with an empty virtual
	 hypothesis collection. In order to use completeusersproofmv() effectively
	 each step of the proof must be a virtual deduction. */

  void mmj2Unify(char* proofWorksheet, char* mmj2BatFile);
  void translate(char* proofWorksheet);
  void tagLabeledSteps(char* proofWorksheet, char* tag);
  void removeUnneededFfLabels(char* proofWorksheet, char* proofWorksheet0);
  void tag(char* iofile, char* begstr, char* endstr, char* startmatch, char* sn, char* endmatch, char* en);
  void match(char* iofile, char* matchstr, char* YorN);
  void substituteLabels(char* proofWorksheet, char* labelMapping, int c);
  void substitute(char* iofile, char* oldstring, char* newstring, char* occurrence, char* matchstring);
  void delete(char* iofile, char* startstr, char* endstr);
  void deleteLabels(char* iofile, char* matchstring);
  void add(char* iofile, char* begstr, char* endstr);
  void copy(char* inpfiles, char* outfile);
  void parallel(char* inpfile1, char* inpfile2, char* outfile, char* btwnstr);
  void addUnionizedAssertionPermutations(char* proofWorksheetmv);
  void pickAPermutationOfEachUnionizedAssertion(char* proofWorksheetmv);
  /* Declare functions used by completeusersproofmv(). */

  substitute(proofWorksheetmv, " |- ", " |- (. T.!!! ->. ", "ALL", " |- ");
  substitute(proofWorksheetmv, " |- (. T.!!! ->. ", " |- ", "ALL", " ).");
  substitute(proofWorksheetmv, " |- (. T.!!! ->. ", " |- ", "ALL", " $p ");
  substitute(proofWorksheetmv, " |- (. T.!!! ->. ", " |- ", "ALL", " $e ");
  add(proofWorksheetmv, "", " ).@@@");
  substitute(proofWorksheetmv, " ).@@@", " ).", "ALL", " T.!!! ");
  substitute(proofWorksheetmv, " ).@@@", "", "ALL", "");
  substitute(proofWorksheetmv, "T.!!!", "T.", "ALL", "");
  /* Make each step of proofWorksheetmv not a virtual deduction (steps with no
     virtual hypotheses) into a virtual deduction with the empty virtual
	 hypothesis collection, " T. ". Every step of a subproof of the proof
	 must be a virtual deduction to unify with a deduction in set.mm of the form

	   ( ph -> <wff1> ) & ( ps -> <wff2> ) . . . & ( ch -> <wff<(n-1)>> )

	   infers

	   ( ( ph /\ ps /\ . . . /\ ch ) -> <wff<n>> )

     Such deductions are called "metavariable deductions". ph, ps, . . . ch
     are the "metavariables". This form of deduction is a variation of the
	 single metavariable form conceived of and used by Mario Carneiro.

	 We use the convention that every use of " |- " in a Proof Worksheet
	 comment shall be preceded by " $p " or " $e " . The matchstring " $p "
	 or " $e " identifies lines for which " |- <wff> " will not be made into
	 " |- (. T. ->. <wff> )." by this program. */

  copy(proofWorksheetmv, "proofWorksheet0.txt");
  /* The value of proofWorksheetmv at this point is saved as the value of
     proofWorksheet0.txt for use at the time removeUnneededFfLabels() is
	 called. It is an argument of removeUnneededFfLabels(). */

  if ( strcmp(argv2,"onlyShortScripts") == 0 || strcmp(argv2,"scripts") == 0 ) mmj2Unify(proofWorksheetmv, "unifyFF.exe");
  else mmj2Unify(proofWorksheetmv, "mmj2FalseDeductions.bat");
  /* No unwanted type ff labels are generated. Specificially, no typ ffunifthm
     labels because each theorem with a conjunction antecedent has been put
	 in standard T form. That form does not unify with a type ffunifthm
	 label. It is assumed that the only unwanted type that might be
	 generated is type ffunifthm. It was not. */

  substitute(proofWorksheetmv, "$", "@!", "ALL", "* ");
  tag(proofWorksheetmv, "", "@@@@@@", "$= ", "1", " $.", "1");
  match(proofWorksheetmv, "@@@@@@", "N");
  match(proofWorksheetmv, "$d ", "N");
  substitute(proofWorksheetmv, "@!", "$", "ALL", "* ");
  match(proofWorksheetmv, "I-PA-0119", "N");
  /* Delete any RPN proof which may be generated by the mmj2 unification. */

  removeUnneededFfLabels(proofWorksheetmv, "proofWorksheet0.txt");
  /* removeUnneededFfLabels() deletes the ff* label from each step of
     proofWorksheetmv.txt if the step in conventional notation will get a
	 set.mm label upon mmj2 unification.

	 The value of the argument proofWorksheet0 is the value of proofWorksheetmv
	 at a point prior to the time at which removeUnneededFfLabels() is called.
	 That value is different at the time the function is called. */

  substituteLabels(proofWorksheetmv, "mapFfToEelUunNumPermFfsmv.txt", 13);
  /* Replace the label of any step of the Proof Worksheet with a ff* false
     deduction label with its corresponding uun* label. If there is no
	 corresponding uun* label for a step because a non-unionized assertion is
	 not necessary, then the filler "nulllabel" is temporarily provided as a
	 label. After the label replacement process has completed each "nulllabel"
	 is replaced by "".

	 At this point, each unionized assertion of a subproof requiring a
	 non-unionized assertion is labeled. The remaining steps are labelless.

	 This statement replaces each ff label in proofWorksheetmv with a label
	 in column 3 of the mapping file. Thus, every ff label is either removed
	 or replaced with a label in set.mm. Therefore, at this point
	 proofWorksheetmv has no ff labels. */

  translate(proofWorksheetmv);
  /* Convert each virtual deduction notation wff of the Proof Worksheet to its
     conventional notation counterpart. */

  copy(proofWorksheetmv, "unionizedAssertionLabels.txt");
  /* unionizedAssertionLabels.txt at this point is proofWorksheetmv. Each step
     of the proof is labelless except for each unionized assertion step whose
	 subproof is not completable without the addition of a non-unionized
	 assertion (and may not be completable even with the addition of
	 a non-unionized assertion). The proof of unionizedAssertionLabels.txt is
	 in conventional notation.

	 In pickAPermutationOfEachUnionizedAssertion() unionizedAssertionLabels.txt
     shall be reduced to a list of its (uun*) labels with the ordering of the
	 labels preserved. */

  addUnionizedAssertionPermutations(proofWorksheetmv);

  tagLabeledSteps(proofWorksheetmv, "!!!");
  /* End-tag each labeled step of proofWorksheetmv with "!!!". */

  copy(proofWorksheetmv, "hypFields.txt");
  match("hypFields.txt", "!!!", "Y");
  delete("hypFields.txt", "", ":");
  delete("hypFields.txt", ":", "");
  /* hypFields.txt is a list of the hypothesis fields of the unionized
     assertions of the original proofWorksheet. The original
	 proofWorksheet ordering is maintained. */

  substitute(proofWorksheetmv, ":", ":@@@", "1", "!!!");
  substitute(proofWorksheetmv, ":", "##@&:", "2", "!!!");
  delete(proofWorksheetmv, "@@@", "##@&");
  substitute(proofWorksheetmv, "!!!", "", "1", "");
  /* Delete the hypothesis field of each unionized assertion step to be unified
     with its corresponding non-unionized assertion with a 1-hypothesis uun*
	 deduction. Each deleted hypothesis field was previously saved in
	 hypFields.txt to later become the non-unionized assertion's hypothesis
	 field. */

  if ( strcmp(argv2,"onlyShortScripts") == 0 || strcmp(argv2,"scripts") == 0 ) mmj2Unify(proofWorksheetmv, "unify.exe");
  else mmj2Unify(proofWorksheetmv, "mmj2StepProver.bat");
  /* mmj2-unify proofWorksheetmv to generate the non-unionized assertions
     (without their hypothesis field filled). This mmj2 unification also
	 completes those subproofs (now in conventional notation) which unify a
	 theorem or deduction in set.mm.

	 There are no type ff labels in proofWorksheetmv immediately preceding
	 the execution of this statement. Therefore no unwanted type ff labels
	 needed to be removed. After the mmj2Unify() call there continue to
	 be no type ff labels in proofWorksheetmv because fd.txt is not
	 loaded. */

  add(proofWorksheetmv, "@@@", "");
  deleteLabels(proofWorksheetmv, "@@@d");
  delete(proofWorksheetmv, "", "@@@");
  /* The last mmj2 unification will generate not only non-unionized assertion
     steps, but will also generate a label for each generated non-unionized
	 assertion step which unifies with a theorem in set.mm. This is because,
	 at this point, the former hypothesis field of each unionized assertion
	 step has not yet been moved to the hypothesis field of the
	 non-unionized assertion, These 3 statements remove any such labels which
	 may have been generated. */

  add(proofWorksheetmv, "@@@", "");
  substitute(proofWorksheetmv, "::", ":hypField:", "1", "@@@d");
  substitute(proofWorksheetmv, "@@@", "", "1", "");
  /* add the variable hypField to each of the empty hypothesis fields of the
     newly generated non-unionized assertion steps */

  vstring hypField = (vstring)malloc(30);

  FILE* fp = fopen("hypFields.txt", "r");

  while(fscanf(fp, "%s", hypField) != EOF)
  /* This loop reads from hypFields one hypField each pass and makes it the
     hypothesis field of the first non-unionized assertion step with an empty
	 hypothesis field. The loop iterates until the hypothesis field of each
	 non-unionized assertion step is filled with the hypothesis field of its
	 original unionized assertion. */

  {

  tag(proofWorksheetmv, "", "@@@", "hypField", "1", "hypField", "1");
  substitute(proofWorksheetmv, "hypField", hypField, "1", "@@@");
  substitute(proofWorksheetmv, "@@@", "", "1", "");
  /* Make the hypothesis field of a unionized assertion of the original
     proofWorksheetmv the hypothesis field of its non-unionized assertion.*/

  }

 fclose(fp);
 /* Close the file pointer for hypFields.txt. */

 let(&hypField, "");
  /* Having finished using the values contained in hypField, the memory of this
     variable is deallocated to avoid the risk of possible memory leaks. */

  if ( strcmp(argv2,"onlyShortScripts") == 0 || strcmp(argv2,"scripts") == 0 ) mmj2Unify(proofWorksheetmv, "unify.exe");
  else mmj2Unify(proofWorksheetmv, "mmj2StepProver.bat");
  /* This mmj2-unification unifies each subproof in proofWorksheet having a
     non-unionized assertion with a metavarible deduction in set.mm, if one
	 exists.

	 No type ff labels were added to prooWorksheetmv since the last
	 mmj2Unify() call. There were none at that time of that call and there were
	 none immediately preceding the instant call and there continue to be none
	 as a consequence of the instant call because fd.txt is not loaded. Thus,
	 at the time completeusersproofmv() has completed execution there are no
	 type ff labels in proofWorksheetmv and no need to removed any unwanted
	 type ff labels. */

  pickAPermutationOfEachUnionizedAssertion(proofWorksheetmv);
  /* For each unionized assertion of the User's Proof, delete from
     proofWorksheetmv all but one permutation of that unionized assertion. */

  return;

}

/* df-addUnionizedAssertionPermutations() */
void addUnionizedAssertionPermutations(char* proofWorksheetmv)
{

  /* Immediately prior to the addUnionizedAssertionPermutations() call the 0th
     uun* permutation label was added to the unionized assertion step of
	 each subproof which is not completable by an mmj2 unification alone.
     addUnionizedAssertionPremutations() adds n-1 additional unionized
     assertion steps to each subproof whose original unionized assertion step
     is labeled with the 0th uun* permutation, where n is the number of uun*
     permutations for that subproof. addUnionizedAssertionPremutations()
     also labels each of the added unionized assertion steps with a uun*
     permutation.

     addUnionizedAssertionPermutations() does not alter the hypothesis
	 field of the added unionized assertion steps.  That is done later by
	 completeusersproofmv(). Nor does it generate the non-unionized assertion
	 steps corresponding to each unionized assertion permutation. That, too, is
	 done by completeusersproofmv(). */

  void tag(char* iofile, char* begstr, char* endstr, char* startmatch, char* sn, char* endmatch, char* en);
  void match(char* iofile, char* matchstr, char* YorN);
  void substituteLabels(char* proofWorksheet, char* labelMapping, int c);
  void tagLabeledSteps(char* proofWorksheet, char* tag);
  void add(char* iofile, char* begstr, char* endstr);
  void addMT(char* iofile, char* begstr, char* endstr, char* matchstring);
  void addMTMF(char* iofile, char* begstr, char* endstr, char* matchstringT,
               char* matchstringF);
  void substitute(char* iofile, char* oldstring, char* newstring, char* occurrence, char* matchstring);
  void delete(char* iofile, char* startstr, char* endstr);
  void copy(char* inpfiles, char* outfile);
  void parallel(char* inpfile1, char* inpfile2, char* outfile, char* btwnstr);
  void clean(char* iofile, char* subcmd);
  /* Declare functions used by addUnionizedAssertionPermutations(). */

  copy(proofWorksheetmv, "proofWorksheetmv2.txt");
  tagLabeledSteps("proofWorksheetmv2.txt", "!!!");
  add("proofWorksheetmv2.txt", "^^^", "");
  substitute("proofWorksheetmv2.txt", "^^^", "", "1", "!!!");
  substitute("proofWorksheetmv2.txt", "!!!", "", "1", "");

  addMTMF("proofWorksheetmv2.txt", "$$$@@@ppp", "", " |- ", ": ");
  substitute("proofWorksheetmv2.txt", "$$$@@@ppp", "", "1", "$p");
  substitute("proofWorksheetmv2.txt", "$$$@@@ppp", "", "1", "$e");
  substitute("proofWorksheetmv2.txt", " |- ", "%%% |- ", "1", "$$$@@@ppp");
  substitute("proofWorksheetmv2.txt", "%%%", "\\n", "1", "");
  clean("proofWorksheetmv2.txt", "E");
  addMT("proofWorksheetmv2.txt", "", "p@@@", "$$$@@@ppp");
  /* The objective of these statements is to edit <label> to be
     "<label>p@@@". This cannot be achieved by editing " |- " to be "p@@@ |- "
     because there may be more than one white space between <label> and
	 " |- ", which would result in one or more white spaces between <label> and
	 "p@@@". Each line is split so that "<label>" is at the end of the line.
	 The end white spaces are deleted by clean(), the endstring "p@@@" is
	 added to the end of each line containing <label> (at the end), and then
	 each line which was split is re-joined. */

  substitute("proofWorksheetmv2.txt", "p@@@\\n", "p@@@", "1", "");
  delete("proofWorksheetmv2.txt", "^^^", "");
  /* Lines containing "^^^" are deleted now instead of immediately after "^^^"
     was added because otherwise the above line re-joining statement does not
	 behave as expected. The re-joined lines become higher than they originally
	 were. It is conjectured that the blank lines cause the unintended
	 behavior. */

  char permutationNumber[10];

  int i;

  for(i = 1; i < 24; i = i + 1)
    {

	parallel(proofWorksheetmv, "proofWorksheetmv2.txt", proofWorksheetmv, "");

	sprintf(permutationNumber, "%d", i);
	/* sprinf() assigns a value to the char* variable permutationNumber the
	   integer i as a character string. */

	substitute(proofWorksheetmv, "@@@", permutationNumber, "A", "");

    }

    addMTMF("unionizedAssertionLabels.txt", "", "!!!", " |- ", ": ");
  /* unionizedAssertionLabels.txt starts as a copy of proofWorksheetmv at
     the point when each step ofthe proof is labelless except for each
	 unionized assertion step whose subproof is not completable without the
	 addition of a non-unionized assertion. This point occurs in
	 completeusersproofmv().

	 In pickAPermutationOfEachUnionizedAssertion() unionizedAssertionLabels.txt
	 shall be reduced to a list of its (uun*) labels with the ordering of the
	 labels preserved. */

  match("unionizedAssertionLabels.txt", "!!!", "Y");
  match("unionizedAssertionLabels.txt", "$p", "N");
  match("unionizedAssertionLabels.txt", "$e", "N");

  /* Delete the comment line containing " |- " and "$p" or "$e" and not
     containing ": ". */

  delete("unionizedAssertionLabels.txt", "", ":");
  delete("unionizedAssertionLabels.txt", "", ":");
  delete("unionizedAssertionLabels.txt", " |- ", "");
  clean("unionizedAssertionLabels.txt", "E");
  /* In general, there may be more than white space between ":<label>%%%" and
     "|-". After the deletion statement is executed, there may be white spaces
	 after some of the labels. Later, when the number of permutations for
	 <label> is substituted for label, any white spaces which existed will
	 exist after the permutation number. Then the matchstring of
	 numberOfPermutations.txt will be of the form "$$$2 ppp" instead of
	 "$$$2ppp" and permutations p2, p3, . . . will not be deleted. The second
	 statement removes any white spaces at the end of the line to prevent
	 this. */

  copy("unionizedAssertionLabels.txt", "numberOfPermutationsList.txt");
  add("numberOfPermutationsList.txt", " ", " ");
  substituteLabels("numberOfPermutationsList.txt", "mapFfToEelUunNumPermFfsmv.txt", 34);
  /* At this point we have the list of the number of permutations ordered from
     the uun* list from the Proof Worksheet.

	 One white space is added before and after each label of
	 numberOfPermutationsList.txt. Otherwise, the oldstring of the
	 substituteLabels() call, " uun1 ", will not match "uun1" in
	 numberOfPermutationsList.txt and the number of permutations of for uun1
	 will not be substituted by substituteLabels(). */

  addMT("numberOfPermutationsList.txt", "$$$", "ppp", "");
  /* "$$$2ppp" matches the "$$$2ppp" of "$$$2ppp15:14,12,8:<label>p1 |- . . .".
 	 The matchstring "$$$2" is insufficient because it matches with
	 "$$$24ppp15 . . ." */

  addMT(proofWorksheetmv, "", "!!!", "$$$");
  /* End-tag with "!!!" each unionized assertion step. */

  vstring numPermutations = (vstring)malloc(30);

  FILE* fp = fopen("numberOfPermutationsList.txt", "r");

  while(fscanf(fp, "%s", numPermutations) != EOF)
  /* This is a loop which reads from labelMapping one oldLabel-newLabel pair
     each pass. If it occurs in proofWorksheet, oldLabel is the existing label
	 to be replaced. newLabel is the replacement label.

     The execution of the conditional expression of the while loop both
	 determines whether or not to exit the loop and reads the next pair of
	 labels from labelMapping. The next pair of labels is read each time
	 execution passes through the top of the while loop where the conditional
	 expression is evaluated. As step 1, the loop simultaneously reads the
	 next pair of labels and determines whether or not to exit the loop. As
	 step 2, the next oldLabel is replaced with its corresponding newLabel.
     The loop iterates until each label in the first column of labelMapping has
	 been compared to each label of each step of proofWorksheet and, if it
	 matches, is replaced in proofWorksheet by its corresponding second column
	 label. */

  {

    tag(proofWorksheetmv, "", "@@@", "!!!", "1", "!!!", "1");
	substitute(proofWorksheetmv, numPermutations, "&&&", "1", "@@@");
	delete(proofWorksheetmv, "&&&", "");
	/* Each processed unionized assertion step does not have a "!!!" end tag.
	   The step to be processed in the current iteration of the loop is the
	   first unionized assertion step not yet processed. It is the first
	   step with a "!!!" end tag. */

  }

  fclose(fp);
  /* Close the file pointer for labelMapping. */

  let(&numPermutations, "");
  /* Having finished using the values contained in oldLabel and newLabel, the
     memory of these variables is deallocated to avoid the risk of possible
	 memory leaks. */

  substitute(proofWorksheetmv, "$$$", "\\n", "A", "");
  /* Un-join the permutations of each line of unionized assertion
     permutations. */

  return;

}

/* df-pickAPermutationOfEachUnionizedAssertion() */
void pickAPermutationOfEachUnionizedAssertion(char* proofWorksheetmv)
{

  /* For those subproofs of the User's Proof not completable by mmj2
     unification alone, addUnionizedAssertionPermutations() duplicates the
	 assertion of each subproof n - 1 times, where n is the nuumber of uun*
	 permutations. completeusersprooofmv() adds the non-unionized assertion
	 uun* permutation corresponding to each of these. completeusersproofmv()
	 completes the transformation each of these subproofs into n subproofs,
	 one for each uun* permutation. It then mmj2-unifies proofWorksheetmv,
	 mv-completing any permutations which are mv-completable. This is the
	 state of proofWorksheetmv when pickAPermutationOfEachUnionizedAssertion()
	 is called.

     For each subproof which mv-completes,
	 pickAPermutationOfEachUnionizedAssertion() deletes the uun* permutations
	 which did not mv-complete. The uun* permutation which mv-completed
	 remains. For each subproof for which no uun* permutation completed,
	 pickAPermutationOfEachUnionizedAssertion() deletes all uun* permutations
	 except the 0th permutation. pickAPermutationOfEachUnionizedAssertion()
	 "picks" a single uun* permutation for each subproof. */

  void addMT(char* iofile, char* begstr, char* endstr, char* matchstring);
  void substitute(char* iofile, char* oldstring, char* newstring, char* occurrence, char* matchstring);
  void pickALabelPermutation(char* proofWorksheetmv, vstring label);
  /* Declare functions used by pickAPermutationOfEachUnionizedAssertion(). */

  addMT(proofWorksheetmv, "@@@", "", " |- ");
  addMT(proofWorksheetmv, "", "%%%", "@@@d");
  substitute(proofWorksheetmv, "@@@", "", "1", "");
  substitute(proofWorksheetmv, "%%%\\n", "%%%", "1", "");
  /* Join unionized and non-unionized assertion steps. */

  vstring label = (vstring)malloc(30);

  FILE* fp = fopen("unionizedAssertionLabels.txt", "r");
  /* unionizedAssertionLabels.txt was created in an above function. */

  while(fscanf(fp, "%s", label) != EOF)
  /* This loops reads one unionized assertion label at a time. It picks a
     single step labeled with a permutation of that label. */

  {

  pickALabelPermutation(proofWorksheetmv, label);
  /* The value saved in oldLabel is, for example, "ff22". But the character
     string to be replaced is ":ff22 ". "ff22" is transformed into ":ff22 "
	 using cat(). A similar transformation is made for the replacement label.
	 The first substitute() substitutes the next transformed new label for its
	 corresponding transformed old label. The second substitute() performs the
	 intended substitution if it was not performed by the first substitute()
	 statement. The reason the substitute statement is duplicated is because,
	 if substitute() is not applied twice per substitution, then it fails to
	 make the substitution for the second pair of labels in labelMapping. */

  }

  fclose(fp);
  /* Close the file pointer for "unionizedAssertionLabels.txt". */

  let(&label, "");
  /* Having finished using the values contained in label, the memory of this
     variable is deallocated to avoid the risk of possible memory leaks. */

  substitute(proofWorksheetmv, "$$$", "\\n", "1", "");
  /* For each unionized assertion label for which a single permutation has been
     chosen (every label in unionizedAssertionLabels.txt), unjoin the unionized
	 and non-unionized assertions.

	 With the execution of this statement, for each subproof of the proof for
	 which a single unionized assertion label had to be chosen from more than
	 one permutation, a single permutation has been chosen. */

  return;

}

/* df-pickALabelPermutation() */
void pickALabelPermutation(char* proofWorksheetmv, vstring label)
{

  /* For the collection of all of the permutations of a particular *uun-labeled
     subproof, pickALabelPermutation() picks, if its exists, the single
     permutation which has mv-completed and deletes the other permutations, If
     none of the permutations mv-completed, pickALabelPermuation() picks the 0th
     permutation. If the step number of the unionized assertion of the chosen
	 subproof has a "ppp" suffix, that suffix is deleted. */

  void tag(char* iofile, char* begstr, char* endstr, char* startmatch, char* sn, char* endmatch, char* en);
  void match(char* iofile, char* matchstr, char* YorN);
  void addMTMF(char* iofile, char* begstr, char* endstr, char* matchstringT,
               char* matchstringF);
  void addMTLast(char* iofile, char* begstr, char* endstr, char* matchstring);
  void substitute(char* iofile, char* oldstring, char* newstring, char* occurrence, char* matchstring);
  void delete(char* iofile, char* startstr, char* endstr);
  /* Declare functions used by pickALabelPermutation(). */

  addMTMF(proofWorksheetmv, "", "@@@", label, "$$$");
  /* End-tag with "@@@" every permutation of label for each instance of label
     for which a permutation has not yet been picked.

	 Each line of proofWorksheetmv in which "%%%" occurs consists of 2 steps
	 joined together, a unionized assertion and its corresponding non-unionized
	 assertion. The joint between the two steps is "%%%". When a permutation of
	 a label is picked, the "%%%" joint is changed to "$$$". Thus, each
	 <unionized assertion, non-unionized assertion> pair (on a single line) is
	 a permutation not yet picked if it has a "%%%" joint and is the chosen
	 single permutation if it has a "$$$" joint. */

  addMTMF(proofWorksheetmv, "", "!!!", "@@@", "ppp");
  /* End-tag with "!!!" every instance of the 0th permutation of label not
     previously picked.

	 The permutation of a uun*-labeled unification assertion without a
	 "ppp"-suffixed step number is the 0th permutation. Each ff* label is
	 replaced by the 0th permutation of its corresponding uun* label when
	 substituteLabels() was executed. */

  tag(proofWorksheetmv, "", "&&&", "!!!", "1", "!!!", "1");
  /* End-tag with "&&&" the first instance of the 0th permutation of label. */

  substitute(proofWorksheetmv, "!!!&&&", "", "1", "");
  /* Remove the "!!!" of the first instance of the 0th permutation of label
     not yet picked. All other instances of the 0th permutation of label not
	 picked remain "!!!"-tagged. */

  tag(proofWorksheetmv, "", "&&&", "!!!", "1", "qed:", "1");
  /* End-tag with "&&&" the second instance of the 0th permutation of label
     not yet picked and all lines of the proof below it. */

  substitute(proofWorksheetmv, "@@@", "", "1", "&&&");
  /* Un-"@@@"-tag all not-yet-picked permutations of label not a first intance.
     Each permutation of the first not-yet-picked instance of label and only
	 those are "@@@"-tagged. */

  substitute(proofWorksheetmv, "&&&", "", "1", "");
  substitute(proofWorksheetmv, "!!!", "", "1", "");
  /* Remove all "&&&" and "!!!" tags. At this point all of the permutations of
     the first instance of label are "@@@"-end-tagged and only those are so
	 end-tagged. "@@@" is the end-tag in the proofWorksheetmv. */

  addMTMF(proofWorksheetmv, "", "&&&", "@@@", ": ");
  /* "&&&"-tag each permutation of the first not-yet-picked instance of label
     which mv-completes its subproof. */

  tag(proofWorksheetmv, "", "^^^", "&&&", "1", "&&&", "1");
  /* "^^^"-tag the first permutation of the first not-yet-picked instance of
     label which mv-completes its subproof. */

  addMTMF(proofWorksheetmv, "", "!!!", "@@@", "ppp");
  /* "!!!"-tag the 0th permutation of the first not-yet-picked instance of
     label. */

  substitute(proofWorksheetmv, "&&&", "", "1", "");
  /* Remove all "&&&" tags. The remaining end tags are "!!!", which
     identifies the 0th permutation of the first not-yet-picked instance of
	 label, and "^^^", if it exists, which identifies the first permutation of
	 the first not-yet-picked instance of label which mv-completes its
	 subproof, if it exists, and "@@@". "^^^" and "!!!" may or may not
	 identify the same permutation (in which case both tags would be on the
	 same line). */

  substitute(proofWorksheetmv, "^^^", "!!!", "1", "");
  /* "!!!" now identifies the first permutation which mv-completes
     the subproof or(incl.) the 0th permutation. */

  addMTLast(proofWorksheetmv, "", "&&&", "!!!");
  /* "&&&" tags the permutation to be chosen. The permutation which
     mv-completes the subproof, if it exists, has precedence over the 0th
	 permutation. If it exists, it will be "&&&"-tagged. If it does not exist,
	 the 0th permutation will be "&&&"-tagged. If it exists, the permutation
	 which mv-completes the subproof is either below the 0th permutation
	 or(excl.) is the 0th permutation. addMTLast() tags the lowest line
	 containing "!!!".

	 The objective is to choose one and only one of the permutations which
	 mv-completes the subproof. If none of the permutations mv-complete the
	 subproof, then at least one of them must be chosen. Then, the 0th
	 permutation is arbitrarily chosen. */

  substitute(proofWorksheetmv, "!!!", "", "A", "");
  /* Remove all "!!!" tags. */

  substitute(proofWorksheetmv, "@@@", "", "1", "&&&");
  /* With this substitution all permutations of the first instance of label not
     chosen and only those are identified by "@@@". */

  match(proofWorksheetmv, "@@@", "N");
  /* For the first instance of label for which a single permutation was not
     previously chosen, delete all permutations of the joined unionized and
	 non-unionized assertion steps except the newly chosen one. */

  substitute(proofWorksheetmv, "%%%", "%%%#*#*#*", "1", "&&&");
  delete(proofWorksheetmv, "#*#*#*", "ppp");
  /* If the chosen permutation is not the 0th permutation, remove the
     "<permutation number>ppp" prefix of the step number its unionized
     assertion. If the chosen permutation is the 0th permutation, then this
	 statement will have no effect.

	 It is necessary to remove the "<permutation number>ppp" prefix of step
	 number of the chosen permutation because, unless its the qed step, the
	 unionized assertion of label's subproof is a hypothesis step of some
	 other proof step(s). If the step number was changed, then it would have
	 been necessary to also change it in the hypothesis field of each step for
	 which it is a hypothesis step. */

  substitute(proofWorksheetmv, "#*#*#*", "", "1", "");
  /* If the chosen permutation is the 0th permutation, "#*#*#*" will not be
     deleted because there exists no "ppp". Then, the "#*#*#*" will be removed
     with this statement. If the chosen permutation is not the 0th permutation
     this statement will have no effect. */

  substitute(proofWorksheetmv, "%%%", "$$$", "1", "&&&");
  /* Change the joint of the chosen permutation from "%%%" to "$$$" to identify
     this unionized / non-unionized assertion pair as a chosen permutation. */

  substitute(proofWorksheetmv, "&&&", "", "1", "");
  /* Having eliminated all permutations except the chosen permutation, remove
     its "&&&" tag. */

  return;

}

/* df-completeTheoremSteps() */
void completeTheoremSteps(char* proofWorksheet)
{

  /* Attempt to complete each theorem step in proofWorksheet. This includes
     generating unification theorem permutations for subproofs for which
	 an attempt is made to complete by the unification theorem means if the
	 initially generated unification theorem does not unify with a theorem
	 in set.mm and if the subproof has 2, 3, or 4 hypothesis steps. */

  void mmj2Unify(char* proofWorksheet, char* mmj2BatFile);
  void substituteLabels(char* proofWorksheet, char* labelMapping, int c);
  void substitute(char* iofile, char* oldstring, char* newstring,
                  char* occurrence, char* matchstring);
  void substituteMTMT(char* iofile, char* oldstring, char* newstring,
                      char* occurrence, char* matchstring1, char* matchstring2);
  void stepprover(char* proofWorksheet, char* labels);
  void addMT(char* iofile, char* begstr, char* endstr, char* matchstring);
  void addMTMF(char* iofile, char* begstr, char* endstr, char* matchstringT,
               char* matchstringF);
  void add(char* iofile, char* begstr, char* endstr);
  void copy(char* inpfiles, char* outfile);
  void tag(char* iofile, char* begstr, char* endstr, char* startmatch,
           char* sn, char* endmatch, char* en);
  void delete(char* iofile, char* startstr, char* endstr);
  void match(char* iofile, char* matchstr, char* YorN);
  void pickUnifThmPermutations(char* proofWorksheet, char* inputFile);
  void removeDecoupledSteps(char* proofWorksheet);
  void removeATypeOfLabel(char* proofWorksheet, char* colonlabeltype);
  /* declare functions used by completeTheoremSteps(). */

  if ( strcmp(argv2,"onlyShortScripts") == 0 || strcmp(argv2,"scripts") == 0 ) mmj2Unify(proofWorksheet, "unifyFF.exe");
  else mmj2Unify(proofWorksheet, "mmj2FalseDeductions.bat");
  /* mmj2-unify the input proofWorksheet to obtain false deduction unifications
     False deductions are identified by their ff* labels. The loaded files
	 containing the reference theorems to be unified with are set.mm and
	 fd.txt.

	 The purpose of the mmj2Unify() call is to generate type ffunifthm labels
	 for each eel deduction's unification theorem for which unification theorem
	 permutations are to be generated. It is assumed that the only type ff
	 labels that will be generated are of type ffunifthm. proofWorksheet is
	 now in conventional notation so most deductions in fd.txt will not unify
	 with any proof step for this reason. For those fd.txt deductions in
	 conventional notation, it is assumed that any that would have unified
	 have already unified and have been replaced by set.mm labels. It is
	 assumed no type ffutperm label will be generated. */

  add(proofWorksheet, "@@@", "");
  substitute(proofWorksheet, " |- ", "!!! |- ", "1", ":ffunifthm");
  substitute(proofWorksheet, "!!! |- ", " |- ", "1", "@@@d");
  delete(proofWorksheet, "ffunifthm", "!!!");
  substitute(proofWorksheet, "@@@", "", "1", "");
  /* Each step temporarily type ffunifthm-labeled is the unification theorem of
     some subproof. Generally, there may exist theorem steps which are not
	 unification theorems. Distinguishing between a theorem step of the proof
	 not a unification theorem and a unification theorem theorem step, a
	 unification theorem is a theorem which is generated by completeusersproof
	 to attempt to complete a subproof by the unification theorem means. A
	 theorem step not a unification theorem is a theorem step of the User's
	 Proof which is a 1-step subproof of the User's Proof. Because it is
	 written by the User and not automatically generated by completeusersproof
	 its step number does not have the prefix "d". In contrast, a unification
	 theorem does have the prefix "d".

	 The last mmj2Unify() call labels all theorem steps of the form of one of
	 the 4 false theorems of type ffunifthm. These statements remove the label
	 from the type ffunifthm-labeled theorem steps not a unification theorem.
	 The purpose of type ffunifthm labels is to identify unification theorems
	 for which unification theorem permutations will be generated.
	 Non-unification theorem theorems are not to have permutations generated.
	 While this could be done, the present version of completeusersproof does
	 not. Moreover, if it was done, the present version of completeusersproof
	 would run incorrectly for some proofs. For example, if a proof is only
	 a single step which is a theorem of type ffunifthm not a unification
	 theorem, an error would be caused when completeusersproof adds a "ppppp"
	 prefix to the step number of each theorem for which permutations are to be
	 generated because then there would be no proof step with the step number
	 "qed", which invalidates the mmj2 Proof Worksheet. Another error would
	 occur if an assertion which has the theorem as one of its hypotheses is
	 not on the line immediately below the theorem (which is never the case for
	 unification theorems). Then the "ppppp" prefix will not be added to the
	 theorem's step number instance contained in the hypothesis field of the
	 assertion. The error would be that the step number in the assertion's
	 hypothesis field is not a step number of a proof step of the proof. */

  substituteLabels(proofWorksheet, "mapFfToEelUunNumPermFfsmv.txt", 12);
  /* Replace each type ffunifthm label in the proof with its corresponding type
     ffutperm label. */

  if ( strcmp(argv2,"onlyShortScripts") == 0 || strcmp(argv2,"scripts") == 0 ) mmj2Unify(proofWorksheet, "unifyFF.exe");
  else mmj2Unify(proofWorksheet, "mmj2FalseDeductions.bat");
  /* mmj2-unify proofWorksheet with fd.txt loaded. The purpose of this
     mmj2Unify() is to generate the unification theorem permutations from
	 the unification theorems of the eel deductions labeled with type
	 ffutperm labels. The mmj2Unify() happens to generate type ffunifthm labels
	 for the unification theorem permutations. These will be removed with
	 the next statement. It is necessary to remove these before stepprover() is
	 executed because stepprover() has mmj2Unify() calls on proofWorksheet with
	 only set.mm loaded. Upon mmmj2 unification error E-PA-0348 will be thrown
	 citing an invalid label(s) in proofWorksheet. The invalidity is due to the
	 fact that type ff labels are not in set.mm. Similarly, type ffutperm
	 labels must be removed from proofWorksheet. They will be removed later
	 (but before the stepprover() call) because, at this point, they are
	 needed. */

  removeATypeOfLabel(proofWorksheet, ":ffunifthm");
  /* As per the comments for the last statement. */

  substitute(proofWorksheet, ",", "@@@", "A", ":ffutperm");
  substitute(proofWorksheet, "\\n", "$$$", "1", ":ffutperm");
  substitute(proofWorksheet, " |- ", "\\n", "2", ":ffutperm");
  substitute(proofWorksheet, ",", ",ppppp", "4", ":ffutperm");
  addMTMF(proofWorksheet, "", "!!!", ":ffutperm", "ppppp");
  substituteMTMT(proofWorksheet, ",", ",ppppp", "3", ":ffutperm", "!!!");
  substitute(proofWorksheet, "!!!", "", "1", "");
  addMTMF(proofWorksheet, "", "!!!", ":ffutperm", "ppppp");
  substituteMTMT(proofWorksheet, ",", ",ppppp", "2", ":ffutperm", "!!!");
  substitute(proofWorksheet, "!!!", "", "1", "");
  addMTMF(proofWorksheet, "", "!!!", ":ffutperm", "pppp");
  substituteMTMT(proofWorksheet, ",", ",ppppp", "1", ":ffutperm", "!!!");
  substitute(proofWorksheet, "!!!", "", "1", "");
  substitute(proofWorksheet, "@@@", ",", "A", "");
  addMT(proofWorksheet, "ppppp", "", ":ffutperm");
  /* Join each type eel deduction's unification theorem having a label of type
     ffutperm with its corresponding assertion step, with "$$$" as the joint.
	 The type eel deduction's unification theorem is on the left side. This
	 step number is the last step number in the hypothesis field of the
	 assertion. Add the prefix "ppppp" to it in the hypothesis field of the
	 assertion.Add the prefix "ppppp" to the step number of each type eel
	 deduction's unification theorem labeled with a label of type ffutperm.

	 In order to add the "ppppp" prefix to the last element of the hypothesis
	 field of an eel deduction's assertion step it is necessary to split the
	 assertion step at the point of the beginning of the wwf, temporarily
	 removing the wwf from the line on which the assertion step's hypothesis
	 field will be edited. The reason this is necessary is because the wff may
	 contain some ",". The algorithm for "ppppp"-prefixing the last element of
	 the assertion's hypothesis field requires that the only occurences of ","
	 on the line occur within the assertion's hypothesis field. The 3rd
	 statement splits the assertion step's wff from its remaining portion.

	 Each type eel deduction has as its last hypothesis a unification theorem.
	 Except for 1-hypothesis type eel deductions, there is more than 1
	 unification theorem permutation which may be used. However, a 1-hypothesis
	 type eel deduction may have a unification theorem whose antecedent has 2
	 or 3 conjuncts or a left-nested 4-conjunct antecendent. These are
	 possible if the conclusion of the single hypothesis is of one of those
	 forms. Then the unification theorem would have a type ffutperm label and
	 unification permutations would be generated. Therefore, the strategy of
	 completeusersproof is to prefix the last element of the assertion's
	 hypothesis field with "ppppp". It would be invalid to assume the eel
	 deduction whose unification theorem has an n-conjunct antecedent has an
	 n+1 element hypothesis field and to add a "pppp" prefix to the (n+1)th
	 element. Then no "ppppp" prefix would be added if the hypothesis field
	 has fewer elements.

	 The above statements add the "ppppp" prefix to the last element of the
	 assertion's hypothesis field without assuming how many elements the
	 hypothesis field has, except to assume it does not have more than 5
	 elements. If it did, its unification theorem would not have a
	 ffutperm2, ffutperm3, or ffutperm4 label, which are the only type
	 ffutperm labels in fd.txt. If an eel deduction has a 2-element hypotheis
	 field and its unification theorem has more than a 0-conjunct antecedent,
	 then the conclusion of the single hypothesis step is a conjunction. For
	 a n-element assertion hypothesis field, the eel deduction's unification
	 theorem will always have greater than or equal to n-1 conjuncts. A
	 unification theorem with an antecedent with more than 4 conjuncts will
	 not unify with any type ffunifthm false theorem in fd.txt and no
	 unification permutations will be generated. A 4-conjunct unification
	 theorem antecedent corresponds to a eel deduction whose assertion has
	 less than or equal to 5 elements. Therefore, when it is required to
	 "ppppp"-prefix the step number of the last element of an assertion's
	 hypothesis field, the last element is at most the 5th element and
	 ",ppppp" replaces the 4th "," on the line, which is the 4th "," on the
	 right side of the "$$$" joint. There are no "," on the left side of the
	 joint. Each was temporarily replaced by "@@@".

	 For each iteration the "!!!" end tag's existence notifies that no
	 ",ppppp" has yet replaced any "," in the assertion's hypothesis field.
	 After each ",ppppp" substitution attempt is made, the "!!!" tag is
	 deleted so that whether or not a ",ppppp" substitution was made with
	 the last substitute statement can be evaluated and recorded for use
	 by the next iteration. The iterations progress from last "," to first.
	 This is done because it is unknown if an eel assertion's hypothesis
	 field has 2, 3, 4, or 5 elements.

	 After the last element of the hypothesis field of each eel assertion
	 corresponding to a type ffutperm-labeled unification theorem has been
	 "ppppp"-prefixed, each "@@@" in the hypothesis field of the unification
	 theorem is restored to be "," and the "ppppp" prefix is added to the
	 step number of the eel deduction's unification theorem step.

	 A eel deduction's unification theorem is itself a particular unification
	 theorem permutation. When all unficiation theorem permutations are
	 generated, the eel deduction's unification theorem is a duplicate of one
	 of them.

	 For each subproof for which an attempt is made to complete it by the
	 unification theorem means, completeusersproof tries to unify each
	 unification theorem permutation with a theorem in set.mm. For each
	 unification theorem permutation which does not unify with a theorem in
	 set.mm, stepprover() is applied to attempt to 2-step prove it. The only
	 exception to this is if the type eel deduction's unification theorem
	 unifies with a theorem in set.mm, then completeusersproof will not attempt
	 to complete any other unification theorem permutations. Unification
	 permutations will not be tried if there are more than 24 of them for a
	 given subproof. This is the case if the eel deduction's unification
	 theorem has an antecedent with more than 4 conjuncts. It the antecedent
	 has only a single element, then there is only a single unification theorem
	 permutation. In that case, the eel deduction's unification theorem is used
	 and what would be its duplicate is not generated.

	 This revision of the step number of each type eel deduction's unification
	 theorem labeled with a label of type ffutperm is so that each of these
	 steps is identifiable by the prefix "@@@ppppp". The "@@@" is added later
	 using addMT(). The type eel deduction unification theorems are
	 distinguished from their corresponding assertions as "@@@ppppp" does not
	 occur in the assertions. They cannot be identified by the matchstring
	 "ffutperm" because each label of this type must be removed prior to
	 calling stepprover() because a label of type ffutperm is not a set.mm
	 label. Near the end of stepprover() each unfication deduction label which
	 2-step proves a theorem step is added to the theorem step. Then
	 mmj2Unify() is applied to proofWorksheet to generate the 2-step proofs.
	 That mmj2Unify() does not load fd.txt. If the type ffutperm labels
     occurred in proofWorksheet at that time, mmj2 would throw an error due to
     the invalid labels which are not in set.mm. The type ffutperm cannot be
     re-added after stepprover() has run in the same way they were added before
     stepprover() was run because each eel deduction's unification theorem for
     which permutations were generated now has a nonempty hypothesis field and
     therefore cannot unify with any type ffunifthm false theorem, which has no
     hypotheses. It would be possible to save the information identifying which
     steps were initially labeled with type ffunifthm labels then use that
     information to identify those steps after stepprover() has run. That has
     been done by prefixing the step numbers with "ppppp". The renaming of each
     step numbers requires that it is also be renamed in the hypothesis field
     in which it occurs. Otherwise, when mmj2Unify() is applied to
	 proofWorksheet to complete the 2-step proofs, mmj2 will throw an error
     due to some step numbers in some hypothesis fields not being a step number
     of a proof step. There may be other ways of retaining this information.
	 This is the way that was chosen.

	 In order for completeusersproof to run as intended, the User's Proof must
	 not contain any step number with the prefix "ppppp". mmj2Unify() will not
	 generate any step with this prefix.

	 It is explained in pickUnifThmpermutations() why it is necessary to
	 identify those steps of proofWorksheet which are a unification theorem
	 of some type eel deduction of some subproof for which unification theorem
	 permutations were generated. */

  substitute(proofWorksheet, "\\n", " |- ", "1", ":ffutperm");
  substitute(proofWorksheet, "$$$", "\\n", "1", "");
  removeATypeOfLabel(proofWorksheet, ":ffutperm");
  /* Join the wwf with the remainder of each split assertion step. Split each
     pair of joined unification theorem-assertion steps. Remove the "$$$"
	 joint. Remove the label from each type eel deduction's unification theorem
	 labeled with a label of type ffutperm. Earlier, all type ff labels had
	 been removed except the type ffutperm labels, which were needed. Now they
	 are no longer needed and are removed. With their removal,
	 all type ff labels have been removed. From this point onward no type ff
	 labels shall be generated because no mmj2Unify() call after this point has
	 fd.txt loaded. This is true inside completeTheoremSteps() and outside of
	 it, when completeusersproof() resumes. Therefore, after this point, there
	 shall never be a need to remove type ff labels. This is true for any value
	 of argv[1]. At this point, whether argv[1] has the value
	 "runCompleteusersproofsmv", "runCompleteusersproofmv", "runStepprover", or
	 the default value, all that remains before the completion of execution of
	 completeusersproof is trying to 2-step prove the theorems in
	 proofWorksheet, generated or original. */

  stepprover(proofWorksheet, "labels.txt");
  /* stepprover() will first mmj2-unify proofWorksheet. stepprover() then
     attempts to 2-step prove all theorem steps (assertion steps with no
	 hypotheses) which have not unified with a theorem in set.mm. Among the
	 theorem steps which stepprover() attempts to 2-step prove are all of the
	 unification theorem permutations generated for each eel deduction's
	 unification theorem having a 2-, 3-, or 4-conjunct antecedent. */

  copy(proofWorksheet, "noncompletingStepNums.txt");
  match("noncompletingStepNums.txt", ":: ", "Y");
  delete("noncompletingStepNums.txt", ":", "");
  /* noncompletingStepNums.txt is a list initially containing each labeless
     step in proofWorksheet with an empty hypothesis field. Each is an
	 uncompleted theorem. Included in this list is all unificiation theorem
	 permutation steps in proofWorksheet which did not complete after being
	 processed by stepprover(). This list is reduced to a list of the step
	 numbers of these uncompleted theorems. noncompletingStepNums.txt is
	 used by pickUnifThmPermutations(). */

  pickUnifThmPermutations(proofWorksheet, "noncompletingStepNums.txt");
  /* For each subproof for which unification theorem permutations are
     generated, for each unification theorem permutation which did not unify
	 with a theorem in set.mm or was not 2-step proven by stepprover(),
	 remove its step number from the hypothesis field of the eel deduction's
	 unification theorem. Then remove all remaining step numbers in that
	 hypothesis field, if any, except for the first one. If none remain, that
	 hypothesis field is empty and the unification theorem for the
	 corresponding subproof is not completed. */

  substitute(proofWorksheet, "ppppp", "", "1", "");
  /* Remove the "ppppp" prefix from the step number of each type eel
     deduction's unification theorem for which unification theorem permutations
	 were generated, where it occurs at the beginning of the
	 type eel deduction's unification theorem step and where it occurs in the
	 hypothesis field of the type eel deduction's unification theorem's
	 assertion. */

  removeDecoupledSteps(proofWorksheet);
  removeDecoupledSteps(proofWorksheet);
  /* Remove any proof steps not part of the proof tree. Each such step is
     either directly decoupled or(excl.) an indirectly decoupled coupled
	 proof step.

	 The first statement removes all directly decoupled proof steps. The
	 second statement removes each proof step which was indirectly decoupled
	 prior to execution of the first statement and became directly decoupled
	 after execution of the first statement. Further explantion and
	 definitions of the terms used herein may be found in the comments of the
	 removeDecoupledSteps() function definition. */

  if ( strcmp(argv2,"onlyShortScripts") == 0 || strcmp(argv2,"scripts") == 0 ) mmj2Unify(proofWorksheet, "unify.exe");
  else mmj2Unify(proofWorksheet, "mmj2StepProver.bat");
  /* Execute mmj2Unify() with only set.mm loaded to complete each completable
     subproof in case it has not been already completed. If all subproofs are
	 completed, a RPN Metamath proof is generated. */

  return;

}

/* df-pickUnifThmPermutations() */
void pickUnifThmPermutations(char* proofWorksheet, char* inputFile)
{

  /* For each subproof for which unification theorem permutations are
     generated, for each unification theorem permutation which did not unify
	 with a theorem in set.mm or was not 2-step proven by stepprover(),
	 pickUnifThmPermutations() removes its step number from the hypothesis
     field of its type eel deduction's unification theorem. It then removes
	 all remaining step numbers in that hypothesis field, if any, except for
	 the first one. If none remain, that hypothesis field is empty and the
	 unification theorem for the corresponding subproof is not completed. */

  void add(char* iofile, char* begstr, char* endstr);
  void substitute(char* iofile, char* oldstring, char* newstring,
                  char* occurrence, char* matchstring);
  void delete(char* iofile, char* startstr, char* endstr);
  /* Declare functions used by pickUnifThmPermutations() */

  vstring stepNumber = (vstring)malloc(100);

  FILE* fp = fopen(inputFile, "r");

  add(proofWorksheet, "@@@", "");
  /* Each type eel deduction's unification theorem is identified by the
     matchstring "@@@ppppp". "@@@" is temporarily added to distinguish
	 between type eel deduction unification theorems and their corresponding
	 assertions, which also contain "ppppp", but do not contain "@@@ppppp". */

  while(fscanf(fp, "%s", stepNumber) != EOF)
  /* Read one stepNumber from inputFile each iteration of this loop. If it is
     contained in the hypothesis field of a type eel deduction's unification
	 theorem, then delete it from that hypothesis field.

	 If the stepNumber is in a hypothesis field it may take the form
	 ":<stepNumber>,", ",<stepNumber>,", ",<stepNumber>:", or(excl.)
	 ":<stepNumber>:". Each substitute() statement has a duplicate to avoid
	 skipping it.

	 Why is the "@@@ppppp" matchstring required in the following statements?
	 Any step number without a "d" prefix could be removed from inputfile to
	 avoid the incorrect removal of those steps from some hypothesis fields.
	 The "pppp" step numbers won't be in inputfile because these steps don't
	 have an empty hypothesis field. An eel deduction's unification theorem
	 have a 1-conjunct antecedent or an antecendent of greater than 4
	 conjuncts will have no unification permutations generated. It will have
	 an empty hypothesis field. If stepprover() does not complete it it will
	 not have a label. It has a step number with a "d" prefix. It qualifies
	 as an element of inputfile and would be incorrectly removed from the
	 assertion of its eel deduction. This alone is sufficient for the need to
	 identify the proof steps for which elements of its hypothesis field may be
	 removed. */

  {

    substitute(proofWorksheet, cat(":", stepNumber, ",", NULL), ":", "1", "@@@ppppp");
    substitute(proofWorksheet, cat(":", stepNumber, ",", NULL), ":", "1", "@@@ppppp");
    substitute(proofWorksheet, cat(",", stepNumber, ",", NULL), ",", "1", "@@@ppppp");
	substitute(proofWorksheet, cat(",", stepNumber, ",", NULL), ",", "1", "@@@ppppp");
    substitute(proofWorksheet, cat(",", stepNumber, ":", NULL), ":", "1", "@@@ppppp");
	substitute(proofWorksheet, cat(",", stepNumber, ":", NULL), ":", "1", "@@@ppppp");
    substitute(proofWorksheet, cat(":", stepNumber, ":", NULL), "::", "1", "@@@ppppp");
	substitute(proofWorksheet, cat(":", stepNumber, ":", NULL), "::", "1", "@@@ppppp");

  }

  fclose(fp);
  /* Close the file pointer. */

  let(&stepNumber, "");
  /* Having finished using the values contained in stepNumber, the memory of
     this variable is deallocated to avoid the risk of possible memory
	 leaks. */

  substitute(proofWorksheet, ":", "!!!:", "2", "@@@ppppp");
  delete(proofWorksheet, ",", "!!!");
  substitute(proofWorksheet, "!!!", "", "1", "");
  substitute(proofWorksheet, "@@@", "", "1", "");
  /* If the hypothesis field of the eel deduction's unification theorem
     contains more than 1 step number, remove all but the first one.
	 Afterwards, the "@@@" beginning tag is no long needed and is deleted. */

  return;

}

/* df-removeDecoupledSteps() */
void removeDecoupledSteps(char* proofWorksheet)
{

  /* Remove all decoupled proof steps from proofWorksheet. That is to remove,
     except for the qed step, each proof step not contained in some hypothesis
	 field of some proof step in proofWorksheet. A decoupled proof step is one
	 which is extraneous to the proof. A completed proof having decoupled proof
	 steps has a corresponding RPN proof which does not contain the decoupled
	 proof steps because they are not part of the proof tree.

	 Some proof steps may be indirectly decoupled coupled proof steps. The step
	 number of such a step is contained in some hypothesis field of some
	 decoupled step(s). It is not contained in any hypothesis field of any
	 coupled step. Such indirectly decoupled coupled proof steps are not part
	 of the proof tree. Because they are coupled, they are not removed by
	 removeDecoupledSteps(). After removeDecoupledSteps() is executed once all
	 indirectly decoupled coupled proof steps become directly decoupled because
	 any proof step containing the step number of such a step in its hypothesis
	 field is removed by removeDecoupledSteps(). If removeDecoupledSteps() is
	 then executed a second time, all proof steps not in the proof tree will
	 be removed. Therefore, in general, whenever removeDecoupledSteps() is
	 called, it should be called twice.

	 It is not necessary to remove decoupled steps from a proof in order to
	 generate an RPN proof. The decoupled steps are removed only so that the
	 proof will not be cluttered with superfluous steps. */

  void copy(char* inpfiles, char* outfile);
  void match(char* iofile, char* matchstr, char* YorN);
  void add(char* iofile, char* begstr, char* endstr);
  void addMT(char* iofile, char* begstr, char* endstr, char* matchstring);
  void substitute(char* iofile, char* oldstring, char* newstring,
                  char* occurrence, char* matchstring);
  void delete(char* iofile, char* startstr, char* endstr);
  void unduplicate(char* iofile);
  /* Declare functions used by removeDecoupledSteps() */

  copy(proofWorksheet, "hypFieldSteps.txt");
  add("hypFieldSteps.txt", "@@@", "");
  match("hypFieldSteps.txt", "@@@*", "N");
  match("hypFieldSteps.txt", " |- ", "Y");
  delete("hypFieldSteps.txt", "|-", "");
  match("hypFieldSteps.txt", "::", "N");
  delete("hypFieldSteps.txt", "", ":");
  delete("hypFieldSteps.txt", ":", "");
  substitute("hypFieldSteps.txt", ",", "\n", "A", "");
  add("hypFieldSteps.txt", "@@@", ":");
  unduplicate("hypFieldSteps.txt");
  /* Create hypFieldSteps.txt, which contains every step number contained in
     some hypothesis field of some step in proofWorksheet.

	 The first match() statements deletes any line that is a comment line. No
	 comment line is a proof step and therefore contains no applicable
	 applicable hypothesis field. The second match() statement removes all
	 lines not a proof step. The third match() statement removes any proof step
	 with an empty hypothesis field. The first delete() statement deletes the
	 wff of each proof step to avoid the possibility that a proof step without
	 an empty hypothesis field is deleted because that step's wff contains
	 "::". The delete() statement deletes all of a proof step except the
	 contents of its hypothesis field. The substitute() statement replaces each
	 "," separating each pair of step numbers with "\n", which splits the line
	 at each "," so that each line contains a single step number. The last
	 add() statement prefixes each step number with "@@@" and suffixes it with
	 ":", avoiding the need for cat() in the addMT() statement of the loop.

	 Some proof steps may occur in in more than once in some hypothesis
	 field(s) or in more than one hypothesis field. Then hypFieldSteps.txt will
     contain duplicate lines. This would cause some proof steps in
     proofWorksheet to be "!!!"-tagged more than once which would result in
     unintended behavior. This possibility is eliminated by removing all
     duplicates of a proof step in hypFieldSteps.txt. The unduplicate()
     statement does this.

     These statements do not create the final hypFieldSteps.txt because
     hypothesis steps have not yet been taken into account. The next three
     statements take hypothesis steps into account.  */

  copy("hypFieldSteps.txt", "hypFieldSteps2.txt");
  substitute("hypFieldSteps2.txt", "@@@", "@@@h", "1", "");
  copy("hypFieldSteps.txt,hypFieldSteps2.txt", "hypFieldSteps.txt");
  /* If a step is a hypothesis of the theorem or(excl.) deduction being proved,
     its step number will have the prefix "h" for its occurrence at the
	 beginning of the step. Its step number will not have this prefix for any
	 occurrence in any hypothesis field.

	 A stepNumber in the original hypFieldSteps.txt is the correct matchstring
	 for any step not a hypothesis. It is not the correct matchstring for any
	 step which is a hypothesis because it does not have the prefix "h". With
	 these 3 statements the list of step numbers of the original
	 hypFieldSteps.txt is duplicated and the prefix "h" is added to each
	 duplicate. The resulting file overwrites the original hypFieldSteps.txt.
	 For each step of the proof the new hypFieldSteps.txt contains two step
     numbers, one with the prefix "h" and one without. If the step is a
     coupled hypothesis, its stepNumber with the prefix "h" will be a matching
     string and the step will be "!!!"-tagged. Its stepNumber without the
     prefix will not be a matching string and will not generate a "!!!"-tag.
     If the step is not a hypothesis and is coupled, its stepNumber without the
     prefix will be a matching string and the step will be "!!!"-tagged. Its
     stepNumber with the prefix will not be a matching string and will not
     generate a "!!!"-tag. Each coupled step of the proof will be "!!!"-tagged
     once, whether it is a hypothesis or(excl.) not. */

  addMT(proofWorksheet, "@@@", "", " |- ");
  substitute(proofWorksheet, "@@@*", "*", "1", "");
  /* "@@@"-beginning-tag every line of proofWorksheet that is a proof step and
     only those lines.

	 The first statement will tag, among other lines, comment lines containing
	 " |- ". The second statement untags any of those tagged comment lines as
	 no comment line is a step of the proof. Every line of proofWorksheet
	 containing " |- " is a proof step or(excl.) a comment line.  */

  vstring stepNumber = (vstring)malloc(100);
  FILE* fp = fopen("hypFieldSteps.txt", "r");

  while(fscanf(fp, "%s", stepNumber) != EOF)
  /* Scan one stepNumber at a time from the file containing every step number
     in some hypothesis field of some proof step in proofWorksheet
	 (hypFieldSteps.txt) and "!!!"-tag the proof step having step number
	 stepNumber. Iterate this loop until all of the steps numbers in
	 hypFieldSteps.txt have been scanned and all proof steps not decoupled are
	 tagged. */

  {

	addMT(proofWorksheet, "!!!", "", stepNumber);
	/* "!!!"-beginning-tag the proof step whose step number is stepNumber. It
       is guaranteed that each proof step to be "!!!"-tagged will be
	   "!!!"-tagged only once because hypFieldSteps.txt contains only no
	   duplicate step numbers.

	   The skipping of statements containing cat() is known to sometimes occur.
	   Here, if cat() is used for the matchstring argument of addMT(), the
	   matchstring is sometimes not recognized, causing the unintended tagging
	   of some proof steps. This is avoided by not using cat(). The "@@@"
	   prefix and ":" suffix are added to each step number in hypFieldSteps.txt
	   so that use of cat() is not required in the addMT() statement of the
	   loop. */

  }

  fclose(fp);
  /* Close the file pointer. */

  let(&stepNumber, "");
  /* Having finished using the values contained in stepNumber, the memory of
     this variable is deallocated to avoid the risk of possible memory
	 leaks. */

  substitute(proofWorksheet, "!!!@@@", "", "1", "");
  substitute(proofWorksheet, "@@@qed:", "qed:", "1", "");
  match(proofWorksheet, "@@@", "N");
  /* These statements remove every uncoupled proof step. Previously, all proof
     steps were "@@@"-tagged. The while() loop "!!!"-tagged all coupled proof
	 steps. The first substitute() statement deletes the "@@@" tag of each
	 coupled proof step. The second substitute() statement deletes the "@@@"
	 tag of the qed step, which is the only proof step which is part of the
	 proof tree whose step number is not contained in the hypothesis field of
	 any step of the proof. The remaining "@@@"-tagged proof steps are
	 decoupled. The match() statement removes them from the proof. */

  return;

}

/* df-substituteLabels() */
void substituteLabels(char* proofWorksheet, char* labelMapping, int c)
{

  /* substituteLabals() reads data from the text file labelMapping.
     labelMapping is a table with 5 columns and any number of rows.  Each value
	 in column 1, 2, 3 or 5 is a reference theorem label.  Each value in the
	 4th column is a positive integer. The 3rd argument of substituteLabels()
	 is an integer variable intended to be 12, 13, 34, or 125. If c is 12, then
	 for all i, substitute() replaces each instance in proofWorksheet of the
	 row i column 1 label <labeli1> with the row i column 2 label <labeli2>. If
	 c is 13, for all i in the table it replaces each instance of <labeli1> in
	 proofWorksheet with <labeli3>. If c is 34, for all i it replaces each
	 instance of <labeli3> in proofWorksheet with the integer of row i
	 column 4. If c is 125, for all i it replaces each instance of <labeli1>
	 with <labeli2>, except for each step of proofWorksheet end-tagged with
	 "!!!ffsmv" <labeli1> is replaced with <labeli5>.

	 mapFfToEelUunNumPermFfsmv.txt is passed as the labelMapping argument for
	 every call of substituteLabels() in completeusersproof. For every row i,
	 <labeli1> is an type ff false deduction, <labeli2> is the unique type eel
	 label corresponding to <labeli1>, <labeli3> is the 0th permutation type
	 uun label corresponding to <labeli1>, the integer value of row i column 4
	 is the number of typ uun label permutations corresponding to <labeli1>,
	 and <labeli5> is the type ffsmv label corresponding to the <labeli1> type
	 ff false deduction. */

  vstring ffLabel = (vstring)malloc(100);
  vstring eelLabel = (vstring)malloc(100);
  vstring uunLabel = (vstring)malloc(100);
  vstring numPerm = (vstring)malloc(100);
  vstring ffsmvLabel = (vstring)malloc(100);

  FILE* fp = fopen(labelMapping, "r");

  void substitute(char* iofile, char* oldstring, char* newstring,
                  char* occurrence, char* matchstring);

  while(fscanf(fp, "%s %s %s %s %s", ffLabel, eelLabel, uunLabel, numPerm, ffsmvLabel) != EOF)
  /* This is a loop which reads from labelMapping one < old value, new value >
     ordered pair each pass. If it occurs in proofWorksheet, old value is the
	 existing label to be replaced. new value is the replacement label or
	 positive integer. If the value of the 3rd argument of substituteLabels()
	 is jk, for all i, the old value is the row i column j value and new value
	 is the row i column k value. For c == 125, the old value is the row i
	 column 1 value and the new value is the row i column 2 value unless the
	 proofWorksheet step is end-tagged with "!!!ffsmv", in which case the new
	 value is the row i column 5 value, which is a label of type ffsmv.

     The execution of the conditional expression of the while loop both
	 determines whether or not to exit the loop and reads the next pair of
	 labels from labelMapping. The next pair of labels is read each time
	 execution passes through the top of the while loop where the conditional
	 expression is evaluated. As step 1, the loop simultaneously reads the
	 next pair of values and determines whether or not to exit the loop. As
	 step 2, the next old value is replaced with its corresponding new value.
     The loop iterates for all row i, replacing each instance of old value in
	 proofWorksheet with its corresponding new value. */

  {

  if (c == 12)
  {
  substitute(proofWorksheet, cat(":", ffLabel, " ", NULL), cat(":", eelLabel, " ", NULL), "1", "");
  substitute(proofWorksheet, cat(":", ffLabel, " ", NULL), cat(":", eelLabel, " ", NULL), "1", "");
  }
  if (c == 13)
  {
  substitute(proofWorksheet, cat(":", ffLabel, " ", NULL), cat(":", uunLabel, " ", NULL), "1", "");
  substitute(proofWorksheet, cat(":", ffLabel, " ", NULL), cat(":", uunLabel, " ", NULL), "1", "");
  }
  if (c == 34)
  {
  substitute(proofWorksheet, cat(" ", uunLabel, " ", NULL), numPerm, "1", "");
  substitute(proofWorksheet, cat(" ", uunLabel, " ", NULL), numPerm, "1", "");
  }
  if (c == 125)
  {
  substitute(proofWorksheet, cat(":", ffLabel, " ", NULL), cat(":", ffsmvLabel, " ", NULL), "1", "!!!ffsmv");
  substitute(proofWorksheet, cat(":", ffLabel, " ", NULL), cat(":", ffsmvLabel, " ", NULL), "1", "!!!ffsmv");
  substitute(proofWorksheet, cat(":", ffLabel, " ", NULL), cat(":", eelLabel, " ", NULL), "1", "");
  substitute(proofWorksheet, cat(":", ffLabel, " ", NULL), cat(":", eelLabel, " ", NULL), "1", "");
  }
  /* For c = 12 or c = 13, the value saved in ffLabel is, for example, "ff22".
     But the character string to be replaced is ":ff22 ". "ff22" is transformed
	 into ":ff22 " using cat(). For c = 34, if uunLabel is "uun1" with 1
	 permutation and "uun111" with 1 permutations occurs in the
	 numberOfPermutationsList.txt, then, without cat() adding a white space
     on each side of "uun1" the "uun111" of the text file would become "111",
     which is not intended. With the cat(), no substitution to " uun111 " will
     occur because " uun1 " does not match " uun111 ". But another label on
     the list, " uun1 " will become "1", which is intended.

	 Use duplicate statements because, for unknown reasons, substitutions are
	 otherwise sometimes skipped. This unintended behavior may be correlated
	 with the arguments which are cat() return values. */

  }

 fclose(fp);
 /* Close the file pointer for labelMapping. */

 let(&ffLabel, "");
 let(&eelLabel, "");
 let(&numPerm, "");
 let(&ffsmvLabel, "");
  /* Having finished using the values contained in ffLabel, eelLabel, numPerm,
     and ffsmvLabel, the memory of these variables is deallocated to avoid the
	 risk of possible memory leaks. */

 substitute(proofWorksheet, "nulllabel", "", "1", "");
 /* Old labels are replaced with "" when mmj2Unify() will generate the new
    label. When mmj2Unify() will generate a new label, specifying the new label
    in labelMapping would create an unnecessary risk of the human error of
    mispecifying the new label. Because fscanf() will not read "" from
	labelMapping, the new labels for which "" is desired are initially
    "nulllabel", then replaced by "" with this statement. This occurs at the
    end of substituteLabels() because, at this point, all of the labels to be
    replaced have been replaced.  */

 return;

}

/* df-stepprover() */
void stepprover(char* proofWorksheet, char* labels)
{

  /* The stepprover function has the argument proofWorksheet, which is any
     valid mmj2 Proof Worksheet text file free of grammatical errors as
	 verified by mmj2, and the argument labels, which is a text file containing
	 list of 1-hypothesis deductions in set.mm, It is not necessary that the
	 proof of proofWorksheet is pre-mmj2-unified. All labels in labels must
	 name valid 1-hypothesis deductions in the version of set.mm used by the
	 mmj2 invoked by stepprover.

	 The 2-step proof for a unification theorem contained in the input Proof
	 Worksheet will not be found if the unification theorem is not a semantic
	 variation of a theorem contained in the version of set.mm used by the
	 mmj2 program invoked by the stepprover.

	 If no 2-step proof is found for a unification theorem in the input Proof
	 Worksheet, there will be no label added to that step. If there was a
	 question mark in that unification theorem's hypothesis field, the
	 stepprover will remove that question mark.

	 The output Proof Worksheet of the stepprover is identical to the input
	 Proof Worksheet except that it is mmj2-unified if the input file was not
	 and any step of the unified Proof Worksheet which is a) a theorem not in
	 set.mm, b) has not already been proved, c) is a semantic variation of a
	 theorem in set.mm, and d) is unifiable with a theorem in set.mm through a
	 deduction in labels.txt will be 2-step proved (except in the unlikely case
	 some of the below limitations below apply).

	 The stepprover has the following limitations:

	 - If the input Proof Worksheet contains duplicate unification theorems, it
	   will find a 2-step proof for only the lowest one of those processed in
	   parallel in the same duplicateUnificationThms.txt file. The label which
	   2-step proves the lowest one is added to the Proof Worksheet for that
	   unification theorem. That label will not be added to any of the other
	   duplicates of that unification theorm which were processed in parallel
	   with it (in the same duplicateUnificationThms.txt file). This is not a
	   significant limitation because, obviously, each unlabeled duplicate is
	   2-step provable using the label of the labeled duplicate. The User may
	   manually add the label to each unlabeled duplicate.

	 - It is possible that two or more unification theorems contained in the
	   input Proof Worksheet are 2-step provable and have identical hypothesis
	   step wffs for their 2-step proofs. Then, whenever more than one of these
	   unification theorems are processed in parallel (in the same
	   duplicateUnificationThms.txt file) only the lowest one will be 2-step
	   proved by the stepprover. This case should occur infrequently. It is
	   unlikely that more than one unification theorems processed in parallel
	   would share the same 2-step-proof hypothesis step.

	 - It is a remote possibility that a unification theorem in the original
	   Proof Worksheet which is not labelled on that Worksheet, is 2-step
	   proveable by 4 or more labels in set.mm. If that is the case, the
	   stepprover output may be incorrect. Because the probability of this
	   occurring is remote, this may be considered to not be a defect of the
	   stepprover. */

  void substitute(char* iofile, char* oldstring, char* newstring,
                  char* occurrence, char* matchstring);
  void add(char* iofile, char* begstr, char* endstr);
  void addMTMF(char* iofile, char* begstr, char* endstr, char* matchstringT,
               char* matchstringF);
  void copy(char* inpfiles, char* outfile);
  void tag(char* iofile, char* begstr, char* endstr, char* startmatch,
           char* sn, char* endmatch, char* en);
  void delete(char* iofile, char* startstr, char* endstr);
  void match(char* iofile, char* matchstr, char* YorN);
  /* declare functions emulating Metamath tools commands used by
     stepprover(). */

  void mmj2Unify(char* proofWorksheet, char* mmj2BatFile);

  if ( strcmp(argv2,"onlyShortScripts") == 0 || strcmp(argv2,"scripts") == 0 ) mmj2Unify(proofWorksheet, "unify.exe");
  else mmj2Unify(proofWorksheet, "mmj2StepProver.bat");
  /* mmj2-unify the input proofWorksheet in case it was not unified. */

  copy(proofWorksheet, "unifThms.txt");
  /* proofWorksheet should be a valid mmj2 Proof Worksheet.  Strictly speaking,
     only the unification theorems of the Proof Worksheet need to be valid in
	 order for the stepprover to run correctly. proofWorksheet is the output
	 file as well as one of two input files. The other input file is labels.
	 The text files unifThms.txt and duplicateUnificationThms.txt are used by
	 and are automatically created by stepprover(). The instant statement
	 creates the text file unifThms.txt and copies the file proofWorksheet onto
	 it. unifThms.txt will be edited to become a list of labels which 2-step
	 prove each unification theorem in the Proof Worksheet which is 2-step
	 proveable. */

  substitute("unifThms.txt", ":: ", ":?: ", "1", "");
  /* Each unification theorem with a space after the second ":" has no label.
     The purpose of stepprover is to find labels for those unification theorems
	 so they are "2-step proved". We start by adding a question mark to each
	 hypothesis field which is empty. We are going to identify each target
	 unification theorem which will be attempted to be 2-step proved by the
	 matchstring ":?: ".  We must add question marks because the hypothesis
	 field of some steps may be empty ("::").

	 Prior to the April 7, 2016 version of mmj2, only hypothesis fields manually
	 input by the User could be empty. With later versions, steps generated
	 by mmj2 have hypothesis fields without question marks. Question
	 marks in a hypothesis field are optional. In the case of a step which
	 unifies with a theorem (a 0-hypothesis deduction) in set.mm, the ":?:"
	 hypothesis field will prevent unification with the theorem in set.mm the wff
	 of the step would otherwise unify with. */

  match("unifThms.txt", ":?: ", "Y");
  /* Delete all steps of the original Proof Worksheet not unproven unification
     theorems. */

  substitute("unifThms.txt", " |- ", "!@!|- ", "1", "");
  delete("unifThms.txt", "", "!@!");
  /* At the point immediately before these two statements unifThms.txt contains
     only unproven unfication theorems of the original Proof Worksheet. With
	 these two statements we delete the step number and hypothesis field of
	 each of these steps. Only the unification theorem itself remains
	 (the wff). */

  char unifThm[600];
  vstring label = (vstring)malloc(30);
  vstring dlabel = (vstring)malloc(30);
  /* label and dlabel are each of type vstring rather than char* in order to
     be able to use let() to avoid possible memory leakage problems. */

  FILE* utfp = fopen("unifThms.txt", "r+");
  FILE* lfp = fopen(labels, "r+");
  FILE* dutfp;
  FILE* fp;
  /* Declare pointers to the used text files. fopen() creates these pointers.
     fscanf() requires the pointers as the argument refering to the
	 corresponding text file. */

  int utmax = 8;
  if ( strcmp(argv3,"1") == 0 ) utmax = 1;
  if ( strcmp(argv3,"2") == 0 ) utmax = 2;
  if ( strcmp(argv3,"3") == 0 ) utmax = 3;
  if ( strcmp(argv3,"4") == 0 ) utmax = 4;
  if ( strcmp(argv3,"5") == 0 ) utmax = 5;
  if ( strcmp(argv3,"6") == 0 ) utmax = 6;
  if ( strcmp(argv3,"7") == 0 ) utmax = 7;

  /* Command line argument specifying value of utmax. The default value of
     utmax is 8. The conditional statements override the default value.
	 The maximum permissible value of utmax is 8. argv3 is the 3rd command
	 line argument. */

  int i = utmax;
  int j;
  /* Declare i, j, and utmax. utmax is the maximum number of unification
     theorems any duplicateUnificationThms.txt file may have. The larger the
	 value of utmax the greater the required Sleep statement durations for the
	 unifylong.ahk AutoHotkey script. The smaller the value of utmax the
	 greater the number of times the unifylong.ahk mmj2-unification must be
	 executed. The optimal value of utmax is that value which minimizes total
	 run time.

	 If a User's Proof has greater than utmax 0-hypothesis steps not unifiable
	 with a theorem in set.mm these steps will be partitioned into cells with
	 utmax steps in each cell except the last, which will contain 1 to utmax
	 steps. The steps in each cell will be processed is parallel, with
	 one duplicateUnificationThms.txt file per cell.

	 For larger values of utmax the duplicateUnificationThms.txt Proof
	 Worksheet has so many steps that pressing GUI buttons using the
	 unifylong.exe AutoHotkey script may be unreliable. Being able to change
	 the value of this variable affords the User an opportunity to reduce the
	 maximum number of proof steps of duplicateUnificationThms.txt in order
	 to increase the reliability of mmj2 unification using the unifylong.exe
	 script.

	 It is necessary to limit the number of 0-hypothesis steps not unifiable
	 with a theorem in set.mm processed in parallel because for some large
	 value mmj2 will be unable to process the duplicateUnificationThms.txt
	 Proof Worksheet because the proof will have too many steps. The
	 maximum value of utmax for completeusersproof has been arbitarily
	 specified to be 8.  It is known that a duplicateUnificationThms.txt file
	 with 8 x 375 steps is within the capacity of mmj2 to mmj2 unify using
	 the batch test run parm. It is unknown how much longer a Proof Worksheet
	 mmj2 can successfully unify. */

  copy(proofWorksheet, "duplicateUnificationThms.txt");
  /* The sole purpose of this statement is to create the text file
     duplicateUnificationThms.txt. Its contents is deleted by the next
	 statement. Otherwise it would have been necessary for the User to manually
	 create the file. copy() creates an output file if it does not exist. */

  while(i == utmax)
  /* Initially, i = utmax and the expession of the while() is true. The maximum
     number of unification theorems the while-loop nested inside of the instant
	 one may read is utmax. If utmax additional unification theorems were read
	 from unifThms.txt for the last iteration of the instant while-loop, then
	 i = utmax and the instant while-loop will iterate again. If fewer than
	 utmax additional unification theorems were read from unifThms. txt for the
	 last iteration of the instant while-loop, then i < utmax and the instant
	 while-loop will terminate upon evaluation of the conditional of the
	 instant while-loop. */

  {

  match("duplicateUnificationThms.txt", "", "N");
  /* initialize duplicateUnificationThms.txt to be an empty text file. */

  dutfp = fopen("duplicateUnificationThms.txt", "a+");
  /* fopen() assigns the file pointer dutfp the value
     "duplicateUnificationThms.txt" and opens that text file.

	 For the first iteration of the instant while-loop
	 "duplicateUnificationThms.txt" exists due to its creation with the
	 copy() statement immediately preceding the beginning of the instant
	 while-loop. For each successor iteration it exists becuase it existed for
	 the predecessor iteration. */

  fprintf(dutfp, "$( <MM> <PROOF_ASST> THEOREM=duplicateUnificationThms LOC_AFTER=?\n\n");
  /* Add the header to the top of the Proof Worksheet. Use two carriage returns
     to create a blank line between the header line and the first step of the
	 proof.

	 The header name of the theorem is proofWorksheet rather than
	 duplicateUnificationThms to prevent mmj2 from assuming the name of the
	 file to open or save to is duplicateUnificationThms. This is needed
	 because an AutoHotkey script is automatically clicking mmj2 buttons
	 to execute the mmj2 unification. */

  i = 0;
  j = -1;
  /* The counter for the inner loop, j, starts as -1 because the "second
     coordinate" of the step number starts at 0 (not 1). */

  while(fgets(unifThm, 600, utfp) != NULL)

  {

      i = i + 1 ;

      while(fscanf(lfp, "%s", label) != EOF)
      /* The outer loop reads the next label. Even though the fscanf() is in
         the conditional expression in order to compare its return value to
		 end-of-file, the next label is read as the expression is evaluated.
		 This nested loop prints each line (one by one) of the Proof Worksheet
		 of of duplicate unification theorems. To determine which label, if
		 any, unifies with (2-step proves) a unification theorem, every label
		 of labels.txt is tried. That is, for each unification theorem N
		 consecutive steps are constructed and written on a Proof Worksheet,
		 one for each label in the text file labels. If there are M unproven
		 unification theorems in the original Proof Worksheet, then the
		 duplicateUnificatioThms.txt Proof Worksheet contains ((M x N) + 1)
		 steps. The extra step is a dummy qed step, required for a valid mmj2
		 Proof Worksheet. It is intended that the labels text file created by
		 the User will include all of the propositional calculus 1-hypothesis
		 deductions in set.mm. It an unproven unification theorem is a
		 "semantic variation" of a theorem in set.mm, stepprover() will "find"
		 a 1-hypothesis deduction in labels which 2-step proves it
		 automatically with mmj2-unification.

		 Because there are so many possible semantic variations of a theorem in
		 set.mm, a User who knows the theorem may not know which of its
		 semantic variations is in set.mm. If the User uses a semantic
		 variation of the unification theorem not in set.mm and at least one
		 other semantic variationis in set.mm, stepprover may be able to find a
		 semantic variation in set.mm and 2-step prove the unification theorem.
		 The stepprover's ability to find the semantic variation in set.mm
		 depends on the richness of the collection of labels contained in
		 labels.

		 i is the first coordinate of the step number. It is the number
		 corresponding to the unification theorem. j is the second coordinate of
		 the step number, which corresponds to the jth label of the labels text
		 file. i and j printed on each line as the step number. There is no
		 white space between the numbers i and j, so they are concatenated to
		 be a single step number. The label is printed to the immediate right
		 of the step number. The wff is printed to the immediate right of the
		 label. */

	  {

	  j = j + 1 ;

      fprintf(dutfp, "%dn%d:?:%s %s", i, j, label, unifThm);
	  /* This print statement prints the next line of the
	     duplicateUnificationThms.txt Proof Worksheet. */

      }

	  rewind(lfp);
	  /* rewind() sets the file position to the beginning of the file */
	  j = -1 ;

	  if (i == utmax) break;
	  /* This break command terminates the instant while-loop and proceeds to
	     execute the outermost while-loop. The conditional test of the
		 instant while-loop is not re-evaluated until the instant while-loop
		 is entered anew.

		 If there exist fewer than utmax unification theorems to be read by
		 iterations of the instant while-loop, then the loop will be broken out
		 of when there are no more unification theorems to read. If there are
		 utmax or more unification theorems remaining to be read when the
		 instant while-loop starts, then this loop will be broken out of when
		 i becomes equal to utmax. */

  }

  fclose(dutfp);
  /* Close the file pointers. Otherwise the program won't run properly. Note
     that the utfp file pointer remains open. It is still being used and it is
	 necessary to keep it open so that the next unification theorem in the
	 unifThm.txt list will be read when the loop creating the
	 "duplciateUnificationThms.txt" file starts again. */

  if (i == 0) break;
  /* Assume the last duplicateUnificationThm.txt created had utmax unification
     theorems and no unification theorems remained for processing. Then at the
	 end of the while(i == utmax) loop i == utmax and the loop will iterate
	 again. Before entering the while(fget(..)..) loop i is assign the value 0.
	 The expression of the while(fget(..).) loop is false and that loop
	 will terminate. Execution will proceed from the end of that loop and the
	 value of i shall remain 0. The dupfp file pointer is closed. Because no
	 unification theorems remain for processing the while(i == utmax) loop
	 should be terminated. The instant statement breaks execution out of the
	 while(i == utmax) loop. */

  dutfp = fopen("duplicateUnificationThms.txt", "a+");
  /* create a file pointer to duplicateUnificationThms.txt. "a+" specifies that
     the added lines add to the end of of the text file. */

  fprintf(dutfp, "qed:: |- ph\n\n$)");
  /* The Proof Worksheet needs a qed step in order to be a valid mmj2 Proof
     Worksheet. This statement adds it as the last step in the Proof Worksheet.
	 Also added is the footer "$)", required at the end of any mmj2 Proof
	 Worksheet. It is not intended to prove the qed step. The purpose of the
	 duplicateUnificationThms.txt Proof Worksheet is to find 2-step proofs for
	 the unfication theorems, which can be individually proved without proving
	 the qed step. An important benefit of putting all possible steps in a
	 single Proof Worksheet is so that all attempts to unify a unification
	 theorem with a label in the labels text file are simultaneously executed
	 with a single invocation of the mmj2 unification command. Because the
	 mmj2unify() function takes about one-quarter minute to execute (primarily
	 due to the long time required to load set.mm into mmj2), this simultaneous
	 execution is essential. */

  fclose(dutfp);

  if ( strcmp(argv2,"scripts") == 0 ) mmj2Unify("duplicateUnificationThms.txt", "unifylong.exe");
  else mmj2Unify("duplicateUnificationThms.txt", "mmj2StepProver.bat");
  /* mmj2-unify the Proof Worksheet duplicateUnificationThms.txt. */

  match("duplicateUnificationThms.txt", ":?:", "N");
  /* Delete every step for which the unification theorem's assertion did not
     unify with its label. None of these are 2-step provable.

	 Note that a step which is an assertion which did not with its label is
	 uniquely identified by the matchstring ":?:".  A hypothesis step which
	 does not unify with a theorem in set.mm is uniquely identified by the
     matchstring ":: ". */

  add("duplicateUnificationThms.txt", "", "@");
  substitute("duplicateUnificationThms.txt", "@", "!!!", "1", "::");
  substitute("duplicateUnificationThms.txt", "@", "", "1", "");
  substitute("duplicateUnificationThms.txt", "!!!", "", "1", "qed::");
  /* The objective of these five statements is to end-tag each hypothesis step
     of each unification theorem, which are identified by the matchstring "::",
	 so that each hypothesis can be joined to its unification theorem on a
	 single line.  All lines are first temporarily tagged with the construction
	 end-tag "@".  The construction end-tags are replaced by the "!!!" end-tags
	 on the target lines and then removed. It is not desired to end-tag the qed
	 step, which happens to contain the "::", so the tag is removed from that
	 step with the last statement. */

  substitute("duplicateUnificationThms.txt", "\\n", "", "1", "::");
  /* Concatenate each unification theorem (assertion step) with its hypothesis
     step, with the unification theorem on the right separated from the
	 hypothesis on the left by "!!!".  The objective is to place each assertion
	 with its hypothesis on the same line. All hypotheses, whether a theorem in
	 set.mm or not are are identified by the unique matchstring"::". The
	 concatenation is done by deleting the invisible but existent carriage
	 return escape character, "\\n", between each adjacent pair of assertion
	 and hypothesis lines. Note that this concatenation is only applied for
	 instances in which the hypothesis step immediately precedes its
	 corresponding assertion step.

	 At this point, every assertion-hypothesis pair for which the hypothesis
	 step had immediately preceded its corresponding assertion step is on a
	 single line. That condition is usually, but not always, satisfied. Some
	 assertions may share an other assertion's hypothesis step. Any
	 pre-unification step for which a hypothesis step was not generated by
	 mmj2 because a lower hypothesis step has the same wff and is
	 automatically re-used by mmj2. Most idi deductions use a lower
	 deduction's hypothesis. Generally, the idi deductions may not be the
	 only deductions using a lower deduction's hypothesis. It is possible
	 that 2 or more distinct non-idi unification theorems will have the same
	 wff for their hypothesis. For those unification theorems a hypothesis
	 step will be automatically generated by mmj2 only for the lowest
	 unification theorem. The remainder of these unification theorems will use
	 the hypothesis step of the lowest unification theorem. It is also
	 possible that the original Proof Worksheet may contain duplicates of some
	 unification theorems. When duplicates occur, they generally may not be
	 reduced to a single instance because some may be hypothesis steps for
	 different assertion steps. It is possible to deal with this by changing
	 the hyp. field of each such assertion step so that each assertion step
	 points to the lowest duplicate unification theorem. Then all but the
	 lowest duplicate may be deleted. But this change is relatively complex and
	 has not been implemented.

	 What is done for a non-idi unification theorem whose assertion unifies
	 with its label and does not have its own hypothesis because it uses the
	 hypothesis of a lower step? It would be more complex to determine if such
	 a unification theorem is 2-step provable. This complexity is not
	 addressed. Avoidance of this complication is justified on the ground that
	 such instances are rare. Instead, each such unification theorem is simply
	 deleted. This will not cause the program to sometimes give incorrect
	 results. The program will always output correct results, it will only be a
	 little less powerful because it will not attempt to 2-step prove a
	 unification theorem for a label for which a lower unification theorem's
	 hypothesis is used. Therefore, the program will sometimes fail to find
	 2-step proof labels which would have otherwise been found.

	 A non-idi unification theorem without its own generated hypothesis is any
	 line not containing the matchstring "!!!" and not containing the
	 matchstring ":idi ". These lines may be tagged by first end-tagging all
	 lines with "@@@" and then deleting that tag for all lines containing the
	 matchstring "!!!" and all lines containing the matchstring ":idi ".
	 Upon deleting all remaining lines end-tagged with "@@@" each non-idi
	 unification theorem without its own generated hypothesis is removed. Also,
	 the qed step and header and footer lines are removed, which are required
	 removals necessary to ultimately obtain a text file containing only lines
	 with single labels. */

  add("duplicateUnificationThms.txt", "", "@@@");
  substitute("duplicateUnificationThms.txt", "@@@", "", "1", "!!!");
  substitute("duplicateUnificationThms.txt", "@@@", "", "1", ":idi ");
  /* end-tag with "@@@" each non-idi unification theorem which did not
     concatenate with its hypothesis because it re-used the hypothesis of a
	 lower unification theorem because the wff of that hypothesis is identical
	 the wff of the hypothesis of the non-idi unification theorem. Do this by
	 first end-tagging all lines of duplicateUnificationThms.txt and then
	 deleting the tag for each concatenated line (containing "!!!") and each
	 idi line */

  match("duplicateUnificationThms.txt", "@@@", "N");
  /* Each tagged line which is a step of the proof other than the qed step is a
     unification theorem not paired with a hypothesis step immediately below.
	 Remove each of them for reasons explained above. Also remove the header,
	 footer, blank lines, and the qed step, which must be removed in order to
	 obtain a pure list of labels. */

  match("duplicateUnificationThms.txt", ":: ", "N");
  /* delete all steps with labels for which the hypothesis of the label is not
     a theorem in set.mm.  Because the hypothesis of the label did not unify
	 with a theorem in set.mm, no label for the hypothesis was generated.
     These steps are uniquely identified by the matchstring ":: ". */

  delete("duplicateUnificationThms.txt", "", "!!!");
  /* Delete the left side of "!!!" for the lines with labels which 2-step prove
     the unification hypothesis because all that is needed is the assertion
	 step with its label.  The hypothesis set containing the semantic variation
	 in set.mm of the unification theorem is not needed because it will be
	 regenerated later using the label. */

  addMTMF("duplicateUnificationThms.txt", "", "!!!", "", ":idi ");
  /* At this point duplicateUnificationThms.txt contains only the assertion
     steps of subproofs which completed by a 2-step proof and every idi-labeled
	 assertion step. Every subproof with an idi-labeled assertion step is
	 incompletable. This statement "!!!"-end-tags each step not an idi-labeled
	 assertion step.

	 Its hypothesis step, with the same wff and unlabeled, does
	 not unify with a theorem in set.mm. If it did, it would not be a step
	 stepprover() is attempting to complete. The idi label will not generate a
	 new step because at least one previous step has a wff identical to the
	 idi-labeled step. The first one of these will become the hypothesis step
	 of the idi-labeled step. In fact, every step having the same prefix number
	 of the idi-labeled step qualifies to be a hypothesis step of the
	 idi-labeled step because each of these steps has the same wff. Because the
	 idi-labeled step is the last step of all steps with the same prefix number
	 and the existing step chose as its hypothesis step will never occur after
	 the first step having the same prefix as the idi-labeled step, the
	 hypothesis step whose assertion is the idi-labeled step does not
	 immediately precede the idi-labeled step.

	 For each wff for which a 2-step proof was attempted by stepprover(),
	 remaining at this point is consecutive lines of labels of the unification
	 deduction which complete the proof of the wff and idi as the last label.
	 The idi label corresponds to a unification deduction that does not
	 complete the proof. If no labels complete the proof, then the sole label
	 corresponding to the wff is idi. Each non-idi label is end-tagged with
	 "!!!". The idi labels are not tagged.

	 For an earlier version of stepprover(), only those lines containing the
	 matchstring ":d" were end-tagged. This was based on the belief that no
	 idi-labeled step contains a d<i> step number in its hypothesis field.
	 This is false. For example, if a wff for which stepprover() will process
	 which precedes the target wff has a corresponding hypothesis step with the
	 same wff as the target wff, then that hypothesis step may be the may be
	 also be the hypothesis step of the target wff. It is a d<i> step. This
	 may occur if the preceding wff processed by stepprover() is a permutation
	 of the unification theorem which is the target wff. Recognizing this, the
	 present stepprover() instead does not end-tag any step with the label
	 idi. */

  substitute("duplicateUnificationThms.txt", "!!!\\n", "!!!", "1", "");
  substitute("duplicateUnificationThms.txt", "!!!\\n", "!!!", "1", "");
  substitute("duplicateUnificationThms.txt", "!!!\\n", "!!!", "1", "");
  substitute("duplicateUnificationThms.txt", "!!!\\n", "!!!", "1", "");
  /* Concatenate the labelled unification theorem step with the corresponding
     idi-labelled step for the same unification theorem for each unification
	 theorem which has a 2-step proof.  This concatenation is not performed on
	 those unification theorems which are not 2-step provable.  The purpose of
	 the idi steps are to use them as a placeholder for each unification
	 theorem which is not 2-step provable.  The idi label is a construction
	 label to be later eliminated.

	 The reason this substitute() command is duplicated multiple times is as
	 follows. There may be more than one non-idi label which 2-step proves a
	 unification theorem. In duplicateUnificationThms.txt, a duplicate
	 unification theorem step which is the immediate sucessor of one that
	 concatenated with its immediate predecessor line will not concatenate with
	 its immediate predecessor because the immediate predecessor will not be
	 processed by the first substitute() command because it ceased to be its
	 own line when its turn for processing came because it had concatenated
	 with its immediate predecessor line. Provided that fewer than 5 non-idi
	 labels were found to 2-step prove a unification theorem, by using 4
	 substitute() commands all unification theorem duplicates for which a
	 non-idi-label was found will be joined together on a single line with the
	 idi-labelled duplicate at the tail of the line. While it is theoretically
	 possible that a unification theorem is 2-step provable by more than 4
	 non-idi labels in set.mm, the probability of this is remote. stepprover
	 will incorrectly alter the Proof Worksheet if this remote possibility
	 obtains for one or more of the 2-step-proved unfication theorems in the
	 Proof Worksheet. Usually, each 2-step-provable unification theorem in a
	 Proof Worksheet will be 2-step proved by no or one label in set.mm.
	 Infrequently, a unification theorem will be 2-step-provable by two labels
	 in set.mm. */

  delete("duplicateUnificationThms.txt", "!!!", "");
  /* Delete the idi-labelled step on the right side of the line containing the
     2-step proof label of a unification theorem on the left side of the "!!!"
	 separator. It idi-labelled step is not needed as a placeholder for 2-step
	 proven unification theorems.  For those unification theorems not 2-step
	 provable no revision is made and they remain as a placeholder. What
	 remains is the 2-step proveable unificiation theorems with their labels
	 and the non-2-step provable unification theorems which have the
	 construction label idi. */

  delete("duplicateUnificationThms.txt", "", ":");
  delete("duplicateUnificationThms.txt", "", ":");
  delete("duplicateUnificationThms.txt", " |-", "");
  /* Delete everything from each unification theorem line
     except the label.  What remains is a list of 2-step proof labels for each
	 unification theorem in the original mmj2 Proof Worksheet.  Each line
	 contains a single label. The ordering of these labels similarity maps to
	 the ordering of the unification theorems which are not in set.mm of the
	 unified original Proof Worksheet. For non-2-step proveable unification
	 theorems the idi label is used as a placeholder. Later, these labels will
	 be added to the mmj2-unified original Proof Worksheet. After they are
	 added, each occurence of an idi placeholder label will be deleted. If more
	 than one non-idi label is found they are concatenated on a single line.
	 Stepprover picks that non-idi label at the head of the line. */

  add("duplicateUnificationThms.txt", " ", " ");
  substitute("duplicateUnificationThms.txt", " idi ", " placeholder ", "1", "");
  /* As we have said above, the idi labels are placeholders for false labels
     for unification theorems for which there is no theorem in set.mm which
	 allows a 2 step proof.  Here we recognize this and replace the idi label
     with the string "placeholder". After the labels are added to the Proof
	 Worksheet the "placeholder" labels will be deleted. If we had retained the
	 label idi that would have created a problem for the deletion of the
	 placeholder labels because they would be indistinguishable from true idi
	 labels which may exist for some previosly labelled steps. For VD proofs,
	 steps with true idi labels are commonly occurring when a single virtual
	 hypothesis is discharged in a VD proof expressed in conventional notation.
	 The add() statement pads the label with a white space at the beginning and
	 end of each label. Without these whitespaces labels having idi substrings
	 would be incorrectly edited. "placeholder" would replace the "idi"
	 substring. The whitespaces have no effect on the reading of the labels by
	 fscanf(). */

  substitute(proofWorksheet, ":: ", ":?: ", "1", "");
  /* The tagging of unification theorems in proofWorksheet.txt in the loop
     below tags only unification theorems with qestion marks in the hypothesis
	 field. Some of the unification theorems in the Proof Worksheet may have
	 empty hypothesis fields. So they will be tagged, a question mark is added
	 to their empty hypothesis fields. */

  fp = fopen("duplicateUnificationThms.txt", "r");

  while(fscanf(fp, "%s", dlabel) != EOF)
  /* This is a loop which reads from duplicateUnificationThms.txt one
     2-step-proving label at a time. It is important to recognize that the
	 execution of the conditional expression of the while loop both determines
	 whether or not to exit the loop and reads the next label from
	 duplicateUnificationThms.txt. So the next label is read each time
	 execution passes through the top of the while loop where the conditional
	 expression is evaluated. As step 1, the loop simultaneously reads the next
	 label and determines whether or not to exit the loop. As step 2, the next
	 unification deduction in proofWorksheet.txt is tagged to identify it for
	 labelling. As step 3, the tagged unification theorem is labelled by the
	 first substitute() statement in the body of the loop. As step 4, the tag
	 is removed.  After step 4 steps 1 repeats. The loop iterates until all
	 unlabelled unification theorems are labelled. */
  {

  tag(proofWorksheet, "", "!!!", ":?: ", "1", ":?: ", "1");
  /* The Proof Worksheet at this instant is the mmj2-unified original Proof
     Worksheet with question marks added to the hypothesis field of unifcation
	 theorems written by the User which are not in set.mm. This command
	 end-tags the first unification theorem not in set.mm for which a label
	 has not yet been added.

     At this point duplicateUnificationThms.txt is a list of labels which
	 2-step prove unification theorems of the original unified Proof Worksheet
	 (except for the placeholder idi).  fscanf() assigns the first label in the
	 list to the vstring label. */

  substitute(proofWorksheet, ":?: ", cat(":?:", dlabel, " ", NULL), "1", "!!!");
  substitute(proofWorksheet, ":?: ", cat(":?:", dlabel, " ", NULL), "1", "!!!");
  /* At this point proofWorksheet is the mmj2-unified original Proof Worksheet
     with question marks added to the hypothesis fields of unification theorems
	 written by the User and not in set.mm (the remaining unification theorems
	 were generated by mmj2-unify and do not have question marks in their hyp.
	 fields).  Some labels were found by stepprover.  These are contained in
	 the latest duplicateUnificationThms.txt. The present command takes one of
	 the labels from the list, stored in label, and adds it to the unification
	 theorem step in proofWorksheet corresponding to that label. Note that the
	 last argument must always be NULL.

	 The substitute statement is duplicated because otherwise, for unknown
	 reasons, it is sometimes not executed */

  substitute(proofWorksheet, "!!!", "", "1", "");
  /* Remove the end-tag.  A subsequent command will add the next end-tag to
     identify the next step to which to add a label. */

  }

 fclose(fp);
 /* Close the file pointer for duplicateunificationThms.txt. */

 }
 /* This curly brace encloses the while(i == utmax) loop, which is the
    outermost loop. The "placeholder" labels are not removed nor is the unify
    command applied until all unification theorem labels are added.

    The while(fgets(unifThm, 400, utfp) != NULL) loop starts with i initialized
    to 0 and incrments i by 1 with each unification theorem added to
    "duplicateUnificationThm.txt". If fewer than utmax unification theorems are
    added, then this loop is broken out of when the value of i is less than
    utmax. Because i is less than utmax, the while(i == utmax) loop will
    terminate. */

 fclose(lfp);
 fclose(utfp);
 /* Close the file pointers for labels.txt and unifThms.txt. The
    while(i == utmax) loop has terminated and these file pointers are no longer
    needed. All unification theorems in unifThms.txt have been read and
    processed. label.txt will not be used again. */

 let(&label, "");
 let(&dlabel, "");
 /* Having finished using the values contained in label and dlabel, we
    deallocate the memory of label and dlabel in case not doing this might
    result in memory leaks. */

 substitute(proofWorksheet, ":?:placeholder ", ":: ", "1", "");
 /* Remove the placeholder labels, which exist only to identify unification
    theorems which no 2-step proof was found. Now that the 2-step prover
    has been executed the placeholder false labels are no longer needed. They
    must be removed. Otherwise, the steps with a placeholder label are invalid
    mmj2 Proof Worksheet steps.

	A unification theorem which was not 2-step proved is not a theorem in
    set.mm. If it was it would have been unified and labeled with the initial
    mmj2-unification of the stepprover. Because it is not a theorem in set.mm,
    if it is to be proven in the future it must be deduced from at least one
    other step, unknown at the time the stepprover is executed. Note that the
    question mark cannot be retained for the purpose of indicating that no
    2-step proof was found because an unlabeled proof step with the hypothesis
    field ":?:" is not a valid step of an mmj2 Proof Worksheet. */

  if ( strcmp(argv2,"onlyShortScripts") == 0 || strcmp(argv2,"scripts") == 0 ) mmj2Unify(proofWorksheet, "unify.exe");
  else mmj2Unify(proofWorksheet, "mmj2StepProver.bat");
  /* Up to this point the 2-step provable unification theorem steps in the
     Proof Worksheet which was input into stepprover() have been labelled.
	 The instant statement invokes the mmj2-unify command to unify this Proof
	 Worksheet. Each 2-step provable unification theorem is 2-step proved with
	 this unification. */

  return;
}

/* df-removeUnneededFfLabels() */
void removeUnneededFfLabels(char* proofWorksheet, char* proofWorksheet0)
{

  /* removeUnneededFfLabels() deletes the ff* label from each step of
     proofWorksheet if the step in conventional notation will get a set.mm
	 label upon mmj2 unification alone. Because, when in conventional notation,
	 the subproof whose assertion is the step is completed by unifying with a
	 theorem or deduction in set.mm, to retain the ff* label serves no purpose.

	 The value of the argument proofWorksheet0 is the value of proofWorksheet
	 at a point prior to the time at which removeUnneededFfLabels() is called.
	 That value is different at the time the function is called. */

  void mmj2Unify(char* proofWorksheet, char* mmj2BatFile);
  void translate(char* proofWorksheet);
  void tagLabeledSteps(char* proofWorksheet, char* tag);
  void add(char* iofile, char* begstr, char* endstr);
  void substitute(char* iofile, char* oldstring, char* newstring,
                  char* occurrence, char* matchstring);
  void delete(char* iofile, char* startstr, char* endstr);
  void parallel(char* inpfile1, char* inpfile2, char* outfile,
                char* btwnstr);
  /* Declare functions used by removeUnneededFfLabels(). */

  translate(proofWorksheet0);
  if ( strcmp(argv2,"onlyShortScripts") == 0 || strcmp(argv2,"scripts") == 0 ) mmj2Unify(proofWorksheet0, "unify.exe");
  else mmj2Unify(proofWorksheet0, "mmj2StepProver.bat");
  /* proofWorksheet0 is translated into conventional notation and
     mmj2-unified. proofWorksheet0 is unlabeled immediately prior to execution
	 of the mmj2Unify() call. It is merely the translated User's Proof.
	 Therefore, no unwanted type ff labels could cause an error for this
	 mmj2Unify(). */

  tagLabeledSteps(proofWorksheet0, "@!!!!");
  delete(proofWorksheet0, "", "@!");
  add(proofWorksheet0, "", "@@@");
  substitute(proofWorksheet0, "!!!@@@", "!!!", "1", "");
  delete(proofWorksheet0, "", "@@@");
  /* Replace all labeled steps of proofWorksheet0 with "!!!". Replace all other
     lines of proofWorksheet0 with "". */

  parallel(proofWorksheet, proofWorksheet0, proofWorksheet, "");
   /* End-tag with "!!!" each step of proofWorksheet which would have a label
      if proofWorksheet was translated into conventional notation. */

  substitute(proofWorksheet, ":", ":@@@", "2", "!!!");
  substitute(proofWorksheet, " |- ", "&#& |- ", "1", "!!!");
  substitute(proofWorksheet, "!!!", "", "1", "");

  delete(proofWorksheet, "@@@", "&#&");
  /* Delete the label from each step of proofWorksheet which would have a label
     if proofWorksheet was translated into conventional notation and
	 mmj2-unified. */

  return;

}

/* df-nameUsersProofFile() */
void nameUsersProofFile(char* proofWorksheet)
{

  /* nameUsersProofFile() copies c:\mmj2\mmj2jar\proofWorksheet.txt onto
     c:\mmj2\mmj2jar\myproofs\<theorem>.mmp, where <theorem> is the label
	 contained in the header of the User's Proof.

	 Regardless of the path and filename of the User's Proof, there will
	 be two output files, each containing the User's Proof after processing by
	 completeusersproof.  Each of the two output files has the same content.
	 One of the output files is c:\mmj2\mmj2jar\proofWorksheet.txt. The other
	 is c:\mmj2\mmj2jar\myproofs\<theorem>. nameUsersProofFile() creates the
	 latter file. */

  void copy(char* inpfiles, char* outfile);
  void match(char* iofile, char* matchstr, char* YorN);
  void add(char* iofile, char* begstr, char* endstr);
  void delete(char* iofile, char* startstr, char* endstr);
  /* Declare functions used by nameUsersProofFile(). */

  copy(proofWorksheet, "proofWorksheet0.txt");
  match("proofWorksheet0.txt", "$( <MM> <PROOF_ASST> THEOREM=", "Y");
  delete("proofWorksheet0.txt", "", "THEOREM=");
  delete("proofWorksheet0.txt", " LOC_AFTER", "");
  add("proofWorksheet0.txt", "c:\\mmj2\\mmj2jar\\myproofs\\", ".mmp");
  /* proofWorksheet0.txt now has the single line
    "c:\mmj2\mmj2jar\myproofs\<label in User's Proof Worksheet header>.mmp". */

  vstring filename = (vstring)malloc(100);
  FILE* fp = fopen("proofWorksheet0.txt", "r");
  fscanf(fp, "%s", filename);
  /* Scan proofWorksheet0.txt, reading the full path filename that the proof
     to which mmj2Unify() has been applied will be contained
	 in. That full path filename is the value of filename. */

  fclose(fp);
  /* Close the file pointer for proofWorksheet0.txt. */

  copy(proofWorksheet, filename);

  let(&filename, "");
  /* Having finished using the value contained in filename, the memory of this
     variable is deallocated to avoid the risk of possible memory leaks. */

  return;

}

/* df-editHypFieldOfHypSteps() */
void editHypFieldOfHypSteps(char* proofWorksheet, char* labeltype)
{

  /* editHypFieldOfHypSteps() does the following. For the subproof each
     assertion of proofWorksheet having a label of type labeltype, for each
	 hypothesis step having a virtual hypothesis collection distinct from the
	 virtual hypothesis collection of the assertion, fill its hypothesis field,
	 which is empty, with the step number of its corresponding original
	 hypothesis step. For labeltype ffeel, the hypothesis field of the
	 unification theorem step is not edited. For labeltype ffTmv, the
	 hypothesis field of the step which is the T form of the assertion is not
	 edited. */

  void copy(char* inpfiles, char* outfile);
  void match(char* iofile, char* matchstr, char* YorN);
  void substitute(char* iofile, char* oldstring, char* newstring,
                  char* occurrence, char* matchstring);
  void substituteMTMT(char* iofile, char* oldstring, char* newstring,
                      char* occurrence, char* matchstring1, char* matchstring2);
  void add(char* iofile, char* begstr, char* endstr);
  void addMTMF(char* iofile, char* begstr, char* endstr, char* matchstringT,
               char* matchstringF);
  void delete(char* iofile, char* startstr, char* endstr);
  /* Declare functions used by editHypFieldOfHypSteps(). */

  vstring labelType = (vstring)malloc(100);
  vstring colonLabelType = (vstring)malloc(100);
  let(&labelType, labeltype);
  let(&colonLabelType,cat(":", labelType, NULL));
  /* Because labelType is an argument for cat() calls below it must of type
     vstring. Otherwise values are incorrectly or not assigned to it.
	 labelTypeTag and colonLabelType, being derived from labelType, must also
	 be of type vstring. Assignment to these vstring variables must be made
	 using let(). Memory must deallocated for these variable after they are no
	 longer used. These variables may be used as arguments of functions even
	 if the function definition designates the arguments to be of type char*.

	 As is done here, initializing a vstring variable to "" using let is
	 allowed even though the same assignment is used for memory
	 deallocation. */

  copy(proofWorksheet, "hypFields.txt");
  match("hypFields.txt", colonLabelType, "Y");
  substitute("hypFields.txt", ":", "@@@:", "1", "");
  delete("hypFields.txt", "", "@@@");
  delete("hypFields.txt", colonLabelType, "");
  add("hypFields.txt", "", ":");
  /* hypFields.txt begins as a copy of proofWorksheet. All lines are
	 removed except proof steps labeled with a label of type labelType.
     After editing is completed, hypFields.txt is a sequence of ordered pairs
	 of character strings, each adjoining pair of ordered pairs separated by a
	 white space. The first coordinate of each ordered pair is the hypothesis
	 field to replace the empty hypothesis field of the step number whose value
	 is a substring the second coordinate.

	 As an example, if a remaining line after the match() statement is
	 executed is

	 25:14,d8,9,d12,22,d7:ffsmv3h0 |- <wff>

	 after these statements are executed it becomes

	 :14,d8,9,d12,22,d7:

	 */

  int p;
  if ( strcmp(labeltype,"ffTmv") == 0 || strcmp(labeltype,"ffeel") == 0 )
  {

    for( p = 1 ; p <= 20 ; p = p + 1 )
    {
	  substitute("hypFields.txt", ",", "@@@", "1", "");
	  substitute("hypFields.txt", "@@@", "$$$", "1", ",");
    }
    substitute("hypFields.txt", ":", "###:", "2", "");
	delete("hypFields.txt", "@@@", "###");
	substitute("hypFields.txt", "$$$", ",", "ALL", "");
  }
  /* If labeltype is "ffTmv" or "ffeel", then before these statements are executed a
     typical line of hypFields.txt is

	 :14,d8,9,d12,22,d7,d10:

	 After the first statement of iteration 1 of the loop is executed the line
	 is

	 :14@@@d8,9,d12,22,d7,d10:

	 After the second statement of iteration 1 is executed its

	 :14$$$d8,9,d12,22,d7,d10:

	 After the first statement of iteration 6 is executed its

	 :14$$$d8$$$9$$$d12$$$22$$$d7@@@d10:

	 After the second statement of iteration 6 is executed its

	 :14$$$d8$$$9$$$d12$$$22$$$d7@@@d10:

	 Its unchanged because the line does not contain the matchstring ",". The
	 line remains unchanged for all subsequent iterations of the loop because
	 the string "," is not contained in the line. Because "@@@" occurs only once in
	 the line, the delete() statement deletes only the last step number, "d10".
	 After all statements have been executed the line is

	 :14,d8,9,d12,22,d7:

	 The last step number in the hypothesis field has been deleted, satisfying
	 the goal of these statements. The last step number is the is the step
	 number of the ffTmv-labeled assertion step in standard T form. It is not
	 a first coordinate of an ordered pair and therefore should be removed.

	 The loop allows for a maximum of 10 hypothesis steps. */

  for( p = 1 ; p <= 10 ; p = p + 1 )
  {
	substitute("hypFields.txt", ",", ": @@@", "1", "");
	substitute("hypFields.txt", ",", ": :", "1", "");
  }
  substitute("hypFields.txt", ",", ": @@@", "1", "");
  /* Transform the line

  :14,d8,9,d12,22,d7:

  to

  :14: @@@d8: :9: @@@d12: :22: @@@d7:

  The number of steps in a hypothesis field varies. This loop allows for
  a maximum of 10 steps. The number of hypotheses of any deduction with a
  label of the labelType type must not exeed 10, otherwise
  completeusersproof will not process the User's Proof correctly.

  After these statements are executed the construction of hypFields.txt
  has been completed. */

  addMTMF(proofWorksheet, "@@@", "", " |- ", " $p ");
  /* Having completed construction of hypFields.txt, prepare to fill the
	 hypothesis field of each generated hypothesis step with its
     corresponding original User's Proof hypothesis step. Add the beginning
	 string "@@@" to each proof step to disambiguate the step numbers. */

  vstring hypField = (vstring)malloc(30);
  vstring stepNumber = (vstring)malloc(30);

  FILE* fp = fopen("hypFields.txt", "r");

  while(fscanf(fp, "%s %s", hypField, stepNumber) != EOF)
  /* This loop reads from hypFields.txt one hypField and its corresponding
     stepNumber each pass and each pass fills the hypothesis field of a
     generated d<i> hypothesis step with the step number of its
	 corresponding orginal User's Proof hypothesis step. It iterates until
	 all hypothesis fields to be filled are filled. */

  {

	substituteMTMT(proofWorksheet, "::", hypField, "1", stepNumber, "@@@d");
    /* Fill the hypothesis field of each generated d<i> hypothesis step with
	   the step number of its corresponding orginal User's Proof hypothesis
	   step.

	   For some subproofs, one or more of its hypothesis steps may have the
	   same virtual hypothesis collection as its assertion. For those
	   subproofs, no step is generated for each of those hypothesis steps and
	   the first and second coordinates of the ordered pair for each of those
	   steps are identical. For each of these hypothesis steps, if its
	   hypothesis field is empty, it should not be filled. Each hypothesis
	   step which is not one of these steps is a generated step whose step
	   number begins with "d". Only hypothesis steps containing the matchstring
	   "@@@d" should have their hypothesis field filled. Therefore, the second
	   matchstring of substituteMTMT() should be "@@@d". An example of a
	   subproof for which one or more of its hypothesis steps has the same
	   virtual hypothesis collection as its assertion is any subproof unifying
	   with the false deduction ff011. Its corresponding single metavariable
	   deduction is ffsmv3h1. */

  }

  fclose(fp);
  /* Close the file pointer for hypFields.txt. */

  let(&hypField, "");
  let(&stepNumber, "");
  /* Having finished using the values contained in hypField and stepNumber,
	 the memory of this variable is deallocated to avoid the risk of
	 possible memory leaks. */

  substitute(proofWorksheet, "@@@", "", "1", "");
  /* Remove the "@@@" beginning tags. */

  return;

}

/* df-editHypFieldOfTAssertions() */
void editHypFieldOfTAssertions(char* proofWorksheet, char* labeltype)
{

  /* editHypFieldOfTAssertions() does the following. For the subproof each
     assertion having a label of type ffTmv, fill with the other generated
	 hypothesis step numbers the empty hypothesis field of the subproof
	 hypothesis which is the assertion step in standard T form. The value of
	 labeltype must be ffTmv in order for function to process useful output.
	 Even though this function is useful only for that particular case, the
	 variable labeltype is used to satisfy the adopted completeusersproof
	 requirement that all hypothesis field editing functions be general.

	 It is necessary that the call of this function precedes the call
	 editHypFieldOfAssertions(proofWorksheet, "ffTmv");. Otherwise, needed
	 information contained in the hypothesis field of each step labeled
	 with a ffTmv label will not exist when the instant function is called.

	 Note that the converse is not true. While the instant function does
	 alter the hypothesis field of each step with a label of type ffTmv,
	 this alteration is of the temporary file hypFields.txt, not of
	 proofWorksheet. The instant function alters proofWorksheet only in
	 that it alters the standard T form step corresponding to each step with a
	 label of type ffTmv. */

  void copy(char* inpfiles, char* outfile);
  void match(char* iofile, char* matchstr, char* YorN);
  void substitute(char* iofile, char* oldstring, char* newstring,
                  char* occurrence, char* matchstring);
  void add(char* iofile, char* begstr, char* endstr);
  void addMTMF(char* iofile, char* begstr, char* endstr, char* matchstringT,
               char* matchstringF);
  void delete(char* iofile, char* startstr, char* endstr);
  /* Declare functions used by editHypFieldOfTAssertions(). */

  vstring labelType = (vstring)malloc(100);
  vstring colonLabelType = (vstring)malloc(100);
  let(&labelType, labeltype);
  let(&colonLabelType,cat(":", labelType, NULL));
  /* Because labelType is an argument for cat() calls below it must of type
     vstring. Otherwise values are incorrectly or not assigned to it.
	 labelTypeTag and colonLabelType, being derived from labelType, must also
	 be of type vstring. Assign to these vstring variables must be made using
	 let(). Memory must deallocated for these variable after they are no
	 longer used. These variables may be used as arguments of functions even
	 if the function definition designates the arguments to be of type
	 char*.

	 As is done here, initializing a vstring variable to "" using let is
	 allowed even though the same assignment is used for memory
	 deallocation. */

  copy(proofWorksheet, "hypFields.txt");
  match("hypFields.txt", colonLabelType, "Y");
  substitute("hypFields.txt", ":", "@@@:", "1", "");
  delete("hypFields.txt", "", "@@@");
  delete("hypFields.txt", colonLabelType, "");
  add("hypFields.txt", "", ":");
  /* hypFields.txt begins as a copy of proofWorksheet. All lines are removed
     except proof steps labeled with a label of type labelType. After editing
	 is completed, hypFields.txt is a sequence of pairs of character strings,
	 each adjoining pair of ordered pairs separated by a white space. The first
	 coordinate of each pair is the hypothesis field to replace the empty
	 hypothesis field of the step number whose value is a substring the second
	 coordinate.

	 As an example, if a remaining line after the match() statement is
	 executed is

	 22:19,d5,20,d6,21,d7,d8:ffTmv000 |- z e. suc A

	 after these statements are executed it becomes

	 :19,d5,20,d6,21,d7,d8:

  */

  int p;
  substitute("hypFields.txt", ":", ":@@@", "1", "");
  delete("hypFields.txt", "@@@", ",");
  for( p = 1 ; p <= 20 ; p = p + 1 )
  {
	 substitute("hypFields.txt", ",", "$$$@@@", "1", "");
	 delete("hypFields.txt", "@@@", ",");
  }
  substitute("hypFields.txt", "$$$@@@", ": @@@", "1", "");
  /* Edit the hypothesis field of each assertion having a label of type
	 ffTmv.

	 As an example, the hypFields.txt line

	 :19,d5,20,d6,21,d7,d8:

	 ,after these statements are executed, becomes

     :d5,d6,d7: @@@d8:

	 :d5,d6,d7: is the hypothesis field to replace the empty hypothesis
	 field of step d8. Step d8 is step 22, the assertion step in standard
	 T form. Step d8 is

     @@@d8::     |- ( T. -> z e. suc A )

  */

  addMTMF(proofWorksheet, "@@@", "", " |- ", " $p ");
  /* Having completed construction of hypFields.txt, prepare to fill the
	 hypothesis field of each generated hypothesis step with its
     corresponding original User's Proof hypothesis step. Add the beginning
	 string "@@@" to each proof step to disambiguate the step numbers. */

  vstring hypField = (vstring)malloc(30);
  vstring stepNumber = (vstring)malloc(30);

  FILE* fp = fopen("hypFields.txt", "r");

  while(fscanf(fp, "%s %s", hypField, stepNumber) != EOF)
  /* This loop reads from hypFields.txt one hypField and its corresponding
     stepNumber each pass and each pass fills the hypothesis field of a
     generated d<i> hypothesis step with the step number of its
	 corresponding orginal User's Proof hypothesis step. It iterates until
	 all hypothesis fields to be filled are filled. */

  {

	substitute(proofWorksheet, "::", hypField, "1", stepNumber);
    /* Fill the hypothesis field of each generated d<i> assertion in standard
       T form with the generated hypothesis step numbers of the subproof of the
	   original assertion step. */

  }

  fclose(fp);
  /* Close the file pointer for hypFields.txt. */

  let(&hypField, "");
  let(&stepNumber, "");
  /* Having finished using the values contained in hypField and stepNumber,
	 the memory of this variable is deallocated to avoid the risk of
	 possible memory leaks. */

  substitute(proofWorksheet, "@@@", "", "1", "");
  /* Remove the "@@@" beginning tags. */

  return;

}

/* df-editHypFieldOfAssertions() */
void editHypFieldOfAssertions(char* proofWorksheet, char* labeltype)
{

  /* editHypFieldOfAssertions does the following. For the proofWorksheet
     assertion step having a label of type labeltype, edit its hypothesis
	 field. If labeltype is ffsmv, delete elements 1, 3, 5, ... of the
     assertion's hypothesis field. If labeltype is ffeel, delete elements
	 1, 3, 5, ... , except for the last element. If labeltype is ffTmv, delete
	 all elements of the assertion's hypothesis field except the last. */

  void substitute(char* iofile, char* oldstring, char* newstring,
                  char* occurrence, char* matchstring);
  void delete(char* iofile, char* startstr, char* endstr);
  /* Declare functions used by editHypFieldsOfAssertions(). */

  vstring labelType = (vstring)malloc(100);
  vstring colonLabelType = (vstring)malloc(100);
  let(&labelType, labeltype);
  let(&colonLabelType,cat(":", labelType, NULL));
  /* Because labelType is an argument for cat() calls below it must of type
     vstring. Otherwise values are incorrectly or not assigned to it.
	 labelTypeTag and colonLabelType, being derived from labelType, must also
	 be of type vstring. Assign to these vstring variables must be made using
	 let(). Memory must deallocated for these variable after they are no
	 longer used. These variables may be used as arguments of functions even
	 if the function definition designates the arguments to be of type
	 char*.

	 As is done here, initializing a vstring variable to "" using let is
	 allowed even though the same assignment is used for memory
	 deallocation. */

  substitute(proofWorksheet, " |- ", "^^^%%% |- ", "1", colonLabelType);
  substitute(proofWorksheet, "%%%", "\\n", "1", "");
  /* Temporarily split each line containing the matchstring colonLableType.
     "^^^" becomes the end tag of the line containing the matchstring
	 colonLabelType and " |- " becomes the beginning string of the line
	 immediately below, which contains the wwf of the original step. The
	 purpose of the split is so that the following processing of the
	 hypothesis field of each split line will not edit the wff, which it
     might if the string "," occurs in it. */

  if ( strcmp(labeltype,"ffsmv") == 0 || strcmp(labeltype,"ffeel") == 0 )
  {
     int p;
	 substitute(proofWorksheet, ":", ":@@@", "1", colonLabelType);
	 delete(proofWorksheet, "@@@", ",");
	 for( p = 1 ; p <= 15 ; p = p + 1 )
	 {
	   substitute(proofWorksheet, ",", "$$$@@@", "1", colonLabelType);
	   delete(proofWorksheet, "@@@", ",");
	 }
	 substitute(proofWorksheet, "$$$", ",", "ALL", "");
	 substitute(proofWorksheet, "@@@", "", "1", "");
	 /* Edit the hypothesis field of each assertion having a label of type
	    ffsmv or ffeel. For a type ffeel-labeled assertion, all "," except the
		last is temporarily replaced by "$$$". The last is temporarily replaced
	    by "$$$@@@" because the delete() statement has no effect on the last
	    step number in the hypothesis field because "," does not exist.

	    As an example of a type ffsmv assertion, the proofWorksheet line

	    15:12,d3,14,d4,8,8:ffsmv3h0 |- ( ( Tr A /\ ( z e. y /\ y e. suc A ) ) -> z e. suc A )

	    ,after these statements are executed, becomes

	    15:d3,d4,8:ffsmv3h0 |- ( ( Tr A /\ ( z e. y /\ y e. suc A ) ) -> z e. suc A )

        As an example of a type ffeel assertion, the proofWorksheet line

	    15:12,d3,14,d4,8,8,d2:ffeel3h0 |- ( ( Tr A /\ ( z e. y /\ y e. suc A ) ) -> z e. suc A )

	    ,after these statements are executed, becomes

	    15:d3,d4,8,d2:ffeel3h0 |- ( ( Tr A /\ ( z e. y /\ y e. suc A ) ) -> z e. suc A )

	 */

  }

  if ( strcmp(labeltype,"ffTmv") == 0 )
  {
    int p;
	for( p = 1 ; p <= 20 ; p = p + 1 )
	{
	  substitute(proofWorksheet, ":", ":@@@", "1", colonLabelType);
	  delete(proofWorksheet, "@@@", ",");
	}
    substitute(proofWorksheet, ":", ":$$$", "1", colonLabelType);
	substitute(proofWorksheet, "@@@d", "###d", "1", "");
	delete(proofWorksheet, "$$$", "###");
	/* Edit the hypothesis field of each assertion have a label of type
	   ffTmv.

	   As an example, the proofWorksheet line

	   22:19,d5,20,d6,21,d7,d8:ffTmv000 |- z e. suc A

	   ,after these statements are executed, becomes

	   22:d8:ffTmv000 |- z e. suc A

	*/

  }

  substitute(proofWorksheet, "^^^\\n", "", "1", "");
  /* Having completed processing the hypothesis field in the left side of each
     split line, re-join the two halves of each split line and remove the "^^^"
	 end tag of the left half.

	 Note that only the left side of each split line was processed because only
	 that side contains the matchstring colonLabelType. The splitting
	 guarantees that the wwf of each split line will not be altered by the
	 processing because only lines containing the matchstring are altered and
	 the right side of each split line does not contain the matchstring. */

  return;

}

/* df-deleteStepsWithWorkVariables() */
void deleteStepsWithWorkVariables(char* proofWorksheet)
{

  /* deleteStepsWithWorkVariables() deletes each d<i> step of proofWorksheet
     having an empty hypothesis field and a wff in which at least one work
	 variable occurs. */

  void add(char* iofile, char* begstr, char* endstr);
  void substituteMTMT(char* iofile, char* oldstring, char* newstring,
                      char* occurrence, char* matchstring1, char* matchstring2);
  void substitute(char* iofile, char* oldstring, char* newstring,
                  char* occurrence, char* matchstring);
  void match(char* iofile, char* matchstr, char* YorN);
  /* Declare functions used by deleteStepsWithWorkVariables(). */

  add(proofWorksheet, "@@@", "");
  substituteMTMT(proofWorksheet, "::", "!!!", "1", "@@@d", " &W");
  substitute(proofWorksheet, "@@@", "", "1", "");
  match(proofWorksheet, "!!!", "N");
  /* Delete each d<i> step with an empty hypothesis field and a wff with a
	 work variable. Such a step has "d" in column 1, the matchstring "::",
	 and the matchstring " &W". The matchstring " &W" occurs if at least one
	 work variable occurs in the wff of the step. There are no $p and $e
	 wffs in set.mm in which " &W" occurs. Based on this, it is assumed that
	 if " &W" occurs in the wff of a step that at least one work variable
	 occurs in that wff. Adding "@@@" as a beginning tag of each line allows
	 identification of d<i> steps. Only those steps have the matchstring
	 "@@@d". Each d<i> step with "::" and a wff containing at least one work
	 variable is identified by the matchstring "!!!". After this matchstring
	 is created the "@@@" beginning tag is deleted from every line and each
	 d<i> step with an empty hypothesis field is deleted.

     The matchstrings "@@@d" and "::" alone are insufficient because a
	 generated unification theorem is a d<i> step which initially has an
	 empty hypothesis field.

	 Work variables can be avoided by using special case ffeel, ffsmv, and
	 ffTmv deductions. By using more general ffeel, ffsmv, and ffTmv false
	 deductions and using completeusersproof to delete the unnecessary steps
	 in which work variables occur, the number of ffeel, ffsmv, and ffTmv
	 false deductions is significantly reduced. */

  return;

}

/* df-mmj2Unify() */
void mmj2Unify(char* proofWorksheet, char* mmj2BatFile)
{

  /* mmj2Unify() edits a valid mmj2 Proof Worksheet by invoking the mmj2 unify
     command. The first argument, proofWorksheet, is the file name of the Proof
	 Worksheet to be mmj2 unified. The second argument, mmj2BatFile may have a
	 value which is a .bat file which runs an instance of mmj2 having the
	 ProofAsstBatchTest run parm. This applies the mmj2 unify command to
	 proofWorksheet and then terminates. The .bat file may be
	 mmj2StepProver.bat or mmj2FalseDeductions.bat. mmj2StepProver.bat loads
	 set.mm alone. mmj2FalseDeductions.bat load both set.mm and fd.txt. fd.txt
	 contains ff* false deductions. Another type of value mmj2BatFile may have
	 is an executable AutoHotkey script. Two instances of mmj2, both loaded
	 with set.mm and one loaded with and one without ff* false deductions are
	 "on" during completeusersproof execution and available for use as a
	 service by completeusersproof on demand. At any point during execution of
	 completeusersproof when the Proof Worksheet requires unification,
	 mmj2unify() invokes either the unify.exe, unifylong.exe, or unifyFF.exe
	 AutoHotkey script which "presses" various buttons in one of the two mmj2
	 GUIs to open, unify, and save the Proof Worksheet. unifyFF.exe uses the
	 instance of mmj2 loaded with the ff* false deductions. unify.exe and
	 unifylong.exe use the instance of mmj2 loaded only with set.mm.

	 The second command line argument of completeusersproof gives the User the
	 option to use either the mmj2 batch test run parm for mmj2 unification or
	 an AutoHotkey script. If AutoHotkey scripts are used, then the User must
	 run completeusersproofGUI.exe and open in the mmj2 GUI not loaded with the
	 ff* false deductions a file containing a Proof Worksheet to be processed
	 by completeusersproof. The script completeusersproofGUI.exe copies this
	 file onto c:\mmj2\mmj2jar\proofWorksheet.txt. Each time mmj2Unify() is
	 called c:\mmj2\mmj2jar\proofWorksheet.txt is copied onto either
	 c:\mmj2\mmj2jar\myproofs\proofWorksheet.mmp or(excl.)
	 c:\mmj2\mmj2jar\myproofs\proofWorksheetFF.mmp. That file
	 is then unified and copied back onto c:\mmj2\mmj2jar\proofWorksheet.txt.
	 At the end of completeusersproof, c:\mmj2\mmj2jar\proofWorksheet.txt
	 holds the fully processed Proof Worksheet and is copied onto
	 c:\mmj2\mmj2jar\myproofs\<theorem>.mmp, where <theorem> is the label of
	 the theorem input by the User as specified in the header of the Proof
	 Worksheet.

	 Each time mmj2Unify() is called it unifies the proof contained in the
	 file proofWorksheet, which is always c:\mmj2\mmj2jar\proofWorksheet.txt,
	 and saves the output file as the same c:\mmj2\mmj2jar\proofWorksheet.txt.

	 c:\mmj2\mmj2jar\proofWorksheetFF.mmp is the unique file unified by the
	 mmj2 instance loaded with the ff* false deductions and set.mm.
	 c:\mmj2\mmj2jar\proofWorksheet.mmp is the unique file unified by the mmj2
	 instance loaded with set.mm and not loaded with the ff* false deductions.
	 If the value of the mmj2BatFile argument of mmj2Unify() is unifyFF.exe,
	 then unifyFF.exe unifies proofWorksheetFF.mmp. If the value of the
	 mmj2BatFile argument of mmj2Unify() is unify.exe(unifylong.exe), then
	 unify.exe(unifylong.exe) unifies proofWorksheet.mmp. If the value of the
	 mmj2BathFile argument is mmj2FalseDeductions.bat, then
	 mmj2FalseDeductions.bat unifies proofWorksheetFF.mmp. If the value is
	 mmj2StepProver.bat, then proofWorksheet.mmp is unified.

	 The Title of the GUI window in which proofWorksheet.mmp
	 (proofWorksheetFF.mmp) is open includes the substring "proofWorksheet.mmp"
	 ("proofWorksheetFF.mmp). unify.exe and unifylong.exe (unifyFF.exe) press
	 the buttons in the GUI whose window Title includes the substring
	 "proofWorksheet.mmp" ("proofWorksheetFF.mmp"). The reason
	 the placeholder files proofWorksheetFF.mmp and proofWorksheet.mmp are
	 used as temporary files is so the scripts can identify which GUI to
	 press buttons in. */

  void copy(char* inpfiles, char* outfile);
  void addMT(char* iofile, char* begstr, char* endstr, char* matchstring);
  void substitute(char* iofile, char* oldstring, char* newstring, char* occurrence, char* matchstring);
  void delete(char* iofile, char* startstr, char* endstr);

  if ( strcmp(mmj2BatFile,"unifyFF.exe") == 0 || strcmp(mmj2BatFile,"mmj2FalseDeductions.bat") == 0 )
	/* strcmp() compares two strings. If the strings are equal, strcmp() returns
       the integer 0. If the strings are not equal, strcmp() returns either a
	   positive or negative integer. */

  {
    copy(proofWorksheet, "c:\\mmj2\\mmj2jar\\myproofs\\proofWorksheetFF.mmp");
	addMT("c:\\mmj2\\mmj2jar\\myproofs\\proofWorksheetFF.mmp", "$( <MM> <PROOF_ASST> THEOREM=proofWorksheetFF LOC_AFTER=?@@@@*", "", "$( <MM> ");
	substitute("c:\\mmj2\\mmj2jar\\myproofs\\proofWorksheetFF.mmp", "@@@@", "\n", "1", "");
    /* Temporarily change the file name and the theorem name to proofWorksheetFF.
	   This is the name corresponding to the instance of mmj2 loaded with
	   ff* false deductions.

	   The original header is saved as a comment line immediately below the
	   temporary header line. After unification the original header line is
	   restored.

	   If mmj2BatFile has the value "unifyFF.exe" or "mmj2FalseDeductions.bat",
	   then the file and header are temporarily changed to proofWorksheetFF.
	   For "unifyFF.exe", this name identifies the GUI corresponding to the
	   instance of mmj2 loaded with the ff* false deductions as the one in
	   which to press buttons. */

	if ( strcmp(mmj2BatFile,"mmj2FalseDeductions.bat") == 0 ) copy("c:\\mmj2\\mmj2jar\\myproofs\\proofWorksheetFF.mmp", "c:\\mmj2\\mmj2jar\\temp.txt");
  }

  if ( strcmp(mmj2BatFile,"unify.exe") == 0 || strcmp(mmj2BatFile,"unifylong.exe") == 0 || strcmp(mmj2BatFile,"mmj2StepProver.bat") == 0 )

  {
    copy(proofWorksheet, "c:\\mmj2\\mmj2jar\\myproofs\\proofWorksheet.mmp");
	addMT("c:\\mmj2\\mmj2jar\\myproofs\\proofWorksheet.mmp", "$( <MM> <PROOF_ASST> THEOREM=proofWorksheet LOC_AFTER=?@@@@*", "", "$( <MM> ");
	substitute("c:\\mmj2\\mmj2jar\\myproofs\\proofWorksheet.mmp", "@@@@", "\n", "1", "");
    /* This conditional is similar to the conditional immediately above. It
	   handles the case of unifications requiring mmj2 to be loaded with only
	   the set.mm theorems, not the ff* false deductions. */

	if ( strcmp(mmj2BatFile,"mmj2StepProver.bat") == 0 ) copy("c:\\mmj2\\mmj2jar\\myproofs\\proofWorksheet.mmp", "c:\\mmj2\\mmj2jar\\temp.txt");
  }

    system(mmj2BatFile);
  /* If the value of mmj2BatFile is an AutoHotkey script, it presses buttons
     in the GUI to open the updated file, unify it, and save it. If the value
	 of mmj2BatFile is a .bat file, it unifies proofWorksheet using the mmj2
	 batch test run parm. */

  if ( strcmp(mmj2BatFile,"unifyFF.exe") == 0 || strcmp(mmj2BatFile,"mmj2FalseDeductions.bat") == 0 )

  {
    if ( strcmp(mmj2BatFile,"mmj2FalseDeductions.bat") == 0 ) copy("c:\\mmj2\\mmj2jar\\temp2.txt", "c:\\mmj2\\mmj2jar\\myproofs\\proofWorksheetFF.mmp");
    copy("c:\\mmj2\\mmj2jar\\myproofs\\proofWorksheetFF.mmp", proofWorksheet);
	substitute(proofWorksheet, "=?\\n", "", "1", "proofWorksheetFF");
	substitute(proofWorksheet, "*", "@@@*", "1", " <MM> <PROOF_ASST> ");
	delete(proofWorksheet, "", "@@@*");
	copy("c:\\mmj2\\mmj2jar\\myproofs\\proofWorksheetFF0.mmp","c:\\mmj2\\mmj2jar\\myproofs\\proofWorksheetFF.mmp");
    /* Change the file name of the unified proof back to proofWorksheet.
	   Join the original header to the right end of the temporary header,
	   then delete the temporary header, leaving only the original header.
	   Restore the destroyed proofWorksheetFF template.

	   The original file and theorem name of proofWorksheet have been
	   restored for the case of false deduction unifications. */
  }

  if ( strcmp(mmj2BatFile,"unify.exe") == 0 || strcmp(mmj2BatFile,"unifylong.exe") == 0 || strcmp(mmj2BatFile,"mmj2StepProver.bat") == 0 )

  {
	if ( strcmp(mmj2BatFile,"mmj2StepProver.bat") == 0 ) copy("c:\\mmj2\\mmj2jar\\temp2.txt", "c:\\mmj2\\mmj2jar\\myproofs\\proofWorksheet.mmp");
    copy("c:\\mmj2\\mmj2jar\\myproofs\\proofWorksheet.mmp", proofWorksheet);
	substitute(proofWorksheet, "=?\\n", "", "1", "proofWorksheet");
	substitute(proofWorksheet, "*", "@@@*", "1", " <MM> <PROOF_ASST> ");
	delete(proofWorksheet, "", "@@@*");
    copy("c:\\mmj2\\mmj2jar\\myproofs\\proofWorksheet0.mmp", "c:\\mmj2\\mmj2jar\\myproofs\\proofWorksheet.mmp");
    /* Similar to the conditional immediately above, except for the
       case of unifications by the mmj2 instance not loaded with false
	   deductions. */
  }

  /* Note that all instances of mmj2 have the run parameter
	 "ProofAsstDeriveAutocomplete, no". Autocomplete is enabled by default with
	 the August 14, 2016 version of mmj2.  The behavior of Autocomplete is
	 incompatible with completeusersproof() and therefore this feature of mmj2
	 is disabled. */

  void match(char* iofile, char* matchstr, char* YorN);
  match(proofWorksheet, "I-UT-", "N");
  match(proofWorksheet, "I-PA-", "N");
  match(proofWorksheet, "E-PR-", "N");
  match(proofWorksheet, "[dummylink]", "N");
  match(proofWorksheet, "Proof incomplete", "N");
  match(proofWorksheet, "Unification failure", "N");
  /* Delete all comments and error messages on the Proof Worksheet generated by
     the mmj2-unification. These invalidate the format of the Proof Worksheet
	 so that it cannot be mmj2 unified again. The Proof Worksheet after the
	 deletion of these is a valid Proof Worksheet. These comments and error
	 messages do not occur within the body of Proof Worksheet when the
	 unification command is executed through the GUI interface. */

  return;
}

/* df-translate() */
void translate(char* proofWorksheet)
{

  /* translate() translates Virtual Deduction notation virtual deductions in
     proofWorksheet into conventional notation virtual deductions. */

  void substitute(char* iofile, char* oldstring, char* newstring,
                  char* occurrence, char* matchstring);
  void addMT(char* iofile, char* begstr, char* endstr, char* matchstring);
  void substituteMTMT(char* iofile, char* oldstring, char* newstring,
                      char* occurrence, char* matchstring1, char* matchstring2);
  /* Declare functions used by translate(). */

  substitute(proofWorksheet, " (. ", " ( ", "1", " ->. ");
  /* If a step's wff is a virtual deduction with virtual deduction notation,
     translate only the first occurrence of " (. " to be " ( ". This is done
	 so that the code of translate() will correctly translate any step which
	 is a virtual deduction with a singleton virtual hypothesis and with a
	 virtual conclusion which is a more-than-3 conjunction in virtual deduction
	 notation. Without this statement such a step would translate incorrectly
	 because the first " (. " would be the left parenthesis of the virtual
	 deduction, not the left parenthesis of the more-than-3 conjunction.

	 This version of completeusersproof will incorrectly translate any step
	 which is a virtual deduction in virtual deduction notation with a virtual
	 conclusion which is a conjunction in virtual deduction notation if the
	 virtual hypothesis is not a singleton and " ,. " occurs in the step more
	 than twice.

	 This version of completeusersproof may not run correctly if any of the
	 following restrictions are violated:

	   1. No step may contain a conjunct in virtual deduction notation with
	      some element(s) which is(are) (a) conjunct(s) in virtual deduction
		  notation.

	   2. No step which is a virtual deduction in virtual deduction notation
          may have a virtual conclusion in virtual deduction notation unless
	      that virtual conclusion is a conjunct in virtual deduction notation.

	   3. No step which is a virtual deduction in virtual deduction notation
	      and with a conclusion which is a conjunct in virtual deduction
		  notation may have a virtual hypothesis collection not a singleton. */

  substitute(proofWorksheet, " ,. ", " @@@ ", "3", "");
  addMT(proofWorksheet, "", "!!!", " @@@ ");
  substitute(proofWorksheet, " @@@ ", " ,. ", "1", "");
  substitute(proofWorksheet, " ,. ", " /\\ ", "1", "!!!");
  /* End-tag each step of of the User's Proof having a virtual hypothesis
     collection with 4 or more elements. These statements also end tag
	 comment lines containing Virtual Deduction wfffs.

	 Conventional notation virtual hypothesis collections which have more than
	 3 elements are in left-nested conjunction form. This is necessary because,
	 unlike Virtual Deduction notation, set.mm does not contain a syntax
	 definition for conjunction wffs having more than 3 conjuncts. The above
	 statements translate the aspects of Virtual Deduction wffs affected by
	 this difference.

	 As an example,

       45:26,35,44: |- (. (. ph ,. Tr A ,. z e. y ,. y e. A ). ->. z e. A ).

     Is a virtual deduction of a User's Proof with a 4-element virtual
     hypothesis collection. After the execution of the above statements it
     becomes

       45:26,35,44: |- (. (. ph /\ Tr A ,. z e. y ,. y e. A ). ->. z e. A ).!!!

  */

  int p;

  for( p = 1 ; p <= 10 ; p = p + 1 )
  {
    substituteMTMT(proofWorksheet, " (. ", " (. (. ", "1", " ,. ", "!!!");
	substitute(proofWorksheet, " ,. ", " ) /\\ ", "1", "!!!");
  }

  /* After the first iteration of this loop the above User's Proof virtual
     deduction becomes

       45:26,35,44: |- (. (. (. ph /\ Tr A ) /\ z e. y ,. y e. A ). ->. z e. A ).!!!

	 After the second iteration it becomes

       45:26,35,44: |- (. (. (. (. ph /\ Tr A ) /\ z e. y ) /\ y e. A ). ->. z e. A ).!!!

     Because " ,. " does not occur after the second iteration the remaining
     iterations have no effect. If the virtual hypothesis collection had n
	 elements, then iterations after iteration ( n - 2 ) would have no effect.
	 The behavior of translate() is correct virtual hypothesis collections of
	 up to 15 elements. translate() will not behave correctly for a virtual
	 deduction with a virtual hypothesis collection of more than 15
	 elements. */

  substitute(proofWorksheet, "!!!", "", "1", "");
  /* After the above statements are executed, special processing for each
     virtual deduction in the User's Proof having a virtual hypothesis
	 collection with more than 3 elements has been completed. The remaining
	 processing applies to all steps of the User's Proof. */

  substitute(proofWorksheet, " (. ", " ( ", "ALL", "");
  substitute(proofWorksheet, " (. ", " ( ", "ALL", "");
  substitute(proofWorksheet, " ).", " )", "ALL", "");
  substitute(proofWorksheet, " ,. ", " /\\ ", "ALL", "");
  substitute(proofWorksheet, " ->. ", " -> ", "ALL", "");
  /* Translate all remaining Virtual Deduction symbols for steps with a more
     than 3 element virtual hypothesis collection and convert each remaining
	 Virtual Deduction wff of the Proof Worksheet to its conventional
	 notation counterpart.

	 The first statement is repeated twice because it was observed that with
	 only a single " ( "-for-" (. " substitution statement there is no
	 substitution for the second " (. " of a wff.

	 Continuing with the above example, after these 5 statements are executed
	 the example step becomes

       45:26,35,44: |- ( ( ( ( ph /\ Tr A ) /\ z e. y ) /\ y e. A ) -> z e. A )

	 The Virtual Deduction symbols have been replaced by their conventional
     notation counterparts and the virtual hypothesis collection in
     conventional notation is a left-nested conjunction. A 3-element virtual
	 hypothesis collection is an unnested 3-conjunct conjunction. A 2-element
	 virtual hypothesis collection is the same 2-element conjunction whether
	 interpreted to be nested or unnested. The nested-unnested dichotomy is
	 inapplicable to singleton virtual hypothesis collections and virtual
	 deductions with no virtual hypotheses. */

  return;
}

/* df-tagLabeledSteps() */
void tagLabeledSteps(char* proofWorksheet, char* tag)
{

  /* tagLabeledSteps() end-tags the labeled steps of the proof of
     proofWorksheet with the value of the variable tag. If
	 "!@#!@#!@#!@#!@#" occurs within iofile tagLabeledSteps() may
	 not edit iofile as intended. */

  void add(char* iofile, char* begstr, char* endstr);
  void substitute(char* iofile, char* oldstring, char* newstring,
                  char* occurrence, char* matchstring);

  add(proofWorksheet, "", "!@#!@#!@#!@#!@#!@#" );
  substitute(proofWorksheet, "!@#!@#!@#!@#!@#!@#", tag, "1", " |- ");
  substitute(proofWorksheet, tag, "!@#!@#!@#!@#!@#!@#", "1", "$p");
  substitute(proofWorksheet, tag, "!@#!@#!@#!@#!@#!@#", "1", "$e");
  substitute(proofWorksheet, tag, "!@#!@#!@#!@#!@#!@#", "1", ": ");
  substitute(proofWorksheet, "!@#!@#!@#!@#!@#!@#", "", "1", "");
  /* It is presumed that "$p" and "$e" only occur in a line with " |- " which
     is a comment line. Lines containing ": " are unlabeled proof steps. */

  return;

}

/* df-endTag() */
void endTag(char* iofile, char* endstr, char* matchstring)
{

  /* endTag(iofile, endstr, matchstring) end-tags with endstr those lines in
     iofile containing matchstring. The value "!@#$%!@#$%!@#$%!@#$%!@#$%" for
	 endstr will not edit iofile as intended. */

  void add(char* iofile, char* begstr, char* endstr);
  void substitute(char* iofile, char* oldstring, char* newstring,
                  char* occurrence, char* matchstring);

  add(iofile, "", "!@#$%!@#$%!@#$%!@#$%!@#$%");
  substitute(iofile, "!@#$%!@#$%!@#$%!@#$%!@#$%", endstr, "1", matchstring);
  substitute(iofile, "!@#$%!@#$%!@#$%!@#$%!@#$%", "", "1", "");

  return;

}

/* df-addMTLast() */
void addMTLast(char* iofile, char* begstr, char* endstr, char* matchstring)

{

  /* addMTLast() is similar to addMT(), except begstr and endstr are added only
     to the last line of iofile in which matchstring occurs. addMTLast() may
	 not edit iofile as intended if "!@#!@#!@#!@#!@#!@#" or(incl.)
	 "$%^$%^$%^$%^$%^$%^" or(incl.) "^&*^&*^&*^&*^&*^&*" occurs in it. */

  void addMT(char* iofile, char* begstr, char* endstr, char* matchstring);
  void reverse(char* iofile);
  void tag(char* iofile, char* begstr, char* endstr, char* startmatch,
           char* sn, char* endmatch, char* en);
  void substitute(char* iofile, char* oldstring, char* newstring,
                  char* occurrence, char* matchstring);

  addMT(iofile, "!@#!@#!@#!@#!@#!@#", "$%^$%^$%^$%^$%^$%^", matchstring);
  reverse(iofile);
  tag(iofile, "", "^&*^&*^&*^&*^&*^&*", "!@#!@#!@#!@#!@#!@#", "1", "!@#!@#!@#!@#!@#!@#", "1");
  reverse(iofile);
  substitute(iofile, "!@#!@#!@#!@#!@#!@#", begstr, "A", "^&*^&*^&*^&*^&*^&*");
  substitute(iofile, "$%^$%^$%^$%^$%^$%^", endstr, "A", "^&*^&*^&*^&*^&*^&*");
  substitute(iofile, "!@#!@#!@#!@#!@#!@#", "", "A", "");
  substitute(iofile, "$%^$%^$%^$%^$%^$%^", "", "A", "");
  substitute(iofile, "^&*^&*^&*^&*^&*^&*", "", "A", "");

  return;

}

/* df-addMTMT() */
void addMTMT(char* iofile, char* begstr, char* endstr, char* matchstring1,
             char* matchstring2)
{

  /* addMTMT() is similar to add(), except begstr and endstr are not added to
     any line in which matchstring1 or(incl.) matchstring2 does not occur.
	 addMTMT() may not edit iofile as intended if "!@#!@#!@#!@#!@#!@#"
	 or(incl.) "$%^$%^$%^$%^$%^$%^" or(incl.) "^&*^&*^&*^&*^&*^&*" occurs in
	 it. */

  void addMT(char* iofile, char* begstr, char* endstr, char* matchstring);
  void addMTMF(char* iofile, char* begstr, char* endstr, char* matchstringT,
               char* matchstringF);
  void substitute(char* iofile, char* oldstring, char* newstring,
                  char* occurrence, char* matchstring);

  addMT(iofile, "!@#!@#!@#!@#!@#!@#", "$%^$%^$%^$%^$%^$%^", matchstring1);
  addMTMF(iofile, "", "^&*^&*^&*^&*^&*^&*", "", matchstring2);
  substitute(iofile, "!@#!@#!@#!@#!@#!@#", "", "A", "^&*^&*^&*^&*^&*^&*");
  substitute(iofile, "$%^$%^$%^$%^$%^$%^", "", "A", "^&*^&*^&*^&*^&*^&*");
  substitute(iofile, "!@#!@#!@#!@#!@#!@#", begstr, "A", "");
  substitute(iofile, "$%^$%^$%^$%^$%^$%^", endstr, "A", "");
  substitute(iofile, "^&*^&*^&*^&*^&*^&*", "", "A", "");

  return;

}

/* df-addMTMF() */
void addMTMF(char* iofile, char* begstr, char* endstr, char* matchstringT,
             char* matchstringF)
{

  /* addMTMF() is similar to addMT(), except each line to which begstr and
     endstr is added contains matchstringT and does not contain
	 matchstringF. addMTMF() may not edit iofile as intended if
	 "!@#!@#!@#!@#!@#!@#" or(incl.) "$%^$%^$%^$%^$%^$%^" occurs in it. */

  void addMT(char* iofile, char* begstr, char* endstr, char* matchstring);
  void substitute(char* iofile, char* oldstring, char* newstring,
                  char* occurrence, char* matchstring);

  addMT(iofile, "!@#!@#!@#!@#!@#!@#", "$%^$%^$%^$%^$%^$%^", matchstringT);
  substitute(iofile, "!@#!@#!@#!@#!@#!@#", "", "1", matchstringF);
  substitute(iofile, "$%^$%^$%^$%^$%^$%^", "", "1", matchstringF);
  substitute(iofile, "!@#!@#!@#!@#!@#!@#", begstr, "A", "");
  substitute(iofile, "$%^$%^$%^$%^$%^$%^", endstr, "A", "");

  return;

}

/* df-addMT() */
void addMT(char* iofile, char* begstr, char* endstr, char* matchstring)
{

  /* addMT(iofile, begstr, endstr, matchstring) is similar to add(), except
     begstr and endstr are added only to lines containing matchstring. If
	 begstr has the value "#@!#@!#@!#@!#@!#@!" or(incl.) endstr has the value
	 "^%$^%$^%$^%$^%$^%$", then iofile will not be edited as intended. */

  void add(char* iofile, char* begstr, char* endstr);
  void substitute(char* iofile, char* oldstring, char* newstring,
                  char* occurrence, char* matchstring);

  add(iofile, "#@!#@!#@!#@!#@!#@!", "^%$^%$^%$^%$^%$^%$");
  substitute(iofile, "#@!#@!#@!#@!#@!#@!", begstr, "1", matchstring);
  substitute(iofile, "^%$^%$^%$^%$^%$^%$", endstr, "1", matchstring);
  substitute(iofile, "#@!#@!#@!#@!#@!#@!", "", "A", "");
  substitute(iofile, "^%$^%$^%$^%$^%$^%$", "", "A", "");

  return;

}

/* df-substituteMTMT() */
void substituteMTMT(char* iofile, char* oldstring, char* newstring,
                    char* occurrence, char* matchstring1, char* matchstring2)
{

  /* substituteMTMT() is similar to substitute() except the substitution is
     made in a line if and only if both matchstring1 and matchstring2 occur in
	 it. substituteMTMT() may not edit iofile as intended if
	 "!#%!#%!#%!#%!#%!#%" or(incl.) "@$^@$^@$^@$^@$^@$^" occur in iofile. */

  void substitute(char* iofile, char* oldstring, char* newstring,
                  char* occurrence, char* matchstring);
  void addMTMF(char* iofile, char* begstr, char* endstr, char* matchstringT,
               char* matchstringF);

  substitute(iofile, oldstring, "!#%!#%!#%!#%!#%!#%", occurrence, matchstring1);
  addMTMF(iofile, "", "@$^@$^@$^@$^@$^@$^", "", matchstring2);
  substitute(iofile, "!#%!#%!#%!#%!#%!#%", oldstring, "A", "@$^@$^@$^@$^@$^@$^");
  substitute(iofile, "@$^@$^@$^@$^@$^@$^", "", "1", "");
  substitute(iofile, "!#%!#%!#%!#%!#%!#%", newstring, "A", "");

  return;

}

/* df-deleteLabels() */
void deleteLabels(char* iofile, char* matchstring)

{

  /* deleteLabels() deletes the labels of all steps of a Proof Worksheet
     containing the match string matchstring. deleteLabels() may not edit
	 iofile as intended if "!@#!@#!@#!@#!@#!@#" or(incl.) "$%^$%^$%^$%^$%^"
	 occurs in it. */

  void substitute(char* iofile, char* oldstring, char* newstring,
                  char* occurrence, char* matchstring);
  void delete(char* iofile, char* startstr, char* endstr);

  substitute(iofile, ":", ":!@#!@#!@#!@#!@#!@#", "2", matchstring);
  substitute(iofile, " |-", "$%^$%^$%^$%^$%^$%^ |-", "1", matchstring);
  substitute(iofile, "$%^$%^$%^$%^$%^$%^ |-", " |-", "1", "$p");
  substitute(iofile, "$%^$%^$%^$%^$%^$%^ |-", " |-", "1", "$e");
  delete(iofile, "!@#!@#!@#!@#!@#!@#", "$%^$%^$%^$%^$%^$%^");

  return;

}

/* df-removeATypeOfLabel() */
void removeATypeOfLabel(char* proofWorksheet, char* colonlabeltype)

{

  /* removeATypeOfLabel() removes all labels in proofWorksheet of the type
     which is colonlabeltype without the prefix ":". colonlabeltype is the
	 label type with the added prefix ":". */

  void substitute(char* iofile, char* oldstring, char* newstring,
                  char* occurrence, char* matchstring);
  void delete(char* iofile, char* startstr, char* endstr);
  /* Declare functions used by removeATypeOfLabel(). */

  substitute(proofWorksheet, ":", ":@@@:", "2", colonlabeltype);
  substitute(proofWorksheet, " |- ", "!!! |- ", "1", colonlabeltype);
  delete(proofWorksheet, "@@@:", "!!!");

  return;

}

/* The C functions emulating the Metamath tools commands */

/* df-substitute() */
void substitute(char* iofile, char* oldstring, char* newstring, char* occurrence, char* matchstring)
{

  void toolscmd(char* arg1, char* arg2, char* arg3, char* arg4, char* arg5, char* arg6, char* arg7, char cmd[]);

  toolscmd(iofile, oldstring, newstring, occurrence, matchstring, "\0", "\0", "SUBSTITUTE");
  return;
}

/* df-add() */
void add(char* iofile, char* begstr, char* endstr)
{

  void toolscmd(char* arg1, char* arg2, char* arg3, char* arg4, char* arg5, char* arg6, char* arg7, char cmd[]);

  toolscmd(iofile, begstr, endstr, "\0", "\0", "\0", "\0", "ADD");
  return;
}

/* df-delete() */
void delete(char* iofile, char* startstr, char* endstr)
{

  void toolscmd(char* arg1, char* arg2, char* arg3, char* arg4, char* arg5, char* arg6, char* arg7, char cmd[]);

  toolscmd(iofile, startstr, endstr, "\0", "\0", "\0", "\0", "DELETE");
  return;
}

/* df-copy() (the first argument is of the form "inpfile1,inpfile2") */


void copy(char* inpfiles, char* outfile)
{

  void toolscmd(char* arg1, char* arg2, char* arg3, char* arg4, char* arg5, char* arg6, char* arg7, char cmd[]);

  toolscmd(inpfiles, outfile, "\0", "\0", "\0", "\0", "\0", "COPY");
  return;
}

/* df-tag() */
void tag(char* iofile, char* begstr, char* endstr, char* startmatch, char* sn, char* endmatch, char* en)
{

  void toolscmd(char* arg1, char* arg2, char* arg3, char* arg4, char* arg5, char* arg6, char* arg7, char cmd[]);

  toolscmd(iofile, begstr, endstr, startmatch, sn, endmatch, en, "TAG");
  return;
}

/* df-parallel() */
void parallel(char* inpfile1, char* inpfile2, char* outfile, char* btwnstr)
{

  void toolscmd(char* arg1, char* arg2, char* arg3, char* arg4, char* arg5, char* arg6, char* arg7, char cmd[]);

  toolscmd(inpfile1, inpfile2, outfile, btwnstr, "\0", "\0", "\0", "PARALLEL");
  return;
}

/* df-clean() */
void clean(char* iofile, char* subcmd)
{

  void toolscmd(char* arg1, char* arg2, char* arg3, char* arg4, char* arg5, char* arg6, char* arg7, char cmd[]);

  toolscmd(iofile, subcmd, "\0", "\0", "\0", "\0", "\0", "CLEAN");
  return;
}

/* df-swap() */
void swap(char* iofile, char* strg)
{

  void toolscmd(char* arg1, char* arg2, char* arg3, char* arg4, char* arg5, char* arg6, char* arg7, char cmd[]);

  toolscmd(iofile, strg, "\0", "\0", "\0", "\0", "\0", "SWAP");
  return;
}

/* df-insert() */
void insert(char* iofile, char* strg, char* column)
{

  void toolscmd(char* arg1, char* arg2, char* arg3, char* arg4, char* arg5, char* arg6, char* arg7, char cmd[]);

  toolscmd(iofile, strg, column, "\0", "\0", "\0", "\0", "INSERT");
  return;
}

/* df-break() (use "brk" instead of "break" because "break" is a C command) */
void brk(char* iofile, char* specchars)
{

  void toolscmd(char* arg1, char* arg2, char* arg3, char* arg4, char* arg5, char* arg6, char* arg7, char cmd[]);

  toolscmd(iofile, specchars, "\0", "\0", "\0", "\0", "\0", "BREAK");
  return;
}

/* df-build() */
void build(char* iofile)
{

  void toolscmd(char* arg1, char* arg2, char* arg3, char* arg4, char* arg5, char* arg6, char* arg7, char cmd[]);

  toolscmd(iofile, "\0", "\0", "\0", "\0", "\0", "\0", "BUILD");
  return;
}

/* df-match() */
void match(char* iofile, char* matchstr, char* YorN)
{

  void toolscmd(char* arg1, char* arg2, char* arg3, char* arg4, char* arg5, char* arg6, char* arg7, char cmd[]);

  toolscmd(iofile, matchstr, YorN, "\0", "\0", "\0", "\0", "MATCH");
  return;
}

/* df-duplicate() */
void duplicate(char* iofile)
{

  void toolscmd(char* arg1, char* arg2, char* arg3, char* arg4, char* arg5, char* arg6, char* arg7, char cmd[]);

  toolscmd(iofile, "\0", "\0", "\0", "\0", "\0", "\0", "DUPLICATE");
  return;
}

/* df-unduplicate() */
void unduplicate(char* iofile)
{

  void toolscmd(char* arg1, char* arg2, char* arg3, char* arg4, char* arg5, char* arg6, char* arg7, char cmd[]);

  toolscmd(iofile, "\0", "\0", "\0", "\0", "\0", "\0", "UNDUPLICATE");
  return;
}

/* df-reverse() */
void reverse(char* iofile)
{

  void toolscmd(char* arg1, char* arg2, char* arg3, char* arg4, char* arg5, char* arg6, char* arg7, char cmd[]);

  toolscmd(iofile, "\0", "\0", "\0", "\0", "\0", "\0", "REVERSE");
  return;
}

/* df-Right() ("right" is used elsewhere as an identifier) */
void Right(char* iofile)
{

  void toolscmd(char* arg1, char* arg2, char* arg3, char* arg4, char* arg5, char* arg6, char* arg7, char cmd[]);

  toolscmd(iofile, "\0", "\0", "\0", "\0", "\0", "\0", "RIGHT");
  return;
}

/* df-number() */
void number(char* outfile, char* first, char* last, char* incr)
{

  void toolscmd(char* arg1, char* arg2, char* arg3, char* arg4, char* arg5, char* arg6, char* arg7, char cmd[]);

  toolscmd(outfile, first, last, incr, "\0", "\0", "\0", "NUMBER");
  return;
}

/* df-unique() */
void unique(char* iofile)
{

  void toolscmd(char* arg1, char* arg2, char* arg3, char* arg4, char* arg5, char* arg6, char* arg7, char cmd[]);

  toolscmd(iofile, "\0", "\0", "\0", "\0", "\0", "\0", "UNIQUE");
  return;
}


/* df-type() */
void type(char* inpfile, char* n)
{

  void toolscmd(char* arg1, char* arg2, char* arg3, char* arg4, char* arg5, char* arg6, char* arg7, char cmd[]);

  toolscmd(inpfile, n, "\0", "\0", "\0", "\0", "\0", "TYPE");
  return;
}

/* End of the C functions emulating the Metamath tools commands */


/* df-toolscmd() */
void toolscmd(char* arg1, char* arg2, char* arg3, char* arg4, char* arg5, char* arg6, char* arg7, char cmd[])

/* tollscmd() is essentially the command() function of metamath,c with minor revisions to its tools

   commands section. These revisions are usually identified. Most of metamath,c
   is not utilized by completeusersproof(). */


/* parameters are of typ char* to insure compatibility with the string literals passed. */

{
  /* Command line user interface -- this is an infinite loop; it fetches and
     processes a command; returns only if the command is 'EXIT' or 'QUIT' and
     never returns otherwise. */


  /* argc and argv[] must be variablew local to main(). Therefore, they do not
     exist in substitute(). So we got an error message that they were not
	 declared in substitute.  But they occur in statements in substitute. We
	 correct this here by declaring them and assigning values to them.  The
	 reason substitute19.c ran is because these were declared as global
	 variables.  We deleted that with substitute190.c */

  int argc = 1;
  char* argv[] = { NULL , NULL };

  long argsProcessed = 0;  /* Number of argv initial command-line
                                     arguments processed so far */

  long c, i, j, k, m, l, n, p, q, r, s /*,tokenNum*/;
  long stmt, step;
  int subType = 0;
#define SYNTAX 4
  vstring str1 = "", str2 = "", str3 = "", str4 = "", str5= "";
  nmbrString *nmbrTmpPtr; /* Pointer only; not allocated directly */
  nmbrString *nmbrTmp = NULL_NMBRSTRING;
  nmbrString *nmbrSaveProof = NULL_NMBRSTRING;
  /*pntrString *pntrTmpPtr;*/ /* Pointer only; not allocated directly */
  pntrString *pntrTmp = NULL_PNTRSTRING;
  pntrString *expandedProof = NULL_PNTRSTRING;
  flag tmpFlag;

  /* 1-Nov-2013 nm proofSavedFlag tells us there was at least one
     SAVE NEW_PROOF during the MM-PA session while the UNDO stack wasn't
     empty, meaning that "UNDO stack empty" is no longer a reliable indication
     that the proof wasn't changed.  It is cleared upon entering MM-PA, and
     set by SAVE NEW_PROOF. */
  flag proofSavedFlag = 0;

  /* Variables for SHOW PROOF */
  flag pipFlag; /* Proof-in-progress flag */
  long startStep; long endStep;
  /* long startIndent; */
  long endIndent; /* Also for SHOW TRACE_BACK */
  flag essentialFlag; /* Also for SHOW TRACE_BACK */
  flag renumberFlag; /* Flag to use essential step numbering */
  flag unknownFlag;
  flag notUnifiedFlag;
  flag reverseFlag;
  long detailStep;
  flag noIndentFlag; /* Flag to use non-indented display */
  long splitColumn; /* Column at which formula starts in nonindented display */
  flag skipRepeatedSteps; /* NO_REPEATED_STEPS qualifier */ /* 28-Jun-2013 nm */
  flag texFlag; /* Flag for TeX */
  flag saveFlag; /* Flag to save in source */
  long indentation; /* Number of spaces to indent proof */
  vstring labelMatch = ""; /* SHOW PROOF <label> argument */

  flag axiomFlag; /* For SHOW TRACE_BACK */
  flag treeFlag; /* For SHOW TRACE_BACK */ /* 19-May-2013 nm */
  flag countStepsFlag; /* For SHOW TRACE_BACK */ /* 19-May-2013 nm */
  flag matchFlag; /* For SHOW TRACE_BACK */ /* 19-May-2013 nm */
  vstring matchList = "";  /* For SHOW TRACE_BACK */ /* 19-May-2013 nm */
  vstring traceToList = ""; /* For SHOW TRACE_BACK */ /* 18-Jul-2015 nm */
  flag recursiveFlag; /* For SHOW USAGE */
  long fromLine, toLine; /* For TYPE, SEARCH */
  flag joinFlag; /* For SEARCH */
  long searchWindow; /* For SEARCH */
  FILE *type_fp; /* For TYPE, SEARCH */

  long maxEssential; /* For MATCH */
  nmbrString *essentialFlags = NULL_NMBRSTRING;
                                            /* For ASSIGN/IMPROVE FIRST/LAST */
  long improveDepth; /* For IMPROVE */
  flag searchAlg; /* For IMPROVE */ /* 22-Aug-2012 nm */
  flag searchUnkSubproofs;  /* For IMPROVE */ /* 4-Sep-2012 nm */
  flag dummyVarIsoFlag; /* For IMPROVE */ /* 25-Aug-2012 nm */
  long improveAllIter; /* For IMPROVE ALL */ /* 25-Aug-2012 nm */
  flag proofStepUnk; /* For IMPROVE ALL */ /* 25-Aug-2012 nm */

  flag texHeaderFlag; /* For OPEN TEX, CLOSE TEX */
  flag commentOnlyFlag; /* For SHOW STATEMENT */
  flag briefFlag; /* For SHOW STATEMENT */
  flag linearFlag; /* For SHOW LABELS */
  vstring bgcolor = ""; /* For SHOW STATEMENT definition list */
                                                            /* 8-Aug-2008 nm */

  flag verboseMode, allowGrowthFlag /*, noDistinctFlag*/; /* For MINIMIZE_WITH */
  long prntStatus; /* For MINIMIZE_WITH */
  flag hasWildCard; /* For MINIMIZE_WITH */
  long exceptPos; /* For MINIMIZE_WITH */
  flag mathboxFlag; /* For MINIMIZE_WITH */ /* 28-Jun-2011 nm */
  long thisMathboxStmt; /* For MINIMIZE_WITH */ /* 14-Aug-2012 nm */
  flag forwFlag; /* For MINIMIZE_WITH */ /* 11-Nov-2011 nm */
  long forbidMatchPos;  /* For MINIMIZE_WITH */ /* 20-May-2013 nm */
  vstring forbidMatchList = "";  /* For MINIMIZE_WITH */ /* 20-May-2013 nm */
  long noNewAxiomsMatchPos;  /* For NO_NEW_AXIOMS_FROM */ /* 22-Nov-2014 nm */
  vstring noNewAxiomsMatchList = "";  /* For NO_NEW_AXIOMS_FROM */ /* 22-Nov-2014 */
  vstring traceProofFlags = ""; /* For NO_NEW_AXIOMS_FROM */ /* 22-Nov-2014 nm */
  vstring traceTrialFlags = ""; /* For NO_NEW_AXIOMS_FROM */ /* 22-Nov-2014 nm */

  struct pip_struct saveProofForReverting = {
       NULL_NMBRSTRING, NULL_PNTRSTRING, NULL_PNTRSTRING, NULL_PNTRSTRING };
                                   /* For MINIMIZE_WITH */ /* 20-May-2013 nm */
  long origCompressedLength; /* For MINIMIZE_WITH */ /* 25-Jun-2014 nm */
  long oldCompressedLength = 0; /* For MINIMIZE_WITH */ /* 25-Jun-2014 nm */
  long newCompressedLength = 0; /* For MINIMIZE_WITH */ /* 25-Jun-2014 nm */
  long forwardCompressedLength = 0; /* For MINIMIZE_WITH */ /* 25-Jun-2014 nm */
  long forwardLength = 0; /* For MINIMIZE_WITH */ /* 25-Jun-2014 nm */
  vstring saveZappedProofSectionPtr; /* Pointer only */ /* For MINIMIZE_WITH */
  long saveZappedProofSectionLen; /* For MINIMIZE_WITH */ /* 25-Jun-2014 nm */

  struct pip_struct saveOrigProof = {
       NULL_NMBRSTRING, NULL_PNTRSTRING, NULL_PNTRSTRING, NULL_PNTRSTRING };
                                   /* For MINIMIZE_WITH */ /* 25-Jun-2014 nm */
  struct pip_struct save1stPassProof = {
       NULL_NMBRSTRING, NULL_PNTRSTRING, NULL_PNTRSTRING, NULL_PNTRSTRING };
                                   /* For MINIMIZE_WITH */ /* 25-Jun-2014 nm */
  long forwRevPass; /* 1 = forward pass */ /* 25-Jun-2014 nm */

  flag showLemmas; /* For WRITE THEOREM_LIST */ /* 10-Oct-2012 nm */

  /* toolsMode-specific variables */
  flag commandProcessedFlag = 0; /* Set when the first command line processed;
                                    used to exit shell command line mode */
  FILE *list1_fp;
  FILE *list2_fp;
  FILE *list3_fp;
  vstring list2_fname = "", list2_ftmpname = "";
  vstring list3_ftmpname = "";
  vstring oldstr = "", newstr = "";
  long lines, changedLines, oldChangedLines, twoMatches, p1, p2;
  long firstChangedLine;
  flag cmdMode, changedFlag, outMsgFlag;
  double sum;
  vstring bufferedLine = "";
  vstring tagStartMatch = "";  /* 2-Jul-2011 nm For TAG command */
  long tagStartCount = 0;      /* 2-Jul-2011 nm For TAG command */
  vstring tagEndMatch = "";    /* 2-Jul-2011 nm For TAG command */
  long tagEndCount = 0;        /* 2-Jul-2011 nm For TAG command */
  long tagStartCounter = 0;    /* 2-Jul-2011 nm For TAG command */
  long tagEndCounter = 0;      /* 2-Jul-2011 nm For TAG command */

  /* 21-Jun-2014 */
  clock_t timePrevious = 0;  /* For SHOW ELAPSED_TIME command */
  clock_t timeNow = 0;       /* For SHOW ELAPSED_TIME command */

  /* Initialization to avoid compiler warning (should not be theoretically
     necessary) */
  p = 0;
  q = 0;
  s = 0;
  texHeaderFlag = 0;
  firstChangedLine = 0;
  tagStartCount = 0;           /* 2-Jul-2011 nm For TAG command */
  tagEndCount = 0;             /* 2-Jul-2011 nm For TAG command */
  tagStartCounter = 0;         /* 2-Jul-2011 nm For TAG command */
  tagEndCounter = 0;           /* 2-Jul-2011 nm For TAG command */

  while (1) {

    if (listMode) {
      /* If called from the OS shell with arguments, do one command
         then exit program. */
      /* (However, let a SUBMIT job complete) */
      if (argc > 1 && commandProcessedFlag &&
             commandFileNestingLevel == 0) return;
    }

    errorCount = 0; /* Reset error count before each read or proof parse. */

    /* Deallocate stuff that may have been used in previous pass */
    let(&str1,"");
    let(&str2,"");
    let(&str3,"");
    let(&str4,"");
    let(&str5,"");
    nmbrLet(&nmbrTmp, NULL_NMBRSTRING);
    pntrLet(&pntrTmp, NULL_PNTRSTRING);
    nmbrLet(&nmbrSaveProof, NULL_NMBRSTRING);
    nmbrLet(&essentialFlags, NULL_NMBRSTRING);
    j = nmbrLen(rawArgNmbr);
    if (j != rawArgs) bug(1110);
    j = pntrLen(rawArgPntr);
    if (j != rawArgs) bug(1111);
    rawArgs = 0;
    for (i = 0; i < j; i++) let((vstring *)(&rawArgPntr[i]), "");
    pntrLet(&rawArgPntr, NULL_PNTRSTRING);
    nmbrLet(&rawArgNmbr, NULL_NMBRSTRING);
    pntrLet(&fullArg, pntrSpace(50));
    j = pntrLen(fullArg);
    for (i = 0; i < j; i++) let((vstring *)(&fullArg[i]),"");
    /*pntrLet(&fullArg,NULL_PNTRSTRING); */
    j = pntrLen(expandedProof);
    if (j) {
      for (i = 0; i < j; i++) {
        let((vstring *)(&expandedProof[i]),"");
      }
     pntrLet(&expandedProof,NULL_PNTRSTRING);
    }

    let(&list2_fname, "");
    let(&list2_ftmpname, "");
    let(&list3_ftmpname, "");
    let(&oldstr, "");
    let(&newstr, "");
    let(&labelMatch, "");
    /* (End of space deallocation) */

    midiFlag = 0; /* 8/28/00 Initialize here in case SHOW PROOF exits early */

    if (memoryStatus) {
      /*??? Change to user-friendly message */
#ifdef THINK_C
     /* print2("Memory:  string %ld xxxString %ld free %ld\n",db,db3,(long)FreeMem()); */
      getPoolStats(&i, &j, &k);
      /* print2("Pool:  free alloc %ld  used alloc %ld  used actual %ld\n",i,j,k); */
#else
      /* print2("Memory:  string %ld xxxString %ld\n",db,db3); */
#endif
      getPoolStats(&i, &j, &k);
      /* print2("Pool:  free alloc %ld  used alloc %ld  used actual %ld\n",i,j,k); */
    }

    if (!toolsMode) {
      if (PFASmode) {
        let(&commandPrompt,"MM-PA> ");
      } else {
        let(&commandPrompt,"MM> ");
      }
    } else {
      if (listMode) {
        let(&commandPrompt,"Tools> ");
        goto toolssubstitute;
      } else {
        let(&commandPrompt,"TOOLS> ");
      }
    }

    let(&commandLine,""); /* Deallocate previous contents */

    if (!commandProcessedFlag && argc > 1 && argsProcessed < argc - 1
        && commandFileNestingLevel == 0) {
      /* if (toolsMode) { */  /* 10-Oct-2006 nm Fix bug: changed to listMode */
      if (listMode) {
        /* If program was compiled in TOOLS mode, the command-line argument
           is assumed to be a single TOOLS command; build the equivalent
           TOOLS command */
        for (i = 1; i < argc; i++) {
          argsProcessed++;
          /* Put quotes around an argument with spaces or tabs or quotes
             or empty string */
          if (instr(1, argv[i], " ") || instr(1, argv[i], "\t")
              || instr(1, argv[i], "\"") || instr(1, argv[i], "'")
              || (argv[i])[0] == 0) {
            /* If it contains a double quote, use a single quote */
            if (instr(1, argv[i], "\"")) {
              let(&str1, cat("'", argv[i], "'", NULL));
            } else {
              /* (??? (TODO)Case of both ' and " is not handled) */
              let(&str1, cat("\"", argv[i], "\"", NULL));
            }
          } else {
            let(&str1, argv[i]);
          }
          let(&commandLine, cat(commandLine, (i == 1) ? "" : " ", str1, NULL));
        }
      } else {
        /* If program was compiled in default (Metamath) mode, each command-line
           argument is considered a full Metamath command.  User is responsible
           for ensuring necessary quotes around arguments are passed in. */
        argsProcessed++;
        scrollMode = 0; /* Set continuous scrolling until completed */
        let(&commandLine, cat(commandLine, argv[argsProcessed], NULL));
        if (argc == 2 && instr(1, argv[1], " ") == 0) {
          /* Assume the user intended a READ command.  This special mode allows
             invocation via "metamath xxx.mm". */
          if (instr(1, commandLine, "\"") || instr(1, commandLine, "'")) {
            /* If it already has quotes don't put quotes */
            let(&commandLine, cat("READ ", commandLine, NULL));
          } else {
            /* Put quotes so / won't be interpreted as qualifier separator */
            let(&commandLine, cat("READ \"", commandLine, "\"", NULL));
          }
        }
      }
      /* print2("%s\n", cat(commandPrompt, commandLine, NULL)); */
    } else {
      /* Get command from user input or SUBMIT script file */
      commandLine = cmdInput1(commandPrompt);
    }
    if (argsProcessed == argc && !commandProcessedFlag) {
      commandProcessedFlag = 1;
      scrollMode = 1; /* Set prompted (default) scroll mode */
    }
    if (argsProcessed == argc - 1) {
      argsProcessed++; /* Indicates restore scroll mode next time around */
      if (toolsMode) {
        /* If program was compiled in TOOLS mode, we're only going to execute
           one command; set flag to exit next time around */
        commandProcessedFlag = 1;
      }
    }

    /* See if it's an operating system command */
    /* (This is a command line that begins with a quote) */
    if (commandLine[0] == '\'' || commandLine[0] == '\"') {
      /* See if this computer has this feature */
      if (!system(NULL)) {
        /* print2("?This computer does not accept an operating system command.\n"); */
        continue;
      } else {
        /* Strip off quote and trailing quote if any */
        let(&str1, right(commandLine, 2));
        if (commandLine[0]) { /* (Prevent stray pointer if empty string) */
          if (commandLine[0] == commandLine[strlen(commandLine) - 1]) {
            let(&str1, left(str1, (long)(strlen(str1)) - 1));
          }
        }
        /* Do the operating system command */
        system(str1);
#ifdef VAXC
        printf("\n"); /* Last line from VAX doesn't have new line */
#endif
        continue;
      }
    }

    parseCommandLine(commandLine);
    if (rawArgs == 0) {
      continue; /* Empty or comment line */
    }
    if (!processCommandLine()) {
      continue;
    }

    if (commandEcho || (toolsMode && listFile_fp != NULL)) {
      /* Build the complete command and print it for the user */
      k = pntrLen(fullArg);
      let(&str1,"");
      for (i = 0; i < k; i++) {
        if (instr(1, fullArg[i], " ") || instr(1, fullArg[i], "\t")
            || instr(1, fullArg[i], "\"") || instr(1, fullArg[i], "'")
            || ((char *)(fullArg[i]))[0] == 0) {
          /* If the argument has spaces or tabs or quotes
             or is empty string, put quotes around it */
          if (instr(1, fullArg[i], "\"")) {
            let(&str1, cat(str1, "'", fullArg[i], "' ", NULL));
          } else {
            /* (???Case of both ' and " is not handled) */
            let(&str1, cat(str1, "\"", fullArg[i], "\" ", NULL));
          }
        } else {
          let(&str1, cat(str1, fullArg[i], " ", NULL));
        }
      }
      let(&str1, left(str1, (long)(strlen(str1)) - 1)); /* Trim trailing spc */
      if (toolsMode && listFile_fp != NULL) {
        /* Put line in list.tmp as command */
        fprintf(listFile_fp, "%s\n", str1);  /* Print to list command file */
      }
      if (commandEcho) {
        /* 15-Jun-2009 nm Added code line below */
        /* Put special character "!" before line for easier extraction to
           build SUBMIT files; see also SET ECHO ON output below */
        let(&str1, cat("!", str1, NULL));
        /* The tilde is a special flag for printLongLine to print a
           tilde before the carriage return in a split line, not after */
        printLongLine(str1, "~", " ");
      }
    }

    if (cmdMatches("BEEP") || cmdMatches("B")) {
      /* Print a bell (if user types ahead "B", the bell lets him know when
         his command is finished - useful for long-running commands */
      print2("%c",7);
      continue;
    }

    if (cmdMatches("HELP")) {
      /* Build the complete command */
      k = pntrLen(fullArg);
      let(&str1,"");
      for (i = 0; i < k; i++) {
        let(&str1, cat(str1, fullArg[i], " ", NULL));
      }
      let(&str1, left(str1, (long)(strlen(str1)) - 1));
      if (toolsMode) {
        help0(str1);
        help1(str1);
      } else {
        help1(str1);
        help2(str1);
        help3(str1); /* 18-Jul-2015 nm */
      }
      continue;
    }


    if (cmdMatches("SET SCROLL")) {
      if (cmdMatches("SET SCROLL CONTINUOUS")) {
        scrollMode = 0;
        print2("Continuous scrolling is now in effect.\n");
      } else {
        scrollMode = 1;
        print2("Prompted scrolling is now in effect.\n");
      }
      continue;
    }

    if (cmdMatches("EXIT") || cmdMatches("QUIT")) {
    /*???        || !strcmp(cmd,"^Z")) { */

      if (toolsMode && !listMode) {
        /* Quitting tools command from within Metamath */
        if (!PFASmode) {
          print2(
 "Exiting the Text Tools.  Type EXIT again to exit Metamath.\n");
        } else {
          print2(
 "Exiting the Text Tools.  Type EXIT again to exit the Proof Assistant.\n");
        }

        continue;
      }

      if (PFASmode) {

        if (proofChanged &&
              /* If proofChanged, but the UNDO stack is empty (and
                 there were no other conditions such as stack overflow),
                 the proof didn't really change, so it is safe to
                 exit MM-PA without warning */
              (processUndoStack(NULL, PUS_GET_STATUS, "", 0)
                 /* However, if the proof was saved earlier, UNDO stack
                    empty no longer indicates proof didn't change */
                 || proofSavedFlag)) {
          print2(
              "Warning:  You have not saved changes to the proof of \"%s\".\n",
              statement[proveStatement].labelName); /* 21-Jan-06 nm */
          /* 17-Aug-04 nm Added / FORCE qualifier */
          if (switchPos("/ FORCE") == 0) {
            str1 = cmdInput1("Do you want to EXIT anyway (Y, N) <N>? ");
            if (str1[0] != 'y' && str1[0] != 'Y') {
              print2("Use SAVE NEW_PROOF to save the proof.\n");
              continue;
            }
          } else {
            /* User specified / FORCE, so answer question automatically */
            print2("Do you want to EXIT anyway (Y, N) <N>? Y\n");
          }
        }

        proofChanged = 0;
        processUndoStack(NULL, PUS_INIT, "", 0);
        proofSavedFlag = 0; /* Will become 1 if proof is ever saved */

        print2(
 "Exiting the Proof Assistant.  Type EXIT again to exit Metamath.\n");

        /* Deallocate proof structure */
        deallocProofStruct(&proofInProgress); /* 20-May-2013 nm */

        /**** old deallocation before 20-May-2013
        i = nmbrLen(proofInProgress.proof);
        nmbrLet(&proofInProgress.proof, NULL_NMBRSTRING);
        for (j = 0; j < i; j++) {
          nmbrLet((nmbrString **)(&((proofInProgress.target)[j])),
              NULL_NMBRSTRING);
          nmbrLet((nmbrString **)(&((proofInProgress.source)[j])),
              NULL_NMBRSTRING);
          nmbrLet((nmbrString **)(&((proofInProgress.user)[j])),
              NULL_NMBRSTRING);
        }
        pntrLet(&proofInProgress.target, NULL_PNTRSTRING);
        pntrLet(&proofInProgress.source, NULL_PNTRSTRING);
        pntrLet(&proofInProgress.user, NULL_PNTRSTRING);
        *** end of old code before 20-May-2013 */

        PFASmode = 0;
        continue;
      } else {
        if (sourceChanged) {
          print2("Warning:  You have not saved changes to the source.\n");
          /* 17-Aug-04 nm Added / FORCE qualifier */
          if (switchPos("/ FORCE") == 0) {
            str1 = cmdInput1("Do you want to EXIT anyway (Y, N) <N>? ");
            if (str1[0] != 'y' && str1[0] != 'Y') {
              print2("Use WRITE SOURCE to save the changes.\n");
              continue;
            }
          } else {
            /* User specified / FORCE, so answer question automatically */
            print2("Do you want to EXIT anyway (Y, N) <N>? Y\n");
          }
          sourceChanged = 0;
        }

        if (texFileOpenFlag) {
          print2("The %s file \"%s\" was closed.\n",
              htmlFlag ? "HTML" : "LaTeX", texFileName);
          printTexTrailer(texHeaderFlag);
          fclose(texFilePtr);
          texFileOpenFlag = 0;
        }
        if (logFileOpenFlag) {
          print2("The log file \"%s\" was closed %s %s.\n",logFileName,
              date(),time_());
          fclose(logFilePtr);
          logFileOpenFlag = 0;
        }
        return; /* Exit from program */
      }
    }

    if (cmdMatches("SUBMIT")) {
      if (commandFileNestingLevel == MAX_COMMAND_FILE_NESTING) {
        printf("?The SUBMIT nesting level has been exceeded.\n");
        continue;
      }
      commandFilePtr[commandFileNestingLevel + 1] = fSafeOpen(fullArg[1], "r");
      if (!commandFilePtr[commandFileNestingLevel + 1]) continue;
                                      /* Couldn't open (err msg was provided) */
      commandFileNestingLevel++;
      commandFileName[commandFileNestingLevel] = ""; /* Initialize if nec. */
      let(&commandFileName[commandFileNestingLevel], fullArg[1]);

      /* 23-Oct-2006 nm Added / SILENT */
      commandFileSilent[commandFileNestingLevel] = 0;
      if (switchPos("/ SILENT")
          || commandFileSilentFlag /* Propagate silence from outer level */) {
        commandFileSilent[commandFileNestingLevel] = 1;
      } else {
        commandFileSilent[commandFileNestingLevel] = 0;
      }
      commandFileSilentFlag = commandFileSilent[commandFileNestingLevel];
      if (!commandFileSilentFlag)
        print2("Taking command lines from file \"%s\"...\n",
            commandFileName[commandFileNestingLevel]);

      continue;
    }
    toolssubstitute:
    if (toolsMode) {
      /* Start of toolsMode-specific commands */
#define ADD_MODE 1
#define DELETE_MODE 2
#define CLEAN_MODE 3
#define SUBSTITUTE_MODE 4
#define SWAP_MODE 5
#define INSERT_MODE 6
#define BREAK_MODE 7
#define BUILD_MODE 8
#define MATCH_MODE 9
#define RIGHT_MODE 10
#define TAG_MODE 11  /* 2-Jul-2011 nm Added TAG command */


	  /* initialize fullArg[i} then assign to fullArg[i] the toolscmd() parameters. */

      let((vstring*)(&fullArg[1]),"");
      let((vstring*)(&fullArg[2]),"");
	  let((vstring*)(&fullArg[3]),"");
	  let((vstring*)(&fullArg[4]),"");
	  let((vstring*)(&fullArg[5]),"");
	  let((vstring*)(&fullArg[6]),"");
	  let((vstring*)(&fullArg[7]),"");

	  let((vstring*)(&fullArg[1]),(vstring)(arg1));
      let((vstring*)(&fullArg[2]),(vstring)(arg2));
	  let((vstring*)(&fullArg[3]),(vstring)(arg3));
	  let((vstring*)(&fullArg[4]),(vstring)(arg4));
	  let((vstring*)(&fullArg[5]),(vstring)(arg5));
	  let((vstring*)(&fullArg[6]),(vstring)(arg6));
	  let((vstring*)(&fullArg[7]),(vstring)(arg7));


	  cmdMode = 0;
      if (cmd == "ADD") cmdMode = ADD_MODE;
      else if (cmd == "DELETE") cmdMode = DELETE_MODE;
      else if (cmd == "CLEAN") cmdMode = CLEAN_MODE;
      else if (cmd == "SUBSTITUTE") cmdMode = SUBSTITUTE_MODE;
      else if (cmd == "SWAP") cmdMode = SWAP_MODE;
      else if (cmd == "INSERT") cmdMode = INSERT_MODE;
      else if (cmd == "BREAK") cmdMode = BREAK_MODE;
      else if (cmd == "BUILD") cmdMode = BUILD_MODE;
      else if (cmd == "MATCH") cmdMode = MATCH_MODE;
      else if (cmd == "RIGHT") cmdMode = RIGHT_MODE;
      else if (cmd == "TAG") cmdMode = TAG_MODE;


      if (cmdMode) {
        list1_fp = fSafeOpen(fullArg[1], "r");
        if (!list1_fp) continue; /* Couldn't open it (error msg was provided) */
        if (cmdMode == RIGHT_MODE) {
          /* Find the longest line */
          p = 0;
          while (linput(list1_fp, NULL, &str1)) {
            if (p < (signed)(strlen(str1))) p = (long)(strlen(str1));
          }
          rewind(list1_fp);
        }
        let(&list2_fname, fullArg[1]);
        if (list2_fname[strlen(list2_fname) - 2] == '~') {
          let(&list2_fname, left(list2_fname, (long)(strlen(list2_fname)) - 2));
          /* print2("The output file will be called %s.\n", list2_fname); */
        }
        let(&list2_ftmpname, "");
        list2_ftmpname = fGetTmpName("zz~tools");
        list2_fp = fSafeOpen(list2_ftmpname, "w");
        if (!list2_fp) continue; /* Couldn't open it (error msg was provided) */
        lines = 0;
        changedLines = 0;
        twoMatches = 0;
        changedFlag = 0;
        outMsgFlag = 0;

        switch (cmdMode) {
          case ADD_MODE:
            break;
          case TAG_MODE:  /* 2-Jul-2011 nm Added TAG command */
            let(&tagStartMatch, fullArg[4]);
            tagStartCount = (long)val(fullArg[5]);
            if (tagStartCount == 0) tagStartCount = 1; /* Default */
            let(&tagEndMatch, fullArg[6]);
            tagEndCount = (long)val(fullArg[7]);
            if (tagEndCount == 0) tagEndCount = 1; /* Default */
            tagStartCounter = 0;
            tagEndCounter = 0;
            break;
          case DELETE_MODE:
            break;
          case CLEAN_MODE:
            let(&str4, edit(fullArg[2], 32));
            q = 0;
            if (instr(1, str4, "P") > 0) q = q + 1;
            if (instr(1, str4, "D") > 0) q = q + 2;
            if (instr(1, str4, "G") > 0) q = q + 4;
            if (instr(1, str4, "B") > 0) q = q + 8;
            if (instr(1, str4, "R") > 0) q = q + 16;
            if (instr(1, str4, "C") > 0) q = q + 32;
            if (instr(1, str4, "E") > 0) q = q + 128;
            if (instr(1, str4, "Q") > 0) q = q + 256;
            if (instr(1, str4, "L") > 0) q = q + 512;
            if (instr(1, str4, "T") > 0) q = q + 1024;
            if (instr(1, str4, "U") > 0) q = q + 2048;
            if (instr(1, str4, "V") > 0) q = q + 4096;
            break;
          case SUBSTITUTE_MODE:
            let(&newstr, fullArg[3]); /* The replacement string */
            if (((vstring)(fullArg[4]))[0] == 'A' ||
                ((vstring)(fullArg[4]))[0] == 'a') { /* ALL */
              q = -1;
            } else {
              q = (long)val(fullArg[4]);
              if (q == 0) q = 1;    /* The occurrence # of string to subst */
            }
            s = instr(1, fullArg[2], "\\n");
            if (s) {
              /*s = 1;*/ /* Replace lf flag */
              q = 1; /* Only 1st occurrence makes sense in this mode */
            }
            if (!strcmp(fullArg[3], "\\n")) {
              let(&newstr, "\n"); /* Replace with lf */
            }
            break;
          case SWAP_MODE:
            break;
          case INSERT_MODE:
            p = (long)val(fullArg[3]);
            break;
          case BREAK_MODE:
            outMsgFlag = 1;
            break;
          case BUILD_MODE:
            let(&str4, "");
            outMsgFlag = 1;
            break;
          case MATCH_MODE:
            outMsgFlag = 1;
        } /* End switch */
        let(&bufferedLine, "");
        /*
        while (linput(list1_fp, NULL, &str1)) {
        */
        while (1) {
          if (bufferedLine[0]) {
            /* Get input from buffered line (from rejected \n replacement) */
            let(&str1, bufferedLine);
            let(&bufferedLine, "");
          } else {
            if (!linput(list1_fp, NULL, &str1)) break;
          }
          lines++;
          oldChangedLines = changedLines;
          let(&str2, str1);
          switch (cmdMode) {
            case ADD_MODE:
              let(&str2, cat(fullArg[2], str1, fullArg[3], NULL));
              if (strcmp(str1, str2)) changedLines++;
              break;
            case TAG_MODE:   /* 2-Jul-2011 nm Added TAG command */
              if (tagStartCounter < tagStartCount) {
                if (instr(1, str1, tagStartMatch)) tagStartCounter++;
              }
              if (tagStartCounter == tagStartCount &&
                  tagEndCounter < tagEndCount) { /* We're in tagging range */
                let(&str2, cat(fullArg[2], str1, fullArg[3], NULL));
                if (strcmp(str1, str2)) changedLines++;
                if (instr(1, str1, tagEndMatch)) tagEndCounter++;
              }
              break;
            case DELETE_MODE:
              p1 = instr(1, str1, fullArg[2]);
              if (strlen(fullArg[2]) == 0) p1 = 1;
              p2 = instr(p1, str1, fullArg[3]);
              if (strlen(fullArg[3]) == 0) p2 = (long)strlen(str1) + 1;
              if (p1 != 0 && p2 != 0) {
                let(&str2, cat(left(str1, p1 - 1), right(str1, p2
                    + (long)strlen(fullArg[3])), NULL));
                changedLines++;
              }
              break;
            case CLEAN_MODE:
              if (q) {
                let(&str2, edit(str1, q));
                if (strcmp(str1, str2)) changedLines++;
              }
              break;
            case SUBSTITUTE_MODE:
              let(&str2, str1);
              p = 0;
              p1 = 0;

              k = 1;
              /* See if an additional match on line is required */
              if (((vstring)(fullArg[5]))[0] != 0) {
                if (!instr(1, str2, fullArg[5])) {
                  /* No match on line; prevent any substitution */
                  k = 0;
                }
              }

              if (s && k) { /* We're asked to replace a newline char */
                /* Read in the next line */
                /*
                if (linput(list1_fp, NULL, &str4)) {
                  let(&str2, cat(str1, "\\n", str4, NULL));
                */
                if (linput(list1_fp, NULL, &bufferedLine)) {
                  /* Join the next line and see if the string matches */
                  if (instr(1, cat(str1, "\\n", bufferedLine, NULL),
                      fullArg[2])) {
                    let(&str2, cat(str1, "\\n", bufferedLine, NULL));
                    let(&bufferedLine, "");
                  } else {
                    k = 0; /* No match - leave bufferedLine for next pass */
                  }
                } else { /* EOF reached */
                  /* print2("Warning: file %s has an odd number of lines\n",
                      fullArg[1]); */
                }
              }

              while (k) {
                p1 = instr(p1 + 1, str2, fullArg[2]);
                if (!p1) break;
                p++;
                if (p == q || q == -1) {
                  let(&str2, cat(left(str2, p1 - 1), newstr,
                      right(str2, p1 + (long)strlen(fullArg[2])), NULL));
                  if (newstr[0] == '\n') {
                    /* Replacement string is an lf */
                    lines++;
                    changedLines++;
                  }
                  /* 14-Sep-2010 nm Continue the search after the replacement
                     string, so that "SUBST 1.tmp abbb ab a ''" will change
                     "abbbab" to "abba" rather than "aa" */
                  p1 = p1 + (long)strlen(newstr) - 1;
                  /* p1 = p1 - (long)strlen(fullArg[2]) + (long)strlen(newstr); */ /* bad */
                  if (q != -1) break;
                }
              }
              if (strcmp(str1, str2)) changedLines++;
              break;
            case SWAP_MODE:
              p1 = instr(1, str1, fullArg[2]);
              if (p1) {
                p2 = instr(p1 + 1, str1, fullArg[2]);
                if (p2) twoMatches++;
                let(&str2, cat(right(str1, p1) + (long)strlen(fullArg[2]),
                    fullArg[2], left(str1, p1 - 1), NULL));
                if (strcmp(str1, str2)) changedLines++;
              }
              break;
            case INSERT_MODE:
              if ((signed)(strlen(str2)) < p - 1)
                let(&str2, cat(str2, space(p - 1 - (long)strlen(str2)), NULL));
              let(&str2, cat(left(str2, p - 1), fullArg[2],
                  right(str2, p), NULL));
              if (strcmp(str1, str2)) changedLines++;
              break;
            case BREAK_MODE:
              let(&str2, str1);
              changedLines++;
              for (i = 0; i < (signed)(strlen(fullArg[2])); i++) {
                p = 0;
                while (1) {
                  p = instr(p + 1, str2, chr(((vstring)(fullArg[2]))[i]));
                  if (!p) break;
                  /* Put spaces arount special one-char tokens */
                  let(&str2, cat(left(str2, p - 1), " ",
                      mid(str2, p, 1),
                      " ", right(str2, p + 1), NULL));
                  p++;
                }
              }
              let(&str2, edit(str2, 8 + 16 + 128)); /* Reduce & trim spaces */
              for (p = (long)strlen(str2) - 1; p >= 0; p--) {
                if (str2[p] == ' ') {
                  str2[p] = '\n';
                  changedLines++;
                }
              }
              if (!str2[0]) changedLines--; /* Don't output blank line */
              break;
            case BUILD_MODE:
              if (str2[0] != 0) { /* Ignore blank lines */
                if (str4[0] == 0) {
                  let(&str4, str2);
                } else {
                  if ((long)strlen(str4) + (long)strlen(str2) > 72) {
                    let(&str4, cat(str4, "\n", str2, NULL));
                    changedLines++;
                  } else {
                    let(&str4, cat(str4, " ", str2, NULL));
                  }
                }
                p = instr(1, str4, "\n");
                if (p) {
                  let(&str2, left(str4, p - 1));
                  let(&str4, right(str4, p + 1));
                } else {
                  let(&str2, "");
                }
              }
              break;
            case MATCH_MODE:
              if (((vstring)(fullArg[2]))[0] == 0) {
                /* Match any non-blank line */
                p = str1[0];
              } else {
                p = instr(1, str1, fullArg[2]);
              }
              if (((vstring)(fullArg[3]))[0] == 'n' ||
                  ((vstring)(fullArg[3]))[0] == 'N') {
                p = !p;
              }
              if (p) changedLines++;
              break;
            case RIGHT_MODE:
              let(&str2, cat(space(p - (long)strlen(str2)), str2, NULL));
              if (strcmp(str1, str2)) changedLines++;
              break;
          } /* End switch(cmdMode) */
          if (lines == 1) let(&str3, left(str2, 79)); /* For msg */
          if (oldChangedLines != changedLines && !changedFlag) {
            changedFlag = 1;
            let(&str3, left(str2, 79)); /* For msg */
            firstChangedLine = lines;
            if ((cmdMode == SUBSTITUTE_MODE && newstr[0] == '\n')
                || cmdMode == BUILD_MODE) /* Joining lines */ {
              firstChangedLine = 1; /* Better message */
            }
          }
          if (((cmdMode != BUILD_MODE && cmdMode != BREAK_MODE)
              || str2[0] != 0)
              && (cmdMode != MATCH_MODE || p))
            fprintf(list2_fp, "%s\n", str2);
        } /* Next input line */
        if (cmdMode == BUILD_MODE) {
          if (str4[0]) {
            /* Output last partial line */
            fprintf(list2_fp, "%s\n", str4);
            changedLines++;
            if (!str3[0]) {
              let(&str3, str4); /* For msg */
            }
          }
        }
        /* Remove any lines after lf for readability of msg */
        p = instr(1, str3, "\n");
        if (p) let(&str3, left(str3, p - 1));
        if (!outMsgFlag) {
          /* 18-Aug-2011 nm Make message depend on line counts */
          if (!changedFlag) {
            if (!lines) {
              /* print2("The file %s has no lines.\n", fullArg[1]); */
            } else {
             /* print2(
"The file %s has %ld line%s; none were changed.  First line:\n",
                list2_fname, lines, (lines == 1) ? "" : "s");
              print2("%s\n", str3); */
            }
          } else {
         /*   print2(
"The file %s has %ld line%s; %ld w%s changed.  First changed line is %ld:\n",
                list2_fname,
                lines,  (lines == 1) ? "" : "s",
                changedLines,  (changedLines == 1) ? "as" : "ere",
                firstChangedLine);
            print2("%s\n", str3); */
          }
          if (twoMatches > 0) {
            /* For SWAP command */
      /*      print2(
"Warning:  %ld line%s more than one \"%s\".  The first one was used.\n",
                twoMatches, (twoMatches == 1) ? " has" : "s have", fullArg[2]); */
          }
        } else {
          /* if (changedLines == 0) let(&str3, ""); */
      /*    print2(
"The input had %ld line%s, the output has %ld line%s.%s\n",
              lines, (lines == 1) ? "" : "s",
              changedLines, (changedLines == 1) ? "" : "s",
              (changedLines == 0) ? "" : " First output line:"); */
          /* if (changedLines != 0) print2("%s\n", str3); */
        }
        fclose(list1_fp);
        fclose(list2_fp);
        fSafeRename(list2_ftmpname, list2_fname);
        /* Deallocate string memory */
        let(&tagStartMatch, "");  /* 2-Jul-2011 nm Added TAG command */
        let(&tagEndMatch, "");  /* 2-Jul-2011 nm Added TAG command */
        return;
      } /* end if cmdMode for ADD, etc. */

#define SORT_MODE 1
#define UNDUPLICATE_MODE 2
#define DUPLICATE_MODE 3
#define UNIQUE_MODE 4
#define REVERSE_MODE 5

      cmdMode = 0;
      if (cmd == "SORT") cmdMode = SORT_MODE;
      else if (cmd == "UNDUPLICATE") cmdMode = UNDUPLICATE_MODE;
      else if (cmd == "DUPLICATE") cmdMode = DUPLICATE_MODE;
      else if (cmd == "UNIQUE") cmdMode = UNIQUE_MODE;
      else if (cmd == "REVERSE") cmdMode = REVERSE_MODE;

     /* cmdMode = 0;
      if (cmdMatches("SORT")) cmdMode = SORT_MODE;
      else if (cmdMatches("UNDUPLICATE")) cmdMode = UNDUPLICATE_MODE;
      else if (cmdMatches("DUPLICATE")) cmdMode = DUPLICATE_MODE;
      else if (cmdMatches("UNIQUE")) cmdMode = UNIQUE_MODE;
      else if (cmdMatches("REVERSE")) cmdMode = REVERSE_MODE;

	  This section of code is changed. cmd is passed through toolscmd as cmd1. It is
	  of type vstring. This character string specifies the tools command to be processed.
	  It is specified by its name.  For example, if the substitute command is to be executed
	  cmd1 = "SUBSTITUTE" . */

      if (cmdMode) {
        list1_fp = fSafeOpen(fullArg[1], "r");
        if (!list1_fp) continue; /* Couldn't open it (error msg was provided) */
        let(&list2_fname, fullArg[1]);
        if (list2_fname[strlen(list2_fname) - 2] == '~') {
          let(&list2_fname, left(list2_fname, (long)strlen(list2_fname) - 2));
         /* print2("The output file will be called %s.\n", list2_fname); */
        }
        let(&list2_ftmpname, "");
        list2_ftmpname = fGetTmpName("zz~tools");
        list2_fp = fSafeOpen(list2_ftmpname, "w");
        if (!list2_fp) continue; /* Couldn't open it (error msg was provided) */

        /* Count the lines */
        lines = 0;
        while (linput(list1_fp, NULL, &str1)) lines++;
        if (cmdMode != SORT_MODE  && cmdMode != REVERSE_MODE) {
         /* print2("The input file has %ld lines.\n", lines); */
        }

        /* Close and reopen the input file */
        fclose(list1_fp);
        list1_fp = fSafeOpen(fullArg[1], "r");
        /* Allocate memory */
        pntrLet(&pntrTmp, pntrSpace(lines));
        /* Assign the lines to string array */
        for (i = 0; i < lines; i++) linput(list1_fp, NULL,
            (vstring *)(&pntrTmp[i]));

        /* Sort */
        if (cmdMode != REVERSE_MODE) {
          if (cmdMode == SORT_MODE) {
            qsortKey = fullArg[2]; /* Do not deallocate! */
          } else {
            qsortKey = "";
          }
          qsort(pntrTmp, (size_t)lines, sizeof(void *), qsortStringCmp);
        } else { /* Reverse the lines */
          for (i = lines / 2; i < lines; i++) {
            qsortKey = pntrTmp[i]; /* Use qsortKey as handy tmp var here */
            pntrTmp[i] = pntrTmp[lines - 1 - i];
            pntrTmp[lines - 1 - i] = qsortKey;
          }
        }

        /* Output sorted lines */
        changedLines = 0;
        let(&str3, "");
        for (i = 0; i < lines; i++) {
          j = 0; /* Flag that line should be printed */
          switch (cmdMode) {
            case SORT_MODE:
            case REVERSE_MODE:
              j = 1;
              break;
            case UNDUPLICATE_MODE:
              if (i == 0) {
                j = 1;
              } else {
                if (strcmp((vstring)(pntrTmp[i - 1]), (vstring)(pntrTmp[i]))) {
                  j = 1;
                }
              }
              break;
            case DUPLICATE_MODE:
              if (i > 0) {
                if (!strcmp((vstring)(pntrTmp[i - 1]), (vstring)(pntrTmp[i]))) {
                  if (i == lines - 1) {
                    j = 1;
                  } else {
                    if (strcmp((vstring)(pntrTmp[i]),
                        (vstring)(pntrTmp[i + 1]))) {
                      j = 1;
                    }
                  }
                }
              }
              break;
            case UNIQUE_MODE:
              if (i < lines - 1) {
                if (strcmp((vstring)(pntrTmp[i]), (vstring)(pntrTmp[i + 1]))) {
                  if (i == 0) {
                    j = 1;
                  } else {
                    if (strcmp((vstring)(pntrTmp[i - 1]),
                        (vstring)(pntrTmp[i]))) {
                      j = 1;
                    }
                  }
                }
              } else {
                if (i == 0) {
                  j = 1;
                } else {
                  if (strcmp((vstring)(pntrTmp[i - 1]),
                        (vstring)(pntrTmp[i]))) {
                      j = 1;
                  }
                }
              }
              break;
          } /* end switch (cmdMode) */
          if (j) {
            fprintf(list2_fp, "%s\n", (vstring)(pntrTmp[i]));
            changedLines++;
            if (changedLines == 1)
              let(&str3, left((vstring)(pntrTmp[i]), 79));
          }
        } /* next i */
        /* print2("The output file has %ld lines.  The first line is:\n",
            changedLines); */
        /* print2("%s\n", str3); */

        /* Deallocate memory */
        for (i = 0; i < lines; i++) let((vstring *)(&pntrTmp[i]), "");
        pntrLet(&pntrTmp,NULL_PNTRSTRING);

        fclose(list1_fp);
        fclose(list2_fp);
        fSafeRename(list2_ftmpname, list2_fname);
        break; /* continue; */ /* Changed continue to break for CUP.c */
      } /* end if cmdMode for SORT, etc. */

      if (cmd == "PARALLEL") {    /* if (cmdMatches("PARALLEL")) { */
        list1_fp = fSafeOpen(fullArg[1], "r");
        list2_fp = fSafeOpen(fullArg[2], "r");
        if (!list1_fp) continue; /* Couldn't open it (error msg was provided) */
        if (!list2_fp) continue; /* Couldn't open it (error msg was provided) */
        let(&list3_ftmpname, "");
        list3_ftmpname = fGetTmpName("zz~tools");
        list3_fp = fSafeOpen(list3_ftmpname, "w");
        if (!list3_fp) continue; /* Couldn't open it (error msg was provided) */

        p1 = 1; p2 = 1; /* not eof */
        p = 0; q = 0; /* lines */
        j = 0; /* 1st line flag */
        let(&str3, "");
        while (1) {
          let(&str1, "");
          if (p1) {
            p1 = linput(list1_fp, NULL, &str1);
            if (p1) p++;
            else let(&str1, "");
          }
          let(&str2, "");
          if (p2) {
            p2 = linput(list2_fp, NULL, &str2);
            if (p2) q++;
            else let(&str2, "");
          }
          if (!p1 && !p2) break;
          let(&str4, cat(str1, fullArg[4], str2, NULL));
          fprintf(list3_fp, "%s\n", str4);
          if (!j) {
            let(&str3, str4); /* Save 1st line for msg */
            j = 1;
          }
        }
       /* if (p == q) {
          print2(
"The input files each had %ld lines.  The first output line is:\n", p);
        } else {
          print2(
"Warning: file \"%s\" had %ld lines while file \"%s\" had %ld lines.\n",
              fullArg[1], p, fullArg[2], q);
          if (p < q) p = q;
          print2("The output file \"%s\" has %ld lines.  The first line is:\n",
              fullArg[3], p);
        }
        print2("%s\n", str3); */

        fclose(list1_fp);
        fclose(list2_fp);
        fclose(list3_fp);
        fSafeRename(list3_ftmpname, fullArg[3]);
        break; /* continue; */ /* Changed continue to break for CUP.c */
      }


      if (cmd == "NUMBER") {
        list1_fp = fSafeOpen(fullArg[1], "w");
        if (!list1_fp) continue; /* Couldn't open it (error msg was provided) */
        j = (long)strlen(str(val(fullArg[2])));
        k = (long)strlen(str(val(fullArg[3])));
        if (k > j) j = k;
        for (i = (long)val(fullArg[2]); i <= val(fullArg[3]);
            i = i + (long)val(fullArg[4])) {
          let(&str1, str(i));
          fprintf(list1_fp, "%s\n", str1);
        }
        fclose(list1_fp);
        continue;
      }

      if (cmd == "COUNT") {
        list1_fp = fSafeOpen(fullArg[1], "r");
        if (!list1_fp) continue; /* Couldn't open it (error msg was provided) */
        p1 = 0;
        p2 = 0;
        lines = 0;
        q = 0; /* Longest line length */
        i = 0; /* First longest line */
        j = 0; /* Number of longest lines */
        sum = 0.0; /* Sum of numeric content of lines */
        firstChangedLine = 0;
        while (linput(list1_fp, NULL, &str1)) {
          lines++;

          /* Longest line */
          if (q < (signed)(strlen(str1))) {
            q = (long)strlen(str1);
            let(&str4, str1);
            i = lines;
            j = 0;
          }

          if (q == (signed)(strlen(str1))) {
            j++;
          }

          if (instr(1, str1, fullArg[2])) {
            if (!firstChangedLine) {
              firstChangedLine = lines;
              let(&str3, str1);
            }
            p1++;
            p = 0;
            while (1) {
              p = instr(p + 1, str1, fullArg[2]);
              if (!p) break;
              p2++;
            }
          }
          sum = sum + (long)val(str1);
        }
        /* print2(
"The file has %ld lines.  The string \"%s\" occurs %ld times on %ld lines.\n",
            lines, fullArg[2], p2, p1); */
        if (firstChangedLine) {
       /*   print2("The first occurrence is on line %ld:\n", firstChangedLine); */
         /* print2("%s\n", str3); */
        }
        /* print2(
"The first longest line (out of %ld) is line %ld and has %ld characters:\n",
            j, i, q); */
        printLongLine(str4, "    "/*startNextLine*/, ""/*breakMatch*/);
            /* breakMatch empty means break line anywhere */  /* 6-Dec-03 */
 /* print2("If each line were a number, their sum would be %s\n", str(sum)); */
        printLongLine(cat(
            "Stripping all but digits, \".\", and \"-\", the sum of lines is ",
            str(sum), NULL), "    ", " ");
        fclose(list1_fp);
        continue;
      }

      if (cmd == "COUNT") {
        list1_fp = fSafeOpen(fullArg[1], "r");
        if (!list1_fp) continue; /* Couldn't open it (error msg was provided) */
        if (rawArgs == 2) {
          n = 10;
        } else {
          if (((vstring)(fullArg[2]))[0] == 'A' ||
              ((vstring)(fullArg[2]))[0] == 'a') { /* ALL */
            n = -1;
          } else {
            n = (long)val(fullArg[2]);
          }
        }
        for (i = 0; i < n || n == -1; i++) {
          if (!linput(list1_fp, NULL, &str1)) break;
          if (!print2("%s\n", str1)) break;
        }
        fclose(list1_fp);
        continue;
      } /* end TYPE */

      if (cmd == "UPDATE") {
        list1_fp = fSafeOpen(fullArg[1], "r");
        list2_fp = fSafeOpen(fullArg[2], "r");
        if (!list1_fp) continue; /* Couldn't open it (error msg was provided) */
        if (!list2_fp) continue; /* Couldn't open it (error msg was provided) */
        if (!getRevision(fullArg[4])) {
          print2(
"?The revision tag must be of the form /*nn*/ or /*#nn*/.  Please try again.\n");
          continue;
        }
        let(&list3_ftmpname, "");
        list3_ftmpname = fGetTmpName("zz~tools");
        list3_fp = fSafeOpen(list3_ftmpname, "w");
        if (!list3_fp) continue; /* Couldn't open it (error msg was provided) */

        revise(list1_fp, list2_fp, list3_fp, fullArg[4],
            (long)val(fullArg[5]));

        fSafeRename(list3_ftmpname, fullArg[3]);
        continue;
      }

      if (cmd == "COPY") {
        let(&list2_ftmpname, "");
        list2_ftmpname = fGetTmpName("zz~tools");
        list2_fp = fSafeOpen(list2_ftmpname, "w");
        if (!list2_fp) continue; /* Couldn't open it (error msg was provided) */
        let(&str4, cat(fullArg[1], ",", NULL));
        lines = 0;
        j = 0; /* Error flag */
        while (1) {
          if (!str4[0]) break; /* Done scanning list */
          p = instr(1, str4, ",");
          let(&str3, left(str4, p - 1));
          let(&str4, right(str4, p + 1));
          list1_fp = fSafeOpen((str3), "r");
          if (!list1_fp) { /* Couldn't open it (error msg was provided) */
            j = 1; /* Error flag */
            break;
          }
          n = 0;
          while (linput(list1_fp, NULL, &str1)) {
            lines++; n++;
            fprintf(list2_fp, "%s\n", str1);
          }
          if (instr(1, fullArg[1], ",")) { /* More than 1 input file */
            /* print2("The input file \"%s\" has %ld lines.\n", str3, n); */
          }
          fclose(list1_fp);
        }
        if (j) continue; /* One of the input files couldn't be opened */
        fclose(list2_fp);
       /* print2("The output file \"%s\" has %ld lines.\n", fullArg[2], lines); */
        fSafeRename(list2_ftmpname, fullArg[2]);
        return;
      }

      /* print2("?This command has not been implemented yet.\n"); */
      continue;
    } /* End of toolsMode-specific commands */
    return;
    if (cmdMatches("TOOLS")) {
      /* print2(
"Entering the Text Tools utilities.  Type HELP for help, EXIT to exit.\n"); */
      toolsMode = 1;
      continue;
    }

    if (cmdMatches("READ")) {
      if (statements) {
        printLongLine(cat(
            "?Sorry, reading of more than one source file is not allowed.  ",
            "The file \"", input_fn, "\" has already been READ in.  ",
                                                             /* 11-Dec-05 nm */
            "You may type ERASE to start over.  Note that additional source ",
        "files may be included in the source file with \"$[ <filename> $]\".",
                                                             /* 11-Dec-05 nm */
            NULL),"  "," ");
        continue;
      }
      let(&input_fn, fullArg[1]);
      input_fp = fSafeOpen(input_fn, "r");
      if (!input_fp) continue; /* Couldn't open it (error msg was provided) */

      fclose(input_fp);
      readInput();
      if (switchPos("/ VERIFY")) {
        verifyProofs("*",1); /* Parse and verify */
      } else {
        /* verifyProofs("*",0); */ /* Parse only (for gross error checking) */
      }

      /* 10/21/02 - detect Microsoft bugs reported by several users, when the
         HTML output files are named "con.html" etc. */
      /* If we want a standard error message underlining token, this could go
         in mmpars.c */
      /* From Microsoft's site:
         "The following reserved words cannot be used as the name of a file:
         CON, PRN, AUX, CLOCK$, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7,
         COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, and LPT9.
         Also, reserved words followed by an extension - for example,
         NUL.tx7 - are invalid file names." */
      /* Check for labels that will lead to illegal Microsoft file names for
         Windows users.  Don't bother checking CLOCK$ since $ is already
         illegal */
      let(&str1, cat(
         ",CON,PRN,AUX,NUL,COM1,COM2,COM3,COM4,COM5,COM6,COM7,",
         "COM8,COM9,LPT1,LPT2,LPT3,LPT4,LPT5,LPT6,LPT7,LPT8,LPT9,", NULL));
      for (i = 1; i <= statements; i++) {
        let(&str2, cat(",", edit(statement[i].labelName, 32/*uppercase*/), ",",
            NULL));
        if (instr(1, str1, str2) ||
            /* 5-Jan-04 mm*.html is reserved for mmtheorems.html, etc. */
            !strcmp(",MM", left(str2, 3))) {
          print2("\n");
          printLongLine(cat("?Error in statement \"",
              statement[i].labelName, "\" at line ", str(statement[i].lineNum),
              " in file \"", statement[i].fileName,
              "\".  To workaround a Microsoft operating system limitation, the",
              " the following reserved words cannot be used for label names:",
              " CON, PRN, AUX, CLOCK$, NUL, COM1, COM2, COM3, COM4, COM5,",
              " COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6,",
              " LPT7, LPT8, and LPT9.  Also, \"mm*.html\" is reserved for",
              " Metamath file names.  Use another name for this label.", NULL),
              "", " ");
          errorCount++;
        }
      }
      /* 10/21/02 end */

      if (!errorCount) {
        let(&str1, "No errors were found.");
        if (!switchPos("/ VERIFY")) {
            let(&str1, cat(str1,
       "  However, proofs were not checked.  Type VERIFY PROOF *",
       " if you want to check them.",
            NULL));
        }
        printLongLine(str1, "", " ");
      } else {
        print2("\n");
        if (errorCount == 1) {
          print2("One error was found.\n");
        } else {
          print2("%ld errors were found.\n", (long)errorCount);
        }
      }

      continue;
    }

    if (cmdMatches("WRITE SOURCE")) {
      let(&output_fn, fullArg[2]);
      output_fp = fSafeOpen(output_fn, "w");
      if (!output_fp) continue; /* Couldn't open it (error msg was provided)*/

      /* Added 24-Oct-03 nm */
      if (switchPos("/ CLEAN") > 0) {
        c = 1; /* Clean out any proof-in-progress (that user has flagged
                        with a ? in its date comment field) */
      } else {
        c = 0; /* Output all proofs (normal) */
      }

      /* Added 12-Jun-2011 nm */
      if (switchPos("/ REWRAP") > 0) {
        r = 2; /* Re-wrap then format (more aggressive than / FORMAT) */
      } else if (switchPos("/ FORMAT") > 0) {
        r = 1; /* Format output according to set.mm standard */
      } else {
        r = 0; /* Keep formatting as-is */
      }

      writeInput((char)c, (char)r); /* Added arg 24-Oct-03 nm 12-Jun-2011 nm */
      fclose(output_fp);
      if (c == 0) sourceChanged = 0; /* Don't unset flag if CLEAN option
                                    since some new proofs may not be saved. */
      continue;
    } /* End of WRITE SOURCE */


    if (cmdMatches("WRITE THEOREM_LIST")) {
      /* 4-Dec-03 - Write out an HTML summary of the theorems to
         mmtheorems.html, mmtheorems2.html,... */
      /* THEOREMS_PER_PAGE is the default number of proof descriptions to output. */
#define THEOREMS_PER_PAGE 100
      /* i is the actual number of proof descriptions to output. */
      /* See if the user overrode the default. */
      i = switchPos("/ THEOREMS_PER_PAGE");
      if (i) {
        i = (long)val(fullArg[i + 1]); /* Use user's value */
      } else {
        i = THEOREMS_PER_PAGE; /* Use the default value */
      }
      showLemmas = (switchPos("/ SHOW_LEMMAS") != 0);

      if (!texDefsRead) {
        htmlFlag = 1;
        print2("Reading definitions from $t statement of %s...\n", input_fn);
        if (!readTexDefs()) {
          continue; /* An error occurred */
        }
      } else {
        /* Current limitation - can only read def's from .mm file once */
        if (!htmlFlag) {
          print2("?You cannot use both LaTeX and HTML in the same session.\n");
          print2(
              "You must EXIT and restart Metamath to switch to the other.\n");
          continue;
        }
      }
      /* Output the theorem list */
      writeTheoremList(i, showLemmas); /* (located in mmwtex.c) */
      continue;
    }  /* End of "WRITE THEOREM_LIST" */


    if (cmdMatches("WRITE BIBLIOGRAPHY")) {
      /* 10/10/02 -
         This utility builds the bibliographical cross-references to various
         textbooks and updates the user-specified file normally called
         mmbiblio.html.
       */
      tmpFlag = 0; /* Error flag to recover input file */
      list1_fp = fSafeOpen(fullArg[2], "r");
      if (list1_fp == NULL) {
        /* Couldn't open it (error msg was provided)*/
        continue;
      }
      fclose(list1_fp);
      /* This will rename the input mmbiblio.html as mmbiblio.html~1 */
      list2_fp = fSafeOpen(fullArg[2], "w");
      if (list2_fp == NULL) {
          /* Couldn't open it (error msg was provided)*/
        continue;
      }
      /* Note: in older versions the "~1" string was OS-dependent, but we
         don't support VAX or THINK C anymore...  Anyway we reopen it
         here with the renamed file in case the OS won't let us rename
         an opened file during the fSafeOpen for write above. */
      list1_fp = fSafeOpen(cat(fullArg[2], "~1", NULL), "r");
      if (list1_fp == NULL) bug(1116);
      if (!texDefsRead) {
        htmlFlag = 1;
        print2("Reading definitions from $t statement of %s...\n", input_fn);
        if (!readTexDefs()) {
          tmpFlag = 1; /* Error flag to recover input file */
          goto bib_error; /* An error occurred */
        }
      }

      /* Transfer the input file up to the special "<!-- #START# -->" comment */
      while (1) {
        if (!linput(list1_fp, NULL, &str1)) {
          print2(
"?Error: Could not find \"<!-- #START# -->\" line in input file \"%s\".\n",
              fullArg[2]);
          tmpFlag = 1; /* Error flag to recover input file */
          break;
        }
        fprintf(list2_fp, "%s\n", str1);
        if (!strcmp(str1, "<!-- #START# -->")) break;
      }
      if (tmpFlag) goto bib_error;

      p2 = 1; /* Pass 1 or 2 flag */
      lines = 0;
      while (1) {

        if (p2 == 2) {  /* Pass 2 */
          /* Allocate memory for sorting */
          pntrLet(&pntrTmp, pntrSpace(lines));
          lines = 0;
        }

        /* Scan all $a and $p statements */
        for (i = 1; i <= statements; i++) {
          if (statement[i].type != (char)p_ &&
            statement[i].type != (char)a_) continue;
          /* Omit ...OLD (obsolete) and ...NEW (to be implemented) statements */
          if (instr(1, statement[i].labelName, "NEW")) continue;
          if (instr(1, statement[i].labelName, "OLD")) continue;
          let(&str1, "");
          str1 = getDescription(i); /* Get the statement's comment */
          if (!instr(1, str1, "[")) continue;
          l = (signed)(strlen(str1));
          for (j = 0; j < l; j++) {
            if (str1[j] == '\n') str1[j] = ' '; /* Change newlines to spaces */
            if (str1[j] == '\r') bug(1119);
          }
          let(&str1, edit(str1, 8 + 128 + 16)); /* Reduce & trim whitespace */

          /* 15-Apr-2015 nm */
          /* Clear out math symbols in backquotes to prevent false matches
             to [reference] bracket */
          k = 0; /* Math symbol mode if 1 */

          l = (signed)(strlen(str1));
          for (j = 0; j < l - 1; j++) {
            if (k == 0) {
              if (str1[j] == '`') {
                k = 1; /* Start of math mode */
              }
            } else { /* In math mode */
              if (str1[j] == '`') { /* A backquote */
                if (str1[j + 1] == '`') {
                  /* It is an escaped backquote */
                  str1[j] = ' ';
                  str1[j + 1] = ' ';
                } else {
                  k = 0; /* End of math mode */
                }
              } else { /* Not a backquote */
                str1[j] = ' '; /* Clear out the math mode part */
              }
            } /* end if k == 0 */
          } /* next j */

          /* Put spaces before page #s (up to 4 digits) for sorting */
          j = 0;
          while (1) {
            j = instr(j + 1, str1, " p. "); /* Heuristic - match " p. " */
            if (!j) break;
            if (j) {
              for (k = j + 4; k <= (signed)(strlen(str1)) + 1; k++) {
                if (!isdigit((unsigned char)(str1[k - 1]))) {
                  let(&str1, cat(left(str1, j + 2),
                      space(4 - (k - (j + 4))), right(str1, j + 3), NULL));
                  /* Add ### after page number as marker */
                  let(&str1, cat(left(str1, j + 7), "###", right(str1, j + 8),
                      NULL));
                  break;
                }
              }
            }
          }
          /* Process any bibliographic references in comment */
          j = 0;
          n = 0;
          while (1) {
            j = instr(j + 1, str1, "["); /* Find reference (not robust) */
            if (!j) break;
            if (!isalnum((unsigned char)(str1[j]))) continue; /* Not start of reference */
            n++;
            /* Backtrack from [reference] to a starting keyword */
            m = 0;
            let(&str2, edit(str1, 32)); /* to uppercase */
            for (k = j - 1; k >= 1; k--) {
              /* ??? Future: make this a loop from a list that can be
                 printed in the error message if not found */
              if (0
                  || !strcmp(mid(str2, k, (long)strlen("THEOREM")), "THEOREM")
                  || !strcmp(mid(str2, k, (long)strlen("LEMMA")), "LEMMA")
                  || !strcmp(mid(str2, k, (long)strlen("LEMMAS")), "LEMMAS")
                  || !strcmp(mid(str2, k, (long)strlen("DEFINITION")), "DEFINITION")
                  || !strcmp(mid(str2, k, (long)strlen("COMPARE")), "COMPARE")
                  || !strcmp(mid(str2, k, (long)strlen("PROPOSITION")), "PROPOSITION")
                  || !strcmp(mid(str2, k, (long)strlen("COROLLARY")), "COROLLARY")
                  || !strcmp(mid(str2, k, (long)strlen("AXIOM")), "AXIOM")
                  || !strcmp(mid(str2, k, (long)strlen("RULE")), "RULE")
                  || !strcmp(mid(str2, k, (long)strlen("REMARK")), "REMARK")
                  || !strcmp(mid(str2, k, (long)strlen("EXERCISE")), "EXERCISE")
                  || !strcmp(mid(str2, k, (long)strlen("PROBLEM")), "PROBLEM")
                  || !strcmp(mid(str2, k, (long)strlen("NOTATION")), "NOTATION")
                  || !strcmp(mid(str2, k, (long)strlen("EXAMPLE")), "EXAMPLE")
                  || !strcmp(mid(str2, k, (long)strlen("PART")), "PART")
                  || !strcmp(mid(str2, k, (long)strlen("SECTION")), "SECTION")
                  || !strcmp(mid(str2, k, (long)strlen("PROPERTY")), "PROPERTY")
                  || !strcmp(mid(str2, k, (long)strlen("FIGURE")), "FIGURE")
                  || !strcmp(mid(str2, k, (long)strlen("POSTULATE")), "POSTULATE")
                  || !strcmp(mid(str2, k, (long)strlen("EQUATION")), "EQUATION")
                  || !strcmp(mid(str2, k, (long)strlen("SCHEME")), "SCHEME")
                  || !strcmp(mid(str2, k, (long)strlen("ITEM")), "ITEM")
                  || !strcmp(mid(str2, k, (long)strlen("LINE")), "LINE")
                  || !strcmp(mid(str2, k, (long)strlen("LINES")), "LINES")
                  /* Don't use SCHEMA since we may have THEOREM SCHEMA
                  || !strcmp(mid(str2, k, (long)strlen("SCHEMA")), "SCHEMA")
                  */
                  || !strcmp(mid(str2, k, (long)strlen("CHAPTER")), "CHAPTER")
                  ) {
                m = k;
                break;
              }
              let(&str3, ""); /* Clear tmp alloc stack created by "mid" */
            }
            if (!m) {
              if (p2 == 1) {
                print2("?Warning: No keyword match in \"%s\".\n",
                  statement[i].labelName);
                /* ??? Future - show the actual list */
                print2(
       "?(See metamath.c source for list of keywords THEOREM, LEMMA, etc.)\n");
              }
              continue; /* Not a bib ref - ignore */
            }
            /* m is at the start of a keyword */
            p = instr(m, str1, "["); /* Start of bibliograpy reference */
            q = instr(p, str1, "]"); /* End of bibliography reference */
            if (!q) {
              if (p2 == 1) print2(
                  "?Warning: Reference not found in HTML file in \"%s\".\n",
                  statement[i].labelName);
              continue; /* Not a bib ref - ignore */
            }
            s = instr(q, str1, "###"); /* Page number marker */
            if (!s) {
              if (p2 == 1) print2(
                  "?Warning: No page number after reference in \"%s\".\n",
                  statement[i].labelName);
              continue; /* No page number given - ignore */
            }
            /* Now we have a real reference; increment reference count */
            lines++;
            if (p2 == 1) continue; /* In 1st pass, we just count refs */

            let(&str2, seg(str1, m, p - 1));     /* "Theorem #" */
            let(&str3, seg(str1, p + 1, q - 1));  /* "[bibref]" w/out [] */
            let(&str4, seg(str1, q + 1, s - 1)); /* " p. nnnn" */
            str2[0] = (char)(toupper((unsigned char)(str2[0])));
            /* Eliminate noise like "of" in "Theorem 1 of [bibref]" */
            for (k = (long)strlen(str2); k >=1; k--) {
              if (0
                  || !strcmp(mid(str2, k, (long)strlen(" of ")), " of ")
                  || !strcmp(mid(str2, k, (long)strlen(" in ")), " in ")
                  || !strcmp(mid(str2, k, (long)strlen(" from ")), " from ")
                  || !strcmp(mid(str2, k, (long)strlen(" on ")), " on ")
                  ) {
                let(&str2, left(str2, k - 1));
                break;
              }
              let(&str2, str2);
            }

            let(&newstr, "");
            newstr = pinkHTML(i); /* Get little pink number */
            let(&oldstr, cat(
                /* Construct the sorting key */
                /* The space() helps Th. 9 sort before Th. 10 on same page */
                str3, " ", str4, space(20 - (long)strlen(str2)), str2,
                "|||",  /* ||| means end of sort key */
                /* Construct just the statement href for combining dup refs */
                "<A HREF=\"", statement[i].labelName,
                ".html\">", statement[i].labelName, "</A>",
                newstr,
                "&&&",  /* &&& means end of statement href */
                /* Construct actual HTML table row (without ending tag
                   so duplicate references can be added) */

                /*
                (i < extHtmlStmt) ?
                   "<TR>" :

                   cat("<TR BGCOLOR=", PURPLISH_BIBLIO_COLOR, ">", NULL),
                */

                /* 29-Jul-2008 nm Sandbox stuff */
                (i < extHtmlStmt)
                   ? "<TR>"
                   : (i < sandboxStmt)
                       ? cat("<TR BGCOLOR=", PURPLISH_BIBLIO_COLOR, ">", NULL)
                       : cat("<TR BGCOLOR=", SANDBOX_COLOR, ">", NULL),

                "<TD NOWRAP>[<A HREF=\"",

                /*
                (i < extHtmlStmt) ?
                   htmlBibliography :
                   extHtmlBibliography,
                */

                /* 29-Jul-2008 nm Sandbox stuff */
                (i < extHtmlStmt)
                   ? htmlBibliography
                   : (i < sandboxStmt)
                       ? extHtmlBibliography
                       /* Note that the sandbox uses the mmset.html
                          bibliography */
                       : htmlBibliography,

                "#",
                str3,
                "\">", str3, "</A>]", str4,
                "</TD><TD>", str2, "</TD><TD><A HREF=\"",
                statement[i].labelName,
                ".html\">", statement[i].labelName, "</A>",
                newstr, NULL));
            /* Put construction into string array for sorting */
            let((vstring *)(&pntrTmp[lines - 1]), oldstr);
          }
        }
        /* 'lines' should be the same in both passes */
        print2("Pass %ld finished.  %ld references were processed.\n", p2, lines);
        if (p2 == 2) break;
        p2++;    /* Increment from pass 1 to pass 2 */
      }

      /* Sort */
      qsortKey = "";
      qsort(pntrTmp, (size_t)lines, sizeof(void *), qsortStringCmp);

      /* Combine duplicate references */
      let(&str1, "");  /* Last biblio ref */
      for (i = 0; i < lines; i++) {
        j = instr(1, (vstring)(pntrTmp[i]), "|||");
        let(&str2, left((vstring)(pntrTmp[i]), j - 1));
        if (!strcmp(str1, str2)) {
          n++;
          /* Combine last with this */
          k = instr(j, (vstring)(pntrTmp[i]), "&&&");
          /* Extract statement href */
          let(&str3, seg((vstring)(pntrTmp[i]), j + 3, k -1));
          let((vstring *)(&pntrTmp[i]),
              cat((vstring)(pntrTmp[i - 1]), " &nbsp;", str3, NULL));
          let((vstring *)(&pntrTmp[i - 1]), ""); /* Clear previous line */
        }
        let(&str1, str2);
      }

      /* Write output */
      n = 0;
      for (i = 0; i < lines; i++) {
        j = instr(1, (vstring)(pntrTmp[i]), "&&&");
        if (j) {  /* Don't print blanked out combined lines */
          n++;
          /* Take off prefixes and reduce spaces */
          let(&str1, edit(right((vstring)(pntrTmp[i]), j + 3), 16));
          j = 1;
          /* Break up long lines for text editors */
          let(&printString, "");
          outputToString = 1;
          printLongLine(cat(str1, "</TD></TR>", NULL),
              " ",  /* Start continuation line with space */
              "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
          outputToString = 0;
          fprintf(list2_fp, "%s", printString);
          let(&printString, "");
        }
      }


      /* Discard the input file up to the special "<!-- #END# -->" comment */
      while (1) {
        if (!linput(list1_fp, NULL, &str1)) {
          print2(
"?Error: Could not find \"<!-- #END# -->\" line in input file \"%s\".\n",
              fullArg[2]);
          tmpFlag = 1; /* Error flag to recover input file */
          break;
        }
        if (!strcmp(str1, "<!-- #END# -->")) {
          fprintf(list2_fp, "%s\n", str1);
          break;
        }
      }
      if (tmpFlag) goto bib_error;

      /* Transfer the rest of the input file */
      while (1) {
        if (!linput(list1_fp, NULL, &str1)) {
          break;
        }

        /* Update the date stamp at the bottom of the HTML page. */
        /* This is just a nicety; no error check is done. */
        if (!strcmp("This page was last updated on ", left(str1, 30))) {
          let(&str1, cat(left(str1, 30), date(), ".", NULL));
        }


        fprintf(list2_fp, "%s\n", str1);
      }

      print2("%ld table rows were written.\n", n);
      /* Deallocate string array */
      for (i = 0; i < lines; i++) let((vstring *)(&pntrTmp[i]), "");
      pntrLet(&pntrTmp,NULL_PNTRSTRING);


     bib_error:
      fclose(list1_fp);
      fclose(list2_fp);
      if (tmpFlag) {
        /* Recover input files in case of error */
        remove(fullArg[2]);  /* Delete output file */
        rename(cat(fullArg[2], "~1", NULL), fullArg[2]);
            /* Restore input file name */
        print2("?The file \"%s\" was not modified.\n", fullArg[2]);
      }
      continue;
    }  /* End of "WRITE BIBLIOGRAPHY" */


    if (cmdMatches("WRITE RECENT_ADDITIONS")) {
      /* 18-Sep-03 -
         This utility creates a list of recent proof descriptions and updates
         the user-specified file normally called mmrecent.html.
       */

      /* RECENT_COUNT is the default number of proof descriptions to output. */
#define RECENT_COUNT 100
      /* i is the actual number of proof descriptions to output. */
      /* See if the user overrode the default. */
      i = switchPos("/ LIMIT");
      if (i) {
        i = (long)val(fullArg[i + 1]); /* Use user's value */
      } else {
        i = RECENT_COUNT; /* Use the default value */
      }

      if (!texDefsRead) {
        htmlFlag = 1;
        print2("Reading definitions from $t statement of %s...\n", input_fn);
        if (!readTexDefs()) {
          continue; /* An error occurred */
        }
      } else {
        /* Current limitation - can only read def's from .mm file once */
        if (!htmlFlag) {
          print2("?You cannot use both LaTeX and HTML in the same session.\n");
          print2(
              "You must EXIT and restart Metamath to switch to the other.\n");
          continue;
        }
      }

      tmpFlag = 0; /* Error flag to recover input file */
      list1_fp = fSafeOpen(fullArg[2], "r");
      if (list1_fp == NULL) {
        /* Couldn't open it (error msg was provided)*/
        continue;
      }
      fclose(list1_fp);
      /* This will rename the input mmrecent.html as mmrecent.html~1 */
      list2_fp = fSafeOpen(fullArg[2], "w");
      if (list2_fp == NULL) {
          /* Couldn't open it (error msg was provided)*/
        continue;
      }
      /* Note: in older versions the "~1" string was OS-dependent, but we
         don't support VAX or THINK C anymore...  Anyway we reopen it
         here with the renamed file in case the OS won't let us rename
         an opened file during the fSafeOpen for write above. */
      list1_fp = fSafeOpen(cat(fullArg[2], "~1", NULL), "r");
      if (list1_fp == NULL) bug(1117);

      /* Transfer the input file up to the special "<!-- #START# -->" comment */
      while (1) {
        if (!linput(list1_fp, NULL, &str1)) {
          print2(
"?Error: Could not find \"<!-- #START# -->\" line in input file \"%s\".\n",
              fullArg[2]);
          tmpFlag = 1; /* Error flag to recover input file */
          break;
        }
        /* 13-May-04 nm Put in "last updated" stamp */
        if (!strcmp(left(str1, 21), "<!-- last updated -->")) {
          let(&str1, cat(left(str1, 21), " <I>Last updated on ", date(),
          /* ??Future: make "EDT"/"EST" or other automatic */
          /*  " at ", time_(), " EST.</I>", NULL)); */
          /* 10-Nov-04 nm Just make it "ET" for "Eastern Time" */
            " at ", time_(), " ET.</I>", NULL));
        }
        fprintf(list2_fp, "%s\n", str1);
        if (!strcmp(str1, "<!-- #START# -->")) break;
      }
      if (tmpFlag) goto wrrecent_error;

      /* Get and parse today's date */
      let(&str1, date());
      j = instr(1, str1, "-");
      k = (long)val(left(str1, j - 1)); /* Day */
#define MONTHS "JanFebMarAprMayJunJulAugSepOctNovDec"
      l = ((instr(1, MONTHS, mid(str1, j + 1, 3)) - 1) / 3) + 1; /* 1 = Jan */
      m = str1[j + 6]; /* Character after 2-digit year */  /* nm 10-Apr-06 */
      if (m == ' ' || m == ']') {                          /* nm 10-Apr-06 */
        /* Handle 2-digit year */
        m = (long)val(mid(str1, j + 5, 2));  /* Year */
#define START_YEAR 93 /* Earliest 19xx year in set.mm database */
        if (m < START_YEAR) {
          m = m + 2000;
        } else {
          m = m + 1900;
        }
      } else {                                            /* nm 10-Apr-06 */
        /* Handle 4-digit year */                         /* nm 10-Apr-06 */
        m = (long)val(mid(str1, j + 5, 4));  /* Year */         /* nm 10-Apr-06 */
      }                                                   /* nm 10-Apr-06 */

      n = 0; /* Count of how many output so far */
      while (n < i /*RECENT_COUNT*/ && m > START_YEAR + 1900 - 1) {
        /* Build date string to match */

        /* Match for 2-digit year */
        let(&str1, cat("$([", str(k), "-", mid(MONTHS, 3 * l - 2, 3), "-",
            right(str(m), 3), "]$)", NULL));
        /* Match for 2-digit year */  /* nm 10-Apr-06 */
        let(&str5, cat("$([", str(k), "-", mid(MONTHS, 3 * l - 2, 3), "-",
            str(m), "]$)", NULL));
        for (s = statements; s >= 1; s--) {

          if (statement[s].type != (char)p_) continue;

          /* Get the comment section after the statement */
          let(&str2, space(statement[s + 1].labelSectionLen));
          memcpy(str2, statement[s + 1].labelSectionPtr,
              (size_t)(statement[s + 1].labelSectionLen));
          p = instr(1, str2, "$)");
          let(&str2, left(str2, p + 1)); /* Get 1st comment (if any) */
          let(&str2, edit(str2, 2)); /* Discard spaces */
          /* See if the date comment matches */
          if (instr(1, str2, str1) || instr(1, str2, str5)) { /* 10-Apr-06 */
            /* We have a match, so increment the match count */
            n++;
            let(&str3, "");
            str3 = getDescription(s);
            let(&str4, "");
            str4 = pinkHTML(s); /* Get little pink number */
            /* Output the description comment */
            /* Break up long lines for text editors with printLongLine */
            let(&printString, "");
            outputToString = 1;
            print2("\n"); /* Blank line for HTML human readability */
            printLongLine(cat(

                  /*
                  (s < extHtmlStmt) ?
                       "<TR>" :
                       cat("<TR BGCOLOR=", PURPLISH_BIBLIO_COLOR, ">", NULL),
                  */

                  /* 29-Jul-2008 nm Sandbox stuff */
                  (s < extHtmlStmt)
                     ? "<TR>"
                     : (s < sandboxStmt)
                         ? cat("<TR BGCOLOR=", PURPLISH_BIBLIO_COLOR, ">",
                             NULL)
                         : cat("<TR BGCOLOR=", SANDBOX_COLOR, ">", NULL),

                  "<TD NOWRAP>",  /* IE breaks up the date */
                  /* mid(str1, 4, (long)strlen(str1) - 6), */ /* Date */
                  /* Use 4-digit year */   /* 10-Apr-06 */
                  mid(str5, 4, (long)strlen(str5) - 6), /* Date */  /* 10-Apr-06 */
                  "</TD><TD ALIGN=CENTER><A HREF=\"",
                  statement[s].labelName, ".html\">",
                  statement[s].labelName, "</A>",
                  str4, "</TD><TD ALIGN=LEFT>", NULL),  /* Description */
                              /* 28-Dec-05 nm Added ALIGN=LEFT for IE */
                " ",  /* Start continuation line with space */
                "\""); /* Don't break inside quotes e.g. "Arial Narrow" */

            showStatement = s; /* For printTexComment */
            outputToString = 0; /* For printTexComment */
            texFilePtr = list2_fp;
            /* 18-Sep-03 ???Future - make this just return a string??? */
            printTexComment(str3, 0); /* Sends result to texFilePtr */
            texFilePtr = NULL;
            outputToString = 1; /* Restore after printTexComment */

            /* Get HTML hypotheses => assertion */
            let(&str4, "");
            str4 = getTexOrHtmlHypAndAssertion(s); /* In mmwtex.c */
            printLongLine(cat("</TD></TR><TR",

                  /*
                  (s < extHtmlStmt) ?
                       ">" :
                       cat(" BGCOLOR=", PURPLISH_BIBLIO_COLOR, ">", NULL),
                  */

                  /* 29-Jul-2008 nm Sandbox stuff */
                  (s < extHtmlStmt)
                     ? ">"
                     : (s < sandboxStmt)
                         ? cat(" BGCOLOR=", PURPLISH_BIBLIO_COLOR, ">",
                             NULL)
                         : cat(" BGCOLOR=", SANDBOX_COLOR, ">", NULL),

                /*** old
                "<TD BGCOLOR=white>&nbsp;</TD><TD COLSPAN=2 ALIGN=CENTER>",
                str4, "</TD></TR>", NULL),
                ****/
                /* 27-Oct-03 nm */
                "<TD COLSPAN=3 ALIGN=CENTER>",
                str4, "</TD></TR>", NULL),

                " ",  /* Start continuation line with space */
                "\""); /* Don't break inside quotes e.g. "Arial Narrow" */

            outputToString = 0;
            fprintf(list2_fp, "%s", printString);
            let(&printString, "");

            if (n >= i /*RECENT_COUNT*/) break; /* We're done */

            /* 27-Oct-03 nm Put separator row if not last theorem */
            outputToString = 1;
            printLongLine(cat("<TR BGCOLOR=white><TD COLSPAN=3>",
                "<FONT SIZE=-3>&nbsp;</FONT></TD></TR>", NULL),
                " ",  /* Start continuation line with space */
                "\""); /* Don't break inside quotes e.g. "Arial Narrow" */

            /* 29-Jul-04 nm Put the previous, current, and next statement
               labels in HTML comments so a script can use them to update
               web site incrementally */
            /* This section can be deleted without side effects */
            /* Find the previous statement with a web page */
            j = 0;
            for (q = s - 1; q >= 1; q--) {
              if (statement[q].type == (char)p_ ||
                  statement[q].type == (char)a_ ) {
                j = q;
                break;
              }
            }
            if (j) print2("<!-- For script: %s -->\n", statement[j].labelName);
            /* Current statement */
            print2("<!-- For script: %s -->\n", statement[s].labelName);
            /* Find the next statement with a web page */
            j = 0;
            for (q = s + 1; q <= statements; q++) {
              if (statement[q].type == (char)p_ ||
                  statement[q].type == (char)a_ ) {
                j = q;
                break;
              }
            }
            if (j) print2("<!-- For script: %s -->\n", statement[j].labelName);
            /* End of 29-Jul-04 section */

            outputToString = 0;
            fprintf(list2_fp, "%s", printString);
            let(&printString, "");

          }
        } /* Next s - statement number */
        /* Decrement date */
        if (k > 1) {
          k--; /* Decrement day */
        } else {
          k = 31; /* Non-existent day 31's will never match, which is OK */
          if (l > 1) {
            l--; /* Decrement month */
          } else {
            l = 12; /* Dec */
            m --; /* Decrement year */
          }
        }
      } /* next while - Scan next date */


      /* Discard the input file up to the special "<!-- #END# -->" comment */
      while (1) {
        if (!linput(list1_fp, NULL, &str1)) {
          print2(
"?Error: Could not find \"<!-- #END# -->\" line in input file \"%s\".\n",
              fullArg[2]);
          tmpFlag = 1; /* Error flag to recover input file */
          break;
        }
        if (!strcmp(str1, "<!-- #END# -->")) {
          fprintf(list2_fp, "%s\n", str1);
          break;
        }
      }
      if (tmpFlag) goto wrrecent_error;

      /* Transfer the rest of the input file */
      while (1) {
        if (!linput(list1_fp, NULL, &str1)) {
          break;
        }

        /* Update the date stamp at the bottom of the HTML page. */
        /* This is just a nicety; no error check is done. */
        if (!strcmp("This page was last updated on ", left(str1, 30))) {
          let(&str1, cat(left(str1, 30), date(), ".", NULL));
        }

        fprintf(list2_fp, "%s\n", str1);
      }

      print2("The %ld most recent theorem(s) were written.\n", n);

     wrrecent_error:
      fclose(list1_fp);
      fclose(list2_fp);
      if (tmpFlag) {
        /* Recover input files in case of error */
        remove(fullArg[2]);  /* Delete output file */
        rename(cat(fullArg[2], "~1", NULL), fullArg[2]);
            /* Restore input file name */
        print2("?The file \"%s\" was not modified.\n", fullArg[2]);
      }
      continue;
    }  /* End of "WRITE RECENT_ADDITIONS" */


    if (cmdMatches("SHOW LABELS")) {
        linearFlag = 0;
        if (switchPos("/ LINEAR")) linearFlag = 1;
        if (switchPos("/ ALL")) {
          m = 1;  /* Include $e, $f statements */
          print2(
  "The labels that match are shown with statement number, label, and type.\n");
        } else {
          m = 0;  /* Show $a, $p only */
          print2(
"The assertions that match are shown with statement number, label, and type.\n");
        }
        j = 0;
        k = 0;
        for (i = 1; i <= statements; i++) {
          if (!statement[i].labelName[0]) continue; /* No label */
          if (!m && statement[i].type != (char)p_ &&
              statement[i].type != (char)a_) continue; /* No /ALL switch */
          /* 30-Jan-06 nm Added single-character-match wildcard argument */
          if (!matchesList(statement[i].labelName, fullArg[2], '*', '?')) {
            continue;
          }
          let(&str1,cat(str(i)," ",
              statement[i].labelName," $",chr(statement[i].type)," ",NULL));
#define COL 19 /* Characters per column */
          if (j + (long)strlen(str1) > MAX_LEN
              || (linearFlag && j != 0)) { /* j != 0 to suppress 1st CR */
            print2("\n");
            j = 0;
            k = 0;
          }
          if (strlen(str1) > COL || linearFlag) {
            j = j + (long)strlen(str1);
            k = k + (long)strlen(str1) - COL;
            print2(str1);
          } else {
            if (k == 0) {
              j = j + COL;
              print2("%s%s",str1,space(COL - (long)strlen(str1)));
            } else {
              k = k - (COL - (long)strlen(str1));
              if (k > 0) {
                print2(str1);
                j = j + (long)strlen(str1);
              } else {
                print2("%s%s",str1,space(COL - (long)strlen(str1)));
                j = j + COL;
                k = 0;
              }
            }
          }
        }
        print2("\n");
        continue;
    }

    if (cmdMatches("SHOW SOURCE")) {

      /* 14-Sep-2012 nm */
      /* Currently, SHOW SOURCE only handles one statement at a time,
         so use getStatementNum().  Eventually, SHOW SOURCE may become
         obsolete; I don't think anyone uses it. */
      s = getStatementNum(fullArg[2],
          1/*startStmt*/,
          statements + 1  /*maxStmt*/,
          1/*aAllowed*/,
          1/*pAllowed*/,
          1/*eAllowed*/,
          1/*fAllowed*/,
          0/*efOnlyForMaxStmt*/,
          1/*uniqueFlag*/);
      if (s == -1) {
        continue; /* Error msg was provided */
      }
      showStatement = s; /* Update for future defaults */

      /*********** 14-Sep-2012 replaced by getStatementNum()
      for (i = 1; i <= statements; i++) {
        if (!strcmp(fullArg[2],statement[i].labelName)) break;
      }
      if (i > statements) {
        printLongLine(cat("?There is no statement with label \"",
            fullArg[2], "\".  ",
            "Use SHOW LABELS for a list of valid labels.", NULL), "", " ");
        showStatement = 0;
        continue;
      }
      showStatement = i;
      ************** end 14-Sep-2012 *******/

      let(&str1, "");
      str1 = outputStatement(showStatement, 0 /* cleanFlag */,
          0 /* reformatFlag */);
      let(&str1,edit(str1,128)); /* Trim trailing spaces */
      if (str1[strlen(str1)-1] == '\n') let(&str1, left(str1,
          (long)strlen(str1) - 1));
      printLongLine(str1, "", "");
      let(&str1,""); /* Deallocate vstring */
      continue;
    } /* if (cmdMatches("SHOW SOURCE")) */


    if (cmdMatches("SHOW STATEMENT") && (
        switchPos("/ HTML")
        || switchPos("/ BRIEF_HTML")
        || switchPos("/ ALT_HTML")
        || switchPos("/ BRIEF_ALT_HTML"))) {
      /* Special processing for the / HTML qualifiers - for each matching
         statement, a .html file is opened, the statement is output,
         and depending on statement type a proof or other information
         is output. */
      /* if (rawArgs != 5) { */  /* obsolete */
      if (rawArgs != (switchPos("/ NO_VERSIONING") ? 7 : 5)) {
                                                            /* 6-Jul-2008 nm */
        printLongLine(cat("?The HTML qualifiers may not be combined with",
            " others except / NO_VERSIONING.\n", NULL), "  ", " ");
        continue;
      }

      if (texDefsRead) {
        /* Current limitation - can only read def's from .mm file once */
        if (!htmlFlag) {
          print2("?You cannot use both LaTeX and HTML in the same session.\n");
          print2(
              "You must EXIT and restart Metamath to switch to the other.\n");
          goto htmlDone;
        } else {
          if ((switchPos("/ ALT_HTML") || switchPos("/ BRIEF_ALT_HTML"))
              == (altHtmlFlag == 0)) {
            print2(
              "?You cannot use both HTML and ALT_HTML in the same session.\n");
            print2(
              "You must EXIT and restart Metamath to switch to the other.\n");
            goto htmlDone;
          }
        }
      }

      if (switchPos("/ BRIEF_HTML") || switchPos("/ BRIEF_ALT_HTML")) {
        if (strcmp(fullArg[2], "*")) {
          print2(
              "?For BRIEF_HTML or BRIEF_ALT_HTML, the label must be \"*\"\n");
          goto htmlDone;
        }
        briefHtmlFlag = 1;
      } else {
        briefHtmlFlag = 0;
      }

      if (switchPos("/ ALT_HTML") || switchPos("/ BRIEF_ALT_HTML")) {
        altHtmlFlag = 1;
      } else {
        altHtmlFlag = 0;
      }

      q = 0;

      /* Special feature:  if the match statement starts with "*", we
         will also output mmascii.html, mmtheoremsall.html, and
         mmdefinitions.html.  So, with
                 SHOW STATEMENT * / HTML
         these will be output plus all statements; with
                 SHOW STATEMENT *! / HTML
         these will be output with no statements (since ! is illegal in a
         statement label); with
                 SHOW STATEMENT ?* / HTML
         all statements will be output, but without mmascii.html etc. */
      /* if (instr(1, fullArg[2], "*") || briefHtmlFlag) { */  /* obsolete */
      if (((char *)(fullArg[2]))[0] == '*' || briefHtmlFlag) {
                                                            /* 6-Jul-2008 nm */
        s = -2; /* -2 is for ASCII table; -1 is for theorems;
                    0 is for definitions */
      } else {
        s = 1;
      }

      for (s = s + 0; s <= statements; s++) {

        if (s > 0 && briefHtmlFlag) break; /* Only do summaries */

        /*
           s = -2:  mmascii.html
           s = -1:  mmtheoremsall.html (used to be mmtheorems.html)
           s = 0:   mmdefinitions.html
           s > 0:   normal statement
        */

        if (s > 0) {
          if (!statement[s].labelName[0]) continue; /* No label */
          /* 30-Jan-06 nm Added single-character-match wildcard argument */
          if (!matchesList(statement[s].labelName, fullArg[2], '*', '?'))
            continue;
          if (statement[s].type != (char)a_
              && statement[s].type != (char)p_) continue;
        }

        q = 1; /* Flag that at least one matching statement was found */

        if (s > 0) {
          showStatement = s;
        } else {
          /* We set it to 1 here so we will output the Metamath Proof
             Explorer and not the Hilbert Space Explorer header for
             definitions and theorems lists, when showStatement is
             compared to extHtmlStmt in printTexHeader in mmwtex.c */
          showStatement = 1;
        }


        /*** Open the html file ***/
        htmlFlag = 1;
        /* Open the html output file */
        switch (s) {
          case -2:
            let(&texFileName, "mmascii.html");
            break;
          case -1:
            let(&texFileName, "mmtheoremsall.html");
            break;
          case 0:
            let(&texFileName, "mmdefinitions.html");
            break;
          default:
            let(&texFileName, cat(statement[showStatement].labelName, ".html",
                NULL));
        }
        print2("Creating HTML file \"%s\"...\n", texFileName);
        if (switchPos("/ NO_VERSIONING") == 0) {
          texFilePtr = fSafeOpen(texFileName, "w");
        } else {
          /* 6-Jul-2008 nm Added / NO_VERSIONING */
          /* Don't create the backup versions ~1, ~2,... */
          texFilePtr = fopen(texFileName, "w");
          if (!texFilePtr) print2("?Could not open the file \"%s\".\n",
              texFileName);
        }
        if (!texFilePtr) goto htmlDone; /* Couldn't open it (err msg was
            provided) */
        texFileOpenFlag = 1;
        printTexHeader((s > 0) ? 1 : 0 /*texHeaderFlag*/);
        if (!texDefsRead) {
          /* 9/6/03 If there was an error reading the $t xx.mm statement,
             texDefsRead won't be set, and we should close out file and skip
             further processing.  Otherwise we will be attempting to process
             uninitialized htmldef arrays and such. */
          print2("?HTML generation was aborted due to the error above.\n");
          s = statements + 1; /* To force loop to exit */
          goto ABORT_S; /* Go to end of loop where file is closed out */
        }

        if (s <= 0) {
          outputToString = 1;
          if (s == -2) {
            printLongLine(cat("<CENTER><FONT COLOR=", GREEN_TITLE_COLOR,
                "><B>",
                "Symbol to ASCII Correspondence for Text-Only Browsers",
                " (in order of appearance in $c and $v statements",
                     " in the database)",
                "</B></FONT></CENTER><P>", NULL), "", "\"");
          }
          /* 13-Oct-2006 nm todo - </CENTER> still appears - where is it? */
          if (!briefHtmlFlag) print2("<CENTER>\n");
          print2("<TABLE BORDER CELLSPACING=0 BGCOLOR=%s\n",
              MINT_BACKGROUND_COLOR);
          /* For bobby.cast.org approval */
          switch (s) {
            case -2:
              print2("SUMMARY=\"Symbol to ASCII correspondences\">\n");
              break;
            case -1:
              print2("SUMMARY=\"List of theorems\">\n");
              break;
            case 0:
              print2("SUMMARY=\"List of syntax, axioms and definitions\">\n");
              break;
          }
          switch (s) {
            case -2:
              print2("<TR ALIGN=LEFT><TD><B>\n");
              break;
            case -1:
              print2(
         "<CAPTION><B>List of Theorems</B></CAPTION><TR ALIGN=LEFT><TD><B>\n");
              break;
            case 0:
              printLongLine(cat(
/*"<CAPTION><B>List of Syntax (not <FONT COLOR=\"#00CC00\">|-&nbsp;</FONT>), ",*/
                  /* 2/9/02 (in case |- suppressed) */
                  "<CAPTION><B>List of Syntax, ",
                  "Axioms (<FONT COLOR=", GREEN_TITLE_COLOR, ">ax-</FONT>) and",
                  " Definitions (<FONT COLOR=", GREEN_TITLE_COLOR,
                  ">df-</FONT>)", "</B></CAPTION><TR ALIGN=LEFT><TD><B>",
                  NULL), "", "\"");
              break;
          }
          switch (s) {
            case -2:
              print2("Symbol</B></TD><TD><B>ASCII\n");
              break;
            case -1:
              print2(
                  "Ref</B></TD><TD><B>Description\n");
              break;
            case 0:
              printLongLine(cat(
                  "Ref</B></TD><TD><B>",
                "Expression (see link for any distinct variable requirements)",
                NULL), "", "\"");
              break;
          }
          print2("</B></TD></TR>\n");
          m = 0; /* Statement number map */
          let(&str3, ""); /* For storing ASCII token list in s=-2 mode */
          let(&bgcolor, MINT_BACKGROUND_COLOR); /* 8-Aug-2008 nm Initialize */
          for (i = 1; i <= statements; i++) {

            /* 8-Aug-2008 nm Commented out: */
            /*
            if (i == extHtmlStmt && s != -2) {
              / * Print a row that identifies the start of the extended
                 database (e.g. Hilbert Space Explorer) * /
              printLongLine(cat(
                  "<TR><TD COLSPAN=2 ALIGN=CENTER><A NAME=\"startext\"></A>",
                  "The list of syntax, axioms (ax-) and definitions (df-) for",
                  " the <B><FONT COLOR=", GREEN_TITLE_COLOR, ">",
                  extHtmlTitle,
                  "</FONT></B> starts here</TD></TR>", NULL), "", "\"");
            }
            */

            /* 8-Aug-2008 nm */
            if (s != -2 && (i == extHtmlStmt || i == sandboxStmt)) {
              /* Print a row that identifies the start of the extended
                 database (e.g. Hilbert Space Explorer) or the user
                 sandboxes */
              if (i == extHtmlStmt) {
                let(&bgcolor, PURPLISH_BIBLIO_COLOR);
              } else {
                let(&bgcolor, SANDBOX_COLOR);
              }
              printLongLine(cat("<TR BGCOLOR=", bgcolor,
                  "><TD COLSPAN=2 ALIGN=CENTER><A NAME=\"startext\"></A>",
                  "The list of syntax, axioms (ax-) and definitions (df-) for",
                  " the <B><FONT COLOR=", GREEN_TITLE_COLOR, ">",
                  (i == extHtmlStmt) ?
                    extHtmlTitle :
                    /*"User Sandboxes",*/
                    /* 24-Jul-2009 nm Changed name of sandbox to "mathbox" */
                    "User Mathboxes",
                  "</FONT></B> starts here</TD></TR>", NULL), "", "\"");
            }

            if (statement[i].type == (char)p_ ||
                statement[i].type == (char)a_ ) m++;
            if ((s == -1 && statement[i].type != (char)p_)
                || (s == 0 && statement[i].type != (char)a_)
                || (s == -2 && statement[i].type != (char)c_
                    && statement[i].type != (char)v_)
                ) continue;
            switch (s) {
              case -2:
                /* Print symbol to ASCII table entry */
                /* It's a $c or $v statement, so each token generates a
                   table row */
                for (j = 0; j < statement[i].mathStringLen; j++) {
                  let(&str1, mathToken[(statement[i].mathString)[j]].tokenName);
                  /* Output each token only once in case of multiple decl. */
                  if (!instr(1, str3, cat(" ", str1, " ", NULL))) {
                    let(&str3, cat(str3, " ", str1, " ", NULL));
                    let(&str2, "");
                    str2 = tokenToTex(mathToken[(statement[i].mathString)[j]
                        ].tokenName, i/*stmt# for error msgs*/);
                    /* 2/9/02  Skip any tokens (such as |-) that may be suppressed */
                    if (!str2[0]) continue;
                    /* Convert special characters to HTML entities */
                    for (k = 0; k < (signed)(strlen(str1)); k++) {
                      if (str1[k] == '&') {
                        let(&str1, cat(left(str1, k), "&amp;",
                            right(str1, k + 2), NULL));
                        k = k + 4;
                      }
                      if (str1[k] == '<') {
                        let(&str1, cat(left(str1, k), "&lt;",
                            right(str1, k + 2), NULL));
                        k = k + 3;
                      }
                      if (str1[k] == '>') {
                        let(&str1, cat(left(str1, k), "&gt;",
                            right(str1, k + 2), NULL));
                        k = k + 3;
                      }
                    } /* next k */
                    printLongLine(cat("<TR ALIGN=LEFT><TD>",
                        str2,
                        "</TD><TD><TT>",
                        str1,
                        "</TT></TD></TR>", NULL), "", "\"");
                  }
                } /* next j */
                /* Close out the string now to prevent memory overflow */
                fprintf(texFilePtr, "%s", printString);
                let(&printString, "");
                break;
              case -1: /* Falls through to next case */
              case 0:
                /* Count the number of essential hypotheses k */
                /* Not needed anymore??? since getTexOrHtmlHypAndAssertion() */
                /*
                k = 0;
                j = nmbrLen(statement[i].reqHypList);
                for (n = 0; n < j; n++) {
                  if (statement[statement[i].reqHypList[n]].type
                      == (char)e_) {
                    k++;
                  }
                }
                */
                let(&str1, "");
                if (s == 0 || briefHtmlFlag) {
                  let(&str1, "");
                  /* 18-Sep-03 Get HTML hypotheses => assertion */
                  str1 = getTexOrHtmlHypAndAssertion(i);
                                                /* In mmwtex.c */
                  let(&str1, cat(str1, "</TD></TR>", NULL));
                }

                /* 13-Oct-2006 nm Made some changes to BRIEF_HTML/_ALT_HTML
                   to use its mmtheoremsall.html output for the Palm PDA */
                if (briefHtmlFlag) {
                  /* Get page number in mmtheorems*.html of WRITE THEOREMS */
                  k = ((statement[i].pinkNumber - 1) /
                      THEOREMS_PER_PAGE) + 1; /* Page # */
                  let(&str2, cat("<TR ALIGN=LEFT><TD ALIGN=LEFT>",
                      /*"<FONT COLOR=\"#FA8072\">",*/
                      "<FONT COLOR=ORANGE>",
                      str(statement[i].pinkNumber), "</FONT> ",
                      "<FONT COLOR=GREEN><A HREF=\"",
                      "mmtheorems", (k == 1) ? "" : str(k), ".html#",
                      statement[i].labelName,
                      "\">", statement[i].labelName,
                      "</A></FONT>", NULL));
                  let(&str1, cat(str2, " ", str1, NULL));
                } else {
                  /* Get little pink (or rainbow-colored) number */
                  let(&str4, "");
                  str4 = pinkHTML(i);
                  let(&str2, cat("<TR BGCOLOR=", bgcolor, /* 8-Aug-2008 nm */
                      " ALIGN=LEFT><TD><A HREF=\"",
                      statement[i].labelName,
                      ".html\">", statement[i].labelName,
                      "</A>", str4, NULL));
                  let(&str1, cat(str2, "</TD><TD>", str1, NULL));
                }
                /* End of 13-Oct-2006 changed section */

                print2("\n");  /* New line for HTML source readability */
                printLongLine(str1, "", "\"");

                if (s == 0 || briefHtmlFlag) {

                              /* Set s == 0 here for Web site version,
                                 s == s for symbol version of theorem list */
                  /* The below has been replaced by
                     getTexOrHtmlHypAndAssertion(i) above. */
                  /*printTexLongMath(statement[i].mathString, "", "", 0, 0);*/
                  /*outputToString = 1;*/ /* Is reset by printTexLongMath */
                } else {
                  /* Theorems are listed w/ description; otherwise file is too
                     big for convenience */
                  let(&str1, "");
                  str1 = getDescription(i);
                  if (strlen(str1) > 29)
                    let(&str1, cat(left(str1, 26), "...", NULL));
                  let(&str1, cat(str1, "</TD></TR>", NULL));
                  printLongLine(str1, "", "\"");
                }
                /* Close out the string now to prevent overflow */
                fprintf(texFilePtr, "%s", printString);
                let(&printString, "");
                break;
            } /* end switch */
          } /* next i (statement number) */
          /* print2("</TABLE></CENTER>\n"); */ /* 8/8/03 Removed - already
              done somewhere else, causing validator.w3.org to fail */
          outputToString = 0;  /* closing will write out the string */
          let(&bgcolor, ""); /* Deallocate (to improve fragmentation) */

        } else { /* s > 0 */

          /*** Output the html statement body ***/
          typeStatement(showStatement,
              0 /*briefFlag*/,
              0 /*commentOnlyFlag*/,
              1 /*texFlag*/,   /* means latex or html */
              1 /*htmlFlag*/);

        } /* if s <= 0 */

       ABORT_S:
        /*** Close the html file ***/
        printTexTrailer(1 /*texHeaderFlag*/);
        fclose(texFilePtr);
        texFileOpenFlag = 0;
        let(&texFileName,"");

      } /* next s */

      if (!q) {
        /* No matching statement was found */
        printLongLine(cat("?There is no statement whose label matches \"",
            fullArg[2], "\".  ",
            "Use SHOW LABELS for a list of valid labels.", NULL), "", " ");
        continue;
      }

      /* Complete the command processing to bypass normal SHOW STATEMENT
         (non-html) below. */
     htmlDone:
      continue;
    } /* if (cmdMatches("SHOW STATEMENT") && switchPos("/ HTML")...) */


    /******* Section for / MNEMONICS added 12-May-2009 by Stefan Allan *****/
    /* Write mnemosyne.txt */
    if (cmdMatches("SHOW STATEMENT") && switchPos("/ MNEMONICS")) {

      if (!texDefsRead) {
        htmlFlag = 1;     /* Use HTML, not TeX section */
        altHtmlFlag = 1;  /* Use Unicode, not GIF */
        print2("Reading definitions from $t statement of %s...\n", input_fn);
        if (!readTexDefs()) {
          print2(
          "?There was an error in the $t comment's LaTeX/HTML definitions.\n");
          print2("?HTML generation was aborted due to the error above.\n");
          continue; /* An error occurred */
        }
      } else {
        /* Current limitation - can only read def's from .mm file once */
        if (!htmlFlag) {
          print2("?You cannot use both LaTeX and HTML in the same session.\n");
          print2(
              "You must EXIT and restart Metamath to switch to the other.\n");
          continue;
        } else {
          if (!altHtmlFlag) {
            print2(
              "?You cannot use both HTML and ALT_HTML in the same session.\n");
            print2(
              "You must EXIT and restart Metamath to switch to the other.\n");
            continue;
          }
        }
      }

      let(&texFileName,"mnemosyne.txt");
      texFilePtr = fSafeOpen(texFileName, "w");
      if (!texFilePtr) {
        /* Couldn't open file; error message was provided by fSafeOpen */
        continue;
      }
      print2("Creating Mnemosyne file \"%s\"...\n", texFileName);

      for (s = 1; s <= statements; s++) {
        showStatement = s;
/*
        if (strcmp("|-", mathToken[
         (statement[showStatement].mathString)[0]].tokenName)) {
           subType = SYNTAX;
        }
*/
        if (!statement[s].labelName[0]) continue; /* No label */
        /* 30-Jan-06 nm Added single-character-match wildcard argument */
        if (!matchesList(statement[s].labelName, fullArg[2], '*', '?'))
          continue;

        if (statement[s].type != (char)a_
            && statement[s].type != (char)p_)
          continue;

        let(&str1, cat("<CENTER><B><FONT SIZE=\"+1\">",
            " <FONT COLOR=", GREEN_TITLE_COLOR,
            " SIZE = \"+3\">", statement[showStatement].labelName,
            "</FONT></FONT></B>", "</CENTER>", NULL));
        fprintf(texFilePtr, "%s", str1);

        let(&str1, cat("<TABLE>",NULL));
        fprintf(texFilePtr, "%s", str1);

        j = nmbrLen(statement[showStatement].reqHypList);
        for (i = 0; i < j; i++) {
          k = statement[showStatement].reqHypList[i];
          if (statement[k].type != (char)e_
              && !(subType == SYNTAX
              && statement[k].type == (char)f_))
            continue;

          let(&str1, cat("<TR ALIGN=LEFT><TD><FONT SIZE=\"+2\">",
              statement[k].labelName, "</FONT></TD><TD><FONT SIZE=\"+2\">",
              NULL));
          fprintf(texFilePtr, "%s", str1);

          /* Print hypothesis */
          let(&str1, ""); /* Free any previous allocation to str1 */
          /* getTexLongMath does not return a temporary allocation; must
             assign str1 directly, not with let().  It will be deallocated
             with the next let(&str1,...). */
          str1 = getTexLongMath(statement[k].mathString,
              k/*stmt# for err msgs*/);
          fprintf(texFilePtr, "%s</FONT></TD>", str1);
        }


        let(&str1, "</TABLE>");
        fprintf(texFilePtr, "%s", str1);

        let(&str1, "<BR><FONT SIZE=\"+2\">What is the conclusion?</FONT>");
        fprintf(texFilePtr, "%s\n", str1);

        let(&str1, "<FONT SIZE=\"+3\">");
        fprintf(texFilePtr, "%s", str1);

        let(&str1, ""); /* Free any previous allocation to str1 */
        /* getTexLongMath does not return a temporary allocation */
        str1 = getTexLongMath(statement[s].mathString, s);
        fprintf(texFilePtr, "%s", str1);

        let(&str1, "</FONT>");
        fprintf(texFilePtr, "%s\n",str1);
      } /*  for(s=1;s<statements;++s) */

      fclose(texFilePtr);
      texFileOpenFlag = 0;
      let(&texFileName,"");
      let(&str1,"");
      let(&str2,"");

      continue;
    } /* if (cmdMatches("SHOW STATEMENT") && switchPos("/ MNEMONICS")) */
    /** End of section for / MNEMONICS added 12-May-2009 by Stefan Allan *****/

    /* If we get here, the user did not specify one of the qualifiers /HTML,
       /BRIEF_HTML, /ALT_HTML, or /BRIEF_ALT_HTML */
    if (cmdMatches("SHOW STATEMENT") && !switchPos("/ HTML")) {

      texFlag = 0;
      /* 14-Sep-2010 nm Added OLD_TEX */
      if (switchPos("/ TEX") || switchPos("/ OLD_TEX")
          || switchPos("/ HTML"))
        texFlag = 1;

      briefFlag = 1;
      oldTexFlag = 0;
      if (switchPos("/ TEX")) briefFlag = 0;
      /* 14-Sep-2010 nm Added OLD_TEX */
      if (switchPos("/ OLD_TEX")) briefFlag = 0;
      if (switchPos("/ OLD_TEX")) oldTexFlag = 1;
      if (switchPos("/ FULL")) briefFlag = 0;

      commentOnlyFlag = 0;
      if (switchPos("/ COMMENT")) {
        commentOnlyFlag = 1;
        briefFlag = 1;
      }


      if (switchPos("/ FULL")) {
        briefFlag = 0;
        commentOnlyFlag = 0;
      }

      if (texFlag) {
        if (!texFileOpenFlag) {
          print2(
      "?You have not opened a %s file.  Use the OPEN TEX command first.\n",
              htmlFlag ? "HTML" : "LaTeX");
          continue;
        }
      }

      if (texFlag && (commentOnlyFlag || briefFlag)) {
        print2("?TEX qualifier should be used alone\n");
        continue;
      }

      q = 0;

      for (s = 1; s <= statements; s++) {
        if (!statement[s].labelName[0]) continue; /* No label */
        /* We are not in MM-PA mode, or the statement isn't "=" */
        /* 30-Jan-06 nm Added single-character-match wildcard argument */
        if (!matchesList(statement[s].labelName, fullArg[2], '*', '?'))
          continue;

        if (briefFlag || commentOnlyFlag || texFlag) {
          /* For brief or comment qualifier, if label has wildcards,
             show only $p and $a's */
          /* 30-Jan-06 nm Added single-character-match wildcard argument */
          if (statement[s].type != (char)p_
              && statement[s].type != (char)a_ && (instr(1, fullArg[2], "*")
                  || instr(1, fullArg[2], "?")))
            continue;
        }

        if (q && !texFlag) {
          if (!print2("%s\n", string(79, '-'))) /* Put line between
                                                   statements */
            break; /* Break for speedup if user quit */
        }
        if (texFlag) print2("Outputting statement \"%s\"...\n",
            statement[s].labelName);

        q = 1; /* Flag that at least one matching statement was found */

        showStatement = s;


        typeStatement(showStatement,
            briefFlag,
            commentOnlyFlag,
            texFlag,
            htmlFlag);
      } /* Next s */

      if (!q) {
        /* No matching statement was found */
        printLongLine(cat("?There is no statement whose label matches \"",
            fullArg[2], "\".  ",
            "Use SHOW LABELS for a list of valid labels.", NULL), "", " ");
        continue;
      }

      if (texFlag && !htmlFlag) {
        print2("The LaTeX source was written to \"%s\".\n", texFileName);
        /* 14-Sep-2010 nm Added OLD_TEX */
        oldTexFlag = 0;
      }
      continue;
    } /* (cmdMatches("SHOW STATEMENT") && !switchPos("/ HTML")) */

    if (cmdMatches("SHOW SETTINGS")) {
      print2("Metamath settings on %s at %s:\n",date(),time_());
      if (commandEcho) {
        print2("(SET ECHO...) Command ECHO is ON.\n");
      } else {
        print2("(SET ECHO...) Command ECHO is OFF.\n");
      }
      if (scrollMode) {
        print2("(SET SCROLL...) SCROLLing mode is PROMPTED.\n");
      } else {
        print2("(SET SCROLL...) SCROLLing mode is CONTINUOUS.\n");
      }
      print2("(SET WIDTH...) Screen display WIDTH is %ld.\n", screenWidth);
      print2("(SET HEIGHT...) Screen display HEIGHT is %ld.\n",
          screenHeight + 1);
      if (strlen(input_fn)) {
        print2("(READ...) %ld statements have been read from '%s'.\n",
          statements, input_fn);
      } else {
        print2("(READ...) No source file has been read in yet.\n");
      }
      if (PFASmode) {
        print2("(PROVE...) The statement you are proving is '%s'.\n",
            statement[proveStatement].labelName);
      }
      print2("(SET UNDO...) The maximum number of UNDOs is %ld.\n",
          processUndoStack(NULL, PUS_GET_SIZE, "", 0));
      print2(
    "(SET UNIFICATION_TIMEOUT...) The unification timeout parameter is %ld.\n",
          userMaxUnifTrials);
      print2(
    "(SET SEARCH_LIMIT...) The SEARCH_LIMIT for the IMPROVE command is %ld.\n",
          userMaxProveFloat);
      if (minSubstLen) {
        print2(
     "(SET EMPTY_SUBSTITUTION...) EMPTY_SUBSTITUTION is not allowed (OFF).\n");
      } else {
        print2(
          "(SET EMPTY_SUBSTITUTION...) EMPTY_SUBSTITUTION is allowed (ON).\n");
      }
      if (hentyFilter) { /* 18-Nov-05 nm Added to the SHOW listing */
        print2(
              "(SET JEREMY_HENTY_FILTER...) The Henty filter is turned ON.\n");
      } else {
        print2(
             "(SET JEREMY_HENTY_FILTER...) The Henty filter is turned OFF.\n");
      }
      if (showStatement) {
        print2("(SHOW...) The default statement for SHOW commands is '%s'.\n",
            statement[showStatement].labelName);
      }
      if (logFileOpenFlag) {
        print2("(OPEN LOG...) The log file '%s' is open.\n", logFileName);
      } else {
        print2("(OPEN LOG...) No log file is currently open.\n");
      }
      if (texFileOpenFlag) {
        print2("The %s file '%s' is open.\n", htmlFlag ? "HTML" : "LaTeX",
            texFileName);
      }
      /* 21-Jun-2014 */
      print2("The program is compiled for a %ld-bit CPU.\n",
          (long)(8 * sizeof(long)));
      continue;
    }

    if (cmdMatches("SHOW MEMORY")) {
      /*print2("%ld bytes of data memory have been used.\n",db+db3);*/
      j = 32000000; /* The largest we'ed ever look for */
#ifdef THINK_C
      i = FreeMem();
#else
      i = getFreeSpace(j);
#endif
      if (i > j-3) {
        print2("At least %ld bytes of memory are free.\n",j);
      } else {
        print2("%ld bytes of memory are free.\n",i);
      }
      continue;
    }

    /* 21-Jun-2014 */
    if (cmdMatches("SHOW ELAPSED_TIME")) {
#ifdef CLOCKS_PER_SEC
      timeNow = clock();
      print2(
      "Time since last SHOW ELAPSED_TIME command = %4.2f s; total = %4.2f s\n",
          (double)((1.0 * (timeNow - timePrevious))/CLOCKS_PER_SEC),
          (double)((1.0 * timeNow)/CLOCKS_PER_SEC));
      timePrevious = timeNow;
#else
      print2("The clock() function is not implemented on this computer.\n");
#endif
      continue;
    } /* if (cmdMatches("SHOW ELAPSED_TIME")) */


    if (cmdMatches("SHOW TRACE_BACK")) {


      /* Pre-21-May-2008
        for (i = 1; i <= statements; i++) {
          if (!strcmp(fullArg[2],statement[i].labelName)) break;
        }
        if (i > statements) {
          printLongLine(cat("?There is no statement with label \"",
              fullArg[2], "\".  ",
              "Use SHOW LABELS for a list of valid labels.", NULL), "", " ");
          showStatement = 0;
          continue;
        }
       */

      essentialFlag = 0;
      axiomFlag = 0;
      endIndent = 0;
      i = switchPos("/ ESSENTIAL");
      if (i) essentialFlag = 1; /* Limit trace to essential steps only */
      i = switchPos("/ ALL");
      if (i) essentialFlag = 0;
      i = switchPos("/ AXIOMS");
      if (i) axiomFlag = 1; /* Limit trace printout to axioms */
      i = switchPos("/ DEPTH"); /* Limit depth of printout */
      if (i) endIndent = (long)val(fullArg[i + 1]);

       /* 19-May-2013 nm */
      i = switchPos("/ COUNT_STEPS");
      countStepsFlag = (i != 0 ? 1 : 0);
      i = switchPos("/ TREE");
      treeFlag = (i != 0 ? 1 : 0);
      i = switchPos("/ MATCH");
      matchFlag = (i != 0 ? 1 : 0);
      if (matchFlag) {
        let(&matchList, fullArg[i + 1]);
      } else {
        let(&matchList, "");
      }
      i = switchPos("/ TO");
      if (i != 0) {
        let(&traceToList, fullArg[i + 1]);
      } else {
        let(&traceToList, "");
      }
      if (treeFlag) {
        if (axiomFlag) {
          print2(
              "(Note:  The AXIOMS switch is ignored in TREE mode.)\n");
        }
        if (countStepsFlag) {
          print2(
              "(Note:  The COUNT_STEPS switch is ignored in TREE mode.)\n");
        }
        if (matchFlag) {
          print2(
  "(Note: The MATCH list is ignored in TREE mode.)\n");
        }
      } else {
        if (endIndent != 0) {
          print2(
  "(Note:  The DEPTH is ignored if the TREE switch is not used.)\n");
        }
        if (countStepsFlag) {
          if (matchFlag) {
            print2(
  "(Note: The MATCH list is ignored in COUNT_STEPS mode.)\n");
          }
        }
      }

      /* 21-May-2008 nm Added wildcard handling */
      showStatement = 0;
      for (i = 1; i <= statements; i++) {
        if (statement[i].type != (char)p_)
          continue; /* Not a $p statement; skip it */
        /* Wildcard matching */
        if (!matchesList(statement[i].labelName, fullArg[2], '*', '?'))
          continue;

        showStatement = i;
        /*** start of 19-May-2013 deletion
        j = switchPos("/ TREE");
        if (j) {
          if (axiomFlag) {
            print2(
                "(Note:  The AXIOMS switch is ignored in TREE mode.)\n");
          }
          if (switchPos("/ COUNT_STEPS")) {
            print2(
                "(Note:  The COUNT_STEPS switch is ignored in TREE mode.)\n");
          }
          traceProofTree(showStatement, essentialFlag, endIndent);
        } else {
          if (endIndent != 0) {
           print2(
"(Note:  The DEPTH is ignored if the TREE switch is not used.)\n");
          }

          j = switchPos("/ COUNT_STEPS");
          if (j) {
            countSteps(showStatement, essentialFlag);
          } else {
            traceProof(showStatement, essentialFlag, axiomFlag);
          }
        }
        *** end of 19-May-2013 deletion */


        /* 19-May-2013 nm - move /TREE and /COUNT_STEPS to outside loop,
           assigning new variables, for cleaner code. */
        if (treeFlag) {
          traceProofTree(showStatement, essentialFlag, endIndent);
        } else {
          if (countStepsFlag) {
            countSteps(showStatement, essentialFlag);
          } else {
            traceProof(showStatement,
                essentialFlag,
                axiomFlag,
                matchList, /* 19-May-2013 nm */
                traceToList, /* 18-Jul-2015 nm */
                0 /* testOnlyFlag */ /* 20-May-2013 nm */);
          }
        }

      /* 21-May-2008 nm Added wildcard handling */
      } /* next i */
      if (showStatement == 0) {
        printLongLine(cat("?There are no $p labels matching \"",
            fullArg[2], "\".  ",
            "See HELP SHOW TRACE_BACK for matching rules.", NULL), "", " ");
      }

      let(&matchList, ""); /* Deallocate memory */
      let(&traceToList, ""); /* Deallocate memory */
      continue;
    } /* if (cmdMatches("SHOW TRACE_BACK")) */


    if (cmdMatches("SHOW USAGE")) {

      /* 7-Jan-2008 nm Added / ALL qualifier */
      if (switchPos("/ ALL")) {
        m = 1;  /* Always include $e, $f statements */
      } else {
        m = 0;  /* If wildcards are used, show $a, $p only */
      }

      showStatement = 0;
      for (i = 1; i <= statements; i++) { /* 7-Jan-2008 */

        /* 7-Jan-2008 nm Added wildcard handling */
        if (!statement[i].labelName[0]) continue; /* No label */
        if (!m && statement[i].type != (char)p_ &&
            statement[i].type != (char)a_
            /* A wildcard-free user-specified statement is always matched even
               if it's a $e, i.e. it overrides omission of / ALL */
            && (instr(1, fullArg[2], "*")
              || instr(1, fullArg[2], "?")))
          continue; /* No /ALL switch and wildcard and not $p, $a */
        /* Wildcard matching */
        if (!matchesList(statement[i].labelName, fullArg[2], '*', '?'))
          continue;

        showStatement = i;
        recursiveFlag = 0;
        j = switchPos("/ RECURSIVE");
        if (j) recursiveFlag = 1; /* Recursive (indirect) usage */
        j = switchPos("/ DIRECT");
        if (j) recursiveFlag = 0; /* Direct references only */

        let(&str1, "");
        str1 = traceUsage(showStatement,
            recursiveFlag,
            0 /* cutoffStmt */);



        /************* 18-Jul-2015 nm Start of deleted code ************/
        /*
        /@ Count the number of statements = # of spaces @/
        k = (long)strlen(str1) - (long)strlen(edit(str1, 2));

        if (!k) {
          printLongLine(cat("Statement \"",
              statement[showStatement].labelName,
              "\" is not referenced in the proof of any statement.", NULL),
              "", " ");
        } else {
          if (recursiveFlag) {
            let(&str2, "\" directly or indirectly affects");
          } else {
            let(&str2, "\" is directly referenced in");
          }
          if (k == 1) {
            printLongLine(cat("Statement \"",
                statement[showStatement].labelName,
                str2, " the proof of ",
                str(k), " statement:", NULL), "", " ");
          } else {
            printLongLine(cat("Statement \"",
                statement[showStatement].labelName,
                str2, " the proofs of ",
                str(k), " statements:", NULL), "", " ");
          }
        }

        if (k) {
          let(&str1, cat(" ", str1, NULL));
        } else {
          let(&str1, "  (None)");
        }

        /@ Print the output @/
        printLongLine(str1, "  ", " ");
        */
        /********* 18-Jul-2015 nm End of deleted code ****************/


        /************* 18-Jul-2015 nm Start of new code ************/
        /* 18-Jul-2015 nm */
        /* str1[0] will be 'Y' or 'N' depending on whether there are any
           statements.  str1[i] will be 'Y' or 'N' depending on whether
           statement[i] uses showStatement. */
        /* Count the number of statements k = # of 'Y' */
        k = 0;
        if (str1[0] == 'Y') {
          /* There is at least one statement using showStatement */
          for (j = showStatement + 1; j <= statements; j++) {
            if (str1[j] == 'Y') {
              k++;
            } else {
              if (str1[j] != 'N') bug(1124); /* Must be 'Y' or 'N' */
            }
          }
        } else {
          if (str1[0] != 'N') bug(1125); /* Must be 'Y' or 'N' */
        }

        if (k == 0) {
          printLongLine(cat("Statement \"",
              statement[showStatement].labelName,
              "\" is not referenced in the proof of any statement.", NULL),
              "", " ");
        } else {
          if (recursiveFlag) {
            let(&str2, "\" directly or indirectly affects");
          } else {
            let(&str2, "\" is directly referenced in");
          }
          if (k == 1) {
            printLongLine(cat("Statement \"",
                statement[showStatement].labelName,
                str2, " the proof of ",
                str(k), " statement:", NULL), "", " ");
          } else {
            printLongLine(cat("Statement \"",
                statement[showStatement].labelName,
                str2, " the proofs of ",
                str(k), " statements:", NULL), "", " ");
          }
        }

        if (k != 0) {
          let(&str3, " "); /* Line buffer */
          for (j = showStatement + 1; j <= statements; j++) {
            if (str1[j] == 'Y') {
              /* Since the output list could be huge, don't build giant
                 string (very slow) but output it line by line */
              if ((long)strlen(str3) + 1 +
                  (long)strlen(statement[j].labelName) > screenWidth) {
                /* Output and reset the line buffer */
                print2("%s\n", str3);
                let(&str3, " ");
              }
              let(&str3, cat(str3, " ", statement[j].labelName, NULL));
            }
          }
          if (strlen(str3) > 1) print2("%s\n", str3);
          let(&str3, "");
        } else {
          print2("  (None)\n");
        } /* if (k != 0) */
        /********* 18-Jul-2015 nm End of new code ****************/


      } /* next i (statement matching wildcard list) */

      if (showStatement == 0) {
        printLongLine(cat("?There are no labels matching \"",
            fullArg[2], "\".  ",
            "See HELP SHOW USAGE for matching rules.", NULL), "", " ");
      }
      continue;
    } /* if cmdMatches("SHOW USAGE") */


    if (cmdMatches("SHOW PROOF")
        || cmdMatches("SHOW NEW_PROOF")
        || cmdMatches("SAVE PROOF")
        || cmdMatches("SAVE NEW_PROOF")
        || cmdMatches("MIDI")) {
      if (switchPos("/ HTML")) {
        print2("?HTML qualifier is obsolete - use SHOW STATEMENT * / HTML\n");
        continue;
      }
      if (cmdMatches("SHOW PROOF") || cmdMatches("SAVE PROOF")) {
        pipFlag = 0;
      } else {
        pipFlag = 1; /* Proof-in-progress (NEW_PROOF) flag */
      }
      if (cmdMatches("SHOW")) {
        saveFlag = 0;
      } else {
        saveFlag = 1; /* The command is SAVE PROOF */
      }

      /* 27-Dec-2013 nm */
      i = switchPos("/ FAST_COMPRESSION");
      if (i) {
        if (!switchPos("/ COMPRESSED")) {
          print2("?/ FAST_COMPRESSION must be accompanied by / COMPRESSED\n");
          continue;
        }
      }


      /* Establish defaults for omitted qualifiers */
      startStep = 0;
      endStep = 0;
      /* startIndent = 0; */ /* Not used */
      endIndent = 0;
      /*essentialFlag = 0;*/
      essentialFlag = 1; /* 10/9/99 - friendlier default */
      renumberFlag = 0;
      unknownFlag = 0;
      notUnifiedFlag = 0;
      reverseFlag = 0;
      detailStep = 0;
      noIndentFlag = 0;
      splitColumn = DEFAULT_COLUMN;
      skipRepeatedSteps = 0; /* 28-Jun-2013 nm */
      texFlag = 0;

      i = switchPos("/ FROM_STEP");
      if (i) startStep = (long)val(fullArg[i + 1]);
      i = switchPos("/ TO_STEP");
      if (i) endStep = (long)val(fullArg[i + 1]);
      i = switchPos("/ DEPTH");
      if (i) endIndent = (long)val(fullArg[i + 1]);
      /* 10/9/99 - ESSENTIAL is retained for downwards compatibility, but is
         now the default, so we ignore it. */
      /*
      i = switchPos("/ ESSENTIAL");
      if (i) essentialFlag = 1;
      */
      i = switchPos("/ ALL");
      if (i) essentialFlag = 0;
      if (i && switchPos("/ ESSENTIAL")) {
        print2("?You may not specify both / ESSENTIAL and / ALL.\n");
        continue;
      }
      i = switchPos("/ RENUMBER");
      if (i) renumberFlag = 1;
      i = switchPos("/ UNKNOWN");
      if (i) unknownFlag = 1;
      i = switchPos("/ NOT_UNIFIED"); /* pip mode only */
      if (i) notUnifiedFlag = 1;
      i = switchPos("/ REVERSE");
      if (i) reverseFlag = 1;
      i = switchPos("/ LEMMON");
      if (i) noIndentFlag = 1;
      i = switchPos("/ START_COLUMN");
      if (i) splitColumn = (long)val(fullArg[i + 1]);
      i = switchPos("/ NO_REPEATED_STEPS"); /* 28-Jun-2013 nm */
      if (i) skipRepeatedSteps = 1;         /* 28-Jun-2013 nm */
      i = switchPos("/ TEX") || switchPos("/ HTML")
          /* 14-Sep-2010 nm Added OLDE_TEX */
          || switchPos("/ OLD_TEX");
      if (i) texFlag = 1;

      /* 14-Sep-2010 nm Added OLD_TEX */
      oldTexFlag = 0;
      if (switchPos("/ OLD_TEX")) oldTexFlag = 1;

      if (cmdMatches("MIDI")) { /* 8/28/00 */
        midiFlag = 1;
        pipFlag = 0;
        saveFlag = 0;
        let(&labelMatch, fullArg[1]);
        i = switchPos("/ PARAMETER"); /* MIDI only */
        if (i) {
          let(&midiParam, fullArg[i + 1]);
        } else {
          let(&midiParam, "");
        }
      } else {
        midiFlag = 0;
        if (!pipFlag) let(&labelMatch, fullArg[2]);
      }


      if (texFlag) {
        if (!texFileOpenFlag) {
          print2(

     "?You have not opened a %s file.  Use the OPEN %s command first.\n",
              htmlFlag ? "HTML" : "LaTeX",
              htmlFlag ? "HTML" : "TEX");
          continue;
        }
        /**** this is now done after outputting
        print2("The %s source was written to \"%s\".\n",
            htmlFlag ? "HTML" : "LaTeX", texFileName);
        */
      }

      i = switchPos("/ DETAILED_STEP"); /* non-pip mode only */
      if (i) {
        detailStep = (long)val(fullArg[i + 1]);
        if (!detailStep) detailStep = -1; /* To use as flag; error message
                                             will occur in showDetailStep() */
      }

/*??? Need better warnings for switch combinations that don't make sense */

      q = 0;
      for (s = 1; s <= statements; s++) {
        /* If pipFlag (NEW_PROOF), we will iterate exactly once.  This
           loop of course will be entered because there is a least one
           statement, and at the end of the s loop we break out of it. */
        /* If !pipFlag, get the next statement: */
        if (!pipFlag) {
          if (statement[s].type != (char)p_) continue; /* Not $p */
          /* 30-Jan-06 nm Added single-character-match wildcard argument */
          if (!matchesList(statement[s].labelName, labelMatch, '*', '?'))
            continue;
          showStatement = s;
        }

        q = 1; /* Flag that at least one matching statement was found */

        if (detailStep) {
          /* Show the details of just one step */
          showDetailStep(showStatement, detailStep);
          continue;
        }

        if (switchPos("/ STATEMENT_SUMMARY")) { /* non-pip mode only */
          /* Just summarize the statements used in the proof */
          proofStmtSumm(showStatement, essentialFlag, texFlag);
          continue;
        }

        /* 21-Jun-2014 */
        if (switchPos("/ SIZE")) { /* non-pip mode only */
          /* Just print the size of the stored proof and continue */
          let(&str1, space(statement[showStatement].proofSectionLen));
          memcpy(str1, statement[showStatement].proofSectionPtr,
              (size_t)(statement[showStatement].proofSectionLen));
          n = instr(1, str1, "$.");
          if (n == 0) {
            /* The original source truncates the proof before $. */
            n = statement[showStatement].proofSectionLen;
          } else {
            /* If a proof is saved, it includes the $. (Should we
               revisit or document better how/why this is done?) */
            n = n - 1;
          }
          print2("The proof source for \"%s\" has %ld characters.\n",
              statement[showStatement].labelName, n);
          continue;
        }

        if (switchPos("/ PACKED") || switchPos("/ NORMAL") ||
            switchPos("/ COMPRESSED") || saveFlag) {
          /*??? Add error msg if other switches were specified. (Ignore them.)*/

          if (!pipFlag) {
            i = showStatement;
          } else {
            i = proveStatement;
          }

          /* Get the amount to indent the proof by */
          indentation = 2 + getSourceIndentation(i);

          if (!pipFlag) {
            parseProof(showStatement);  /* Prints message if severe error */
            if (wrkProof.errorSeverity > 1) {  /* 21-Aug-04 nm */
                              /* Prevent bugtrap in nmbrSquishProof -> */
                              /* nmbrGetSubProofLen if proof corrupted */
              print2(
          "?The proof has a severe error and cannot be displayed or saved.\n");
              continue;
            }
            /* verifyProof(showStatement); */ /* Not necessary */
            nmbrLet(&nmbrSaveProof, nmbrUnsquishProof(wrkProof.proofString));
          } else {
            nmbrLet(&nmbrSaveProof, proofInProgress.proof);
          }
          if (switchPos("/ PACKED")  || switchPos("/ COMPRESSED")) {
            nmbrLet(&nmbrSaveProof, nmbrSquishProof(nmbrSaveProof));
          }

          if (switchPos("/ COMPRESSED")) {
            let(&str1, compressProof(nmbrSaveProof,
                i, /* showStatement or proveStatement based on pipFlag */
                (switchPos("/ FAST_COMPRESSION")) ? 1 : 0  /* 27-Dec-2013 nm */
                ));
          } else {
            let(&str1, nmbrCvtRToVString(nmbrSaveProof));
          }


          if (saveFlag) {
            /* ??? This is a problem when mixing html and save proof */
            if (printString[0]) bug(1114);
            let(&printString, "");
            outputToString = 1; /* Flag for print2 to add to printString */
          } else {
            if (!print2("Proof of \"%s\":\n", statement[i].labelName))
              break; /* Break for speedup if user quit */
            print2(
"---------Clip out the proof below this line to put it in the source file:\n");
            /* 19-Apr-2015 so */
            /* 24-Apr-2015 nm Reverted */
            /*print2("\n");*/ /* Add a blank line to make clipping easier */
          }
          if (switchPos("/ COMPRESSED")) {
            printLongLine(cat(space(indentation), str1, " $.", NULL),
              space(indentation), "& "); /* "&" is special flag to break
                  compressed part of proof anywhere */
          } else {
            printLongLine(cat(space(indentation), str1, " $.", NULL),
              space(indentation), " ");
          }

          /* 24-Apr-2015 nm */
          l = (long)(strlen(str1)); /* Save length for printout below */

          if /*(pipFlag)*/ (1) { /* Add the date proof was created */
            /* 6/13/98 If the proof already has a date stamp, don't add
               a new one.  Note: for the last statement, i + 1
               will refer to a final "dummy" statement containing
               text (comments) through the end of file, stored in its
               labelSectionXxx structure members. */
            let(&str2, space(statement[i + 1].labelSectionLen));
            memcpy(str2, statement[i + 1].labelSectionPtr,
                (size_t)(statement[i + 1].labelSectionLen));
            /* 12-Jun-2011 nm Removed pipFlag condition so that a date
               stamp will always be created if it doesn't exist */
            if ( /* pipFlag && */ !instr(1, str2, "$([")
                /* 7-Sep-04 Allow both "$([<date>])$" and "$( [<date>] )$" */
                /* 19-Apr-2015 so */
                /* SOREAR Only generate date if the proof looks complete.
                   This is not intended as a grading mechanism, just trying
                   to avoid premature output */
                && !nmbrElementIn(1, nmbrSaveProof, -(long)'?')
                && !instr(1, str2, "$( [")) {
            /* 6/13/98 end */
              /* No date stamp existed before.  Create one for today's
                 date.  Note that the characters after "$." at the end of
                 the proof normally go in the labelSection of the next
                 statement, but a special mode in outputStatement() (in
                 mmpars.c) will output the date stamp characters for a saved
                 proof. */
              /* print2("%s$([%s]$)\n", space(indentation), date()); */
              /* 4/23/04 nm Initialize with a "?" date followed by today's
                 date.  The "?" date can be edited by the user when the
                 proof is becomes "official." */
              /*print2("%s$([?]$) $([%s]$)\n", space(indentation), date());*/
              /* 7-Sep-04 Put space around "[<date>]" */
              /*print2("%s$( [?] $) $( [%s] $)\n", space(indentation), date());*/
              /* 30-Nov-2013 remove the unknown date placeholder */
              print2("%s$( [%s] $)\n", space(indentation), date());
            } else {
              if (saveFlag && (instr(1, str2, "$([")
                  /* 7-Sep-04 Allow both "$([<date>])$" and "$( [<date>] )$" */
                  || instr(1, str2, "$( ["))) {
                /* An old date stamp existed, and we're saving the proof to
                   the output file.  Make sure the indentation of the old
                   date stamp (which exists in the labelSection of the
                   next statement) matches the indentation of the saved
                   proof.  To do this, we "delete" the indentation spaces
                   on the old date in the labelSection of the next statement,
                   and we put the actual required indentation spaces at
                   the end of the saved proof.  This is done because the
                   labelSectionPtr of the next statement does not point to
                   an isolated string that can be allocated/deallocated but
                   rather to a place in the input source buffer. */
                /* Correct the indentation on old date */
                while ((statement[i + 1].labelSectionPtr)[0] !=
                    '$') {
                  /* "Delete" spaces before old date (by moving source
                     buffer pointer forward), and also "delete"
                     the \n that comes before those spaces */
                  /* If the proof is saved a 2nd time, this loop will
                     not be entered because the pointer will already be
                     at the "$". */
                  (statement[i + 1].labelSectionPtr)++;
                  (statement[i + 1].labelSectionLen)--;
                }
                if (!outputToString) bug(1115);
                /* The final \n will not appear in final output (done in
                   outputStatement() in mmpars.c) because the proofSectionLen
                   below is adjusted to omit it.  This will allow the
                   space(indentation) to appear before the old date without an
                   intervening \n. */
                print2("%s\n", space(indentation));
              }
            }
          }
          if (saveFlag) {
            sourceChanged = 1;
            proofChanged = 0;
            if (processUndoStack(NULL, PUS_GET_STATUS, "", 0)) {
              /* The UNDO stack may not be empty */
              proofSavedFlag = 1; /* UNDO stack empty no longer reliably
                             indicates that proof hasn't changed */
            }
            /* ASCII 1 is a flag that string was allocated and not part of
               original source file text buffer */
            let(&printString, cat(chr(1), "\n", printString, NULL));
            if (statement[i].proofSectionPtr[-1] == 1) {
              /* Deallocate old proof if not original source */
              let(&str1, "");
              str1 = statement[i].proofSectionPtr - 1;
              let(&str1, "");
            }
            statement[i].proofSectionPtr = printString + 1;
            /* Subtr 1 char for ASCII 1 at beg, 1 char for "\n" */
            statement[i].proofSectionLen = (long)strlen(printString) - 2;
            /* Reset printString without deallocating */
            printString = "";
            outputToString = 0;
            if (!pipFlag) {
              printLongLine(cat("The proof of \"", statement[i].labelName,
                  "\" has been reformatted and saved internally.",
                  NULL), "", " ");
            } else {
              printLongLine(cat("The new proof of \"", statement[i].labelName,
                  "\" has been saved internally.",
                  NULL), "", " ");
            }
          } else {
            /* 19-Apr-2015 so */
            /* 24-Apr-2015 nm Reverted */
            /*print2("\n");*/ /* Add a blank line to make clipping easier */
            print2(cat(
                "---------The proof of \"",statement[i].labelName,
                /* "\" to clip out ends above this line.\n",NULL)); */
                /* 24-Apr-2015 nm */
                "\" (", str(l), " bytes) ends above this line.\n", NULL));
          } /* End if saveFlag */
          nmbrLet(&nmbrSaveProof, NULL_NMBRSTRING);
          if (pipFlag) break; /* Only one iteration for NEW_PROOF stuff */
          continue;  /* to next s iteration */
        } /* end if (switchPos("/ PACKED") || switchPos("/ NORMAL") ||
            switchPos("/ COMPRESSED") || saveFlag) */

        if (saveFlag) bug(1112); /* Shouldn't get here */

        if (!pipFlag) {
          i = showStatement;
        } else {
          i = proveStatement;
        }
        if (texFlag) {
          outputToString = 1; /* Flag for print2 to add to printString */
          if (!htmlFlag) {
            if (!oldTexFlag) {
              /* 14-Sep-2010 nm */
              print2("\\begin{proof}\n");
              print2("\\begin{align}\n");
            } else {
              print2("\n");
              print2("\\vspace{1ex} %%1\n");
              printLongLine(cat("Proof of ",
                  "{\\tt ",
                  asciiToTt(statement[i].labelName),
                  "}:", NULL), "", " ");
              print2("\n");
              print2("\n");
            }
          } else { /* htmlFlag */
            bug(1118);
            /*???? The code below is obsolete - now down in show statement*/
            /*
            print2("<CENTER><TABLE BORDER CELLSPACING=0 BGCOLOR=%s>\n",
                MINT_BACKGROUND_COLOR);
            print2("<CAPTION><B>Proof of Theorem <FONT\n");
            printLongLine(cat("   COLOR=", GREEN_TITLE_COLOR, ">",
                asciiToTt(statement[i].labelName),
                "</FONT></B></CAPTION>", NULL), "", "\"");
            print2(
                "<TR><TD><B>Step</B></TD><TD><B>Hyp</B></TD><TD><B>Ref</B>\n");
            print2("</TD><TD><B>Expression</B></TD></TR>\n");
            */
          }
          outputToString = 0;
          /* 8/26/99: Obsolete: */
          /* printTexLongMath in typeProof will do this
          fprintf(texFilePtr, "%s", printString);
          let(&printString, "");
          */
          /* 8/26/99: printTeXLongMath now clears printString in LaTeX
             mode before starting its output, so we must put out the
             printString ourselves here */
          fprintf(texFilePtr, "%s", printString);
          let(&printString, ""); /* We'll clr it anyway */
        } else { /* !texFlag */
          /* Terminal output - display the statement if wildcard is used */
          if (!pipFlag) {
            /* 30-Jan-06 nm Added single-character-match wildcard argument */
            if (instr(1, labelMatch, "*") || instr(1, labelMatch, "?")) {
              if (!print2("Proof of \"%s\":\n", statement[i].labelName))
                break; /* Break for speedup if user quit */
            }
          }
        }


        if (texFlag) print2("Outputting proof of \"%s\"...\n",
            statement[s].labelName);

        typeProof(i,
            pipFlag,
            startStep,
            endStep,
            endIndent,
            essentialFlag,
            renumberFlag,
            unknownFlag,
            notUnifiedFlag,
            reverseFlag,
            noIndentFlag,
            splitColumn,
            skipRepeatedSteps, /* 28-Jun-2013 nm */
            texFlag,
            htmlFlag);
        if (texFlag) {
          if (!htmlFlag) {
            /* 14-Sep-2010 nm */
            if (!oldTexFlag) {
              outputToString = 1;
              print2("\\end{align}\n");
              print2("\\end{proof}\n");
              print2("\n");
              outputToString = 0;
              fprintf(texFilePtr, "%s", printString);
              let(&printString, "");
            } else {
            }
          } else { /* htmlFlag */
            outputToString = 1;
            print2("</TABLE>\n");
            print2("</CENTER>\n");
            /* print trailer will close out string later */
            outputToString = 0;
          }
        }

        /*???CLEAN UP */
        /*nmbrLet(&wrkProof.proofString, nmbrSaveProof);*/

        /*E*/ if (0) { /* for debugging: */
          printLongLine(nmbrCvtRToVString(wrkProof.proofString)," "," ");
          print2("\n");

          nmbrLet(&nmbrSaveProof, nmbrSquishProof(wrkProof.proofString));
          printLongLine(nmbrCvtRToVString(nmbrSaveProof)," "," ");
          print2("\n");

          nmbrLet(&nmbrTmp, nmbrUnsquishProof(nmbrSaveProof));
          printLongLine(nmbrCvtRToVString(nmbrTmp)," "," ");

          nmbrLet(&nmbrTmp, nmbrGetTargetHyp(nmbrSaveProof,showStatement));
          printLongLine(nmbrCvtAnyToVString(nmbrTmp)," "," "); print2("\n");

          nmbrLet(&nmbrTmp, nmbrGetEssential(nmbrSaveProof));
          printLongLine(nmbrCvtAnyToVString(nmbrTmp)," "," "); print2("\n");

          cleanWrkProof(); /* Deallocate verifyProof storage */
        } /* end debugging */

        if (pipFlag) break; /* Only one iteration for NEW_PROOF stuff */
      } /* Next s */
      if (!q) {
        /* No matching statement was found */
        printLongLine(cat("?There is no $p statement whose label matches \"",
            (cmdMatches("MIDI")) ? fullArg[1] : fullArg[2],
            "\".  ",
            "Use SHOW LABELS to see list of valid labels.", NULL), "", " ");
      } else {
        if (saveFlag) {
          print2("Remember to use WRITE SOURCE to save changes permanently.\n");
        }
        if (texFlag) {
          print2("The LaTeX source was written to \"%s\".\n", texFileName);
         /* 14-Sep-2010 nm Added OLD_TEX */
         oldTexFlag = 0;
        }
      }

      continue;
    } /* if (cmdMatches("SHOW/SAVE [NEW_]PROOF" or" MIDI") */


/*E*/ /*???????? DEBUG command for debugging only */
    if (cmdMatches("DBG")) {
      print2("DEBUGGING MODE IS FOR DEVELOPER'S USE ONLY!\n");
      print2("Argument:  %s\n", fullArg[1]);
      nmbrLet(&nmbrTmp, parseMathTokens(fullArg[1], proveStatement));
      for (j = 0; j < 3; j++) {
        print2("Trying depth %ld\n", j);
        nmbrTmpPtr = proveFloating(nmbrTmp, proveStatement, j, 0, 0);
        if (nmbrLen(nmbrTmpPtr)) break;
      }

      print2("Result:  %s\n", nmbrCvtRToVString(nmbrTmpPtr));
      nmbrLet(&nmbrTmpPtr, NULL_NMBRSTRING);

      continue;
    }
/*E*/ /*???????? DEBUG command for debugging only */

    if (cmdMatches("PROVE")) {

      /* 14-Sep-2012 nm */
      /* Get the unique statement matching the fullArg[1] pattern */
      i = getStatementNum(fullArg[1],
          1/*startStmt*/,
          statements + 1  /*maxStmt*/,
          0/*aAllowed*/,
          1/*pAllowed*/,
          0/*eAllowed*/,
          0/*fAllowed*/,
          0/*efOnlyForMaxStmt*/,
          1/*uniqueFlag*/);
      if (i == -1) {
        continue; /* Error msg was provided if not unique */
      }
      proveStatement = i;

      /*********** 14-Sep-2012 replaced by getStatementNum()
      /@??? Make sure only $p statements are allowed. @/
      for (i = 1; i <= statements; i++) {
        if (!strcmp(fullArg[1],statement[i].labelName)) break;
      }
      if (i > statements) {
        printLongLine(cat("?There is no statement with label \"",
            fullArg[1], "\".  ",
            "Use SHOW LABELS for a list of valid labels.", NULL), "", " ");
        proveStatement = 0;
        continue;
      }
      proveStatement = i;
      if (statement[proveStatement].type != (char)p_) {
        printLongLine(cat("?Statement \"", fullArg[1],
            "\" is not a $p statement.", NULL), "", " ");
        proveStatement = 0;
        continue;
      }
      ************** end of 14-Sep-2012 deletion ************/

      print2(
"Entering the Proof Assistant.  HELP PROOF_ASSISTANT for help, EXIT to exit.\n");

      /* Obsolete:
      print2("You will be working on the proof of statement %s:\n",
          statement[proveStatement].labelName);
      printLongLine(cat("  $p ", nmbrCvtMToVString(
          statement[proveStatement].mathString), NULL), "    ", " ");
      */

      PFASmode = 1; /* Set mode for commands here and in mmcmdl.c */
      /* Note:  Proof Assistant mode can equivalently be determined by:
            nmbrLen(proofInProgress.proof) != 0  */

      parseProof(proveStatement);
      verifyProof(proveStatement); /* Necessary to set RPN stack ptrs
                                      before calling cleanWrkProof() */
      if (wrkProof.errorSeverity > 1) {
     print2("The starting proof has a severe error.  It will not be used.\n");
        nmbrLet(&nmbrSaveProof, nmbrAddElement(NULL_NMBRSTRING, -(long)'?'));
      } else {
        nmbrLet(&nmbrSaveProof, wrkProof.proofString);
      }
      cleanWrkProof(); /* Deallocate verifyProof storage */

      /* Right now, only non-packed proofs are handled. */
      nmbrLet(&nmbrSaveProof, nmbrUnsquishProof(nmbrSaveProof));

      /* Assign initial proof structure */
      if (nmbrLen(proofInProgress.proof)) bug(1113); /* Should've been deall.*/
      nmbrLet(&proofInProgress.proof, nmbrSaveProof);
      i = nmbrLen(proofInProgress.proof);
      pntrLet(&proofInProgress.target, pntrNSpace(i));
      pntrLet(&proofInProgress.source, pntrNSpace(i));
      pntrLet(&proofInProgress.user, pntrNSpace(i));
      nmbrLet((nmbrString **)(&((proofInProgress.target)[i - 1])),
          statement[proveStatement].mathString);
      pipDummyVars = 0;

      /* Assign known subproofs */
      assignKnownSubProofs();

      /* Initialize remaining steps */
      for (j = 0; j < i/*proof length*/; j++) {
        if (!nmbrLen((proofInProgress.source)[j])) {
          initStep(j);
        }
      }

      /* Unify whatever can be unified */
      autoUnify(0); /* 0 means no "congrats" message */

/*
      print2(
"Periodically save your work with SAVE NEW_PROOF and WRITE SOURCE.\n");
      print2(
"There is no UNDO command yet.  You can OPEN LOG to track what you've done.\n");
*/
      /* Show the user the statement to be proved */
      print2("You will be working on statement (from \"SHOW STATEMENT %s\"):\n",
          statement[proveStatement].labelName);
      typeStatement(proveStatement /*showStatement*/,
          1 /*briefFlag*/,
          0 /*commentOnlyFlag*/,
          0 /*texFlag*/,
          0 /*htmlFlag*/);

      if (!nmbrElementIn(1, proofInProgress.proof, -(long)'?')) {
        print2(
        "Note:  The proof you are starting with is already complete.\n");
      } else {

        print2(
     "Unknown step summary (from \"SHOW NEW_PROOF / UNKNOWN\"):\n");
        /* 6/14/98 - Automatically display new unknown steps
         ???Future - add switch to enable/defeat this */
        typeProof(proveStatement,
            1 /*pipFlag*/,
            0 /*startStep*/,
            0 /*endStep*/,
            0 /*endIndent*/,
            1 /*essentialFlag*/,
            0 /*renumberFlag*/,
            1 /*unknownFlag*/,
            0 /*notUnifiedFlag*/,
            0 /*reverseFlag*/,
            0 /*noIndentFlag*/,
            0 /*splitColumn*/,
            0 /*skipRepeatedSteps*/, /* 28-Jun-2013 nm */
            0 /*texFlag*/,
            0 /*htmlFlag*/);
        /* 6/14/98 end */
      }

      processUndoStack(NULL, PUS_INIT, "", 0); /* Optional? */
      /* Put the initial proof into the UNDO stack; we don't need
         the info string since it won't be undone */
      processUndoStack(&proofInProgress, PUS_PUSH, "", 0);
      continue;
    }


    /* 1-Nov-2013 nm Added UNDO */
    if (cmdMatches("UNDO")) {
      processUndoStack(&proofInProgress, PUS_UNDO, "", 0);
      proofChanged = 1; /* Maybe make this more intelligent some day */
      /* 6/14/98 - Automatically display new unknown steps
         ???Future - add switch to enable/defeat this */
      typeProof(proveStatement,
          1 /*pipFlag*/,
          0 /*startStep*/,
          0 /*endStep*/,
          0 /*endIndent*/,
          1 /*essentialFlag*/,
          0 /*renumberFlag*/,
          1 /*unknownFlag*/,
          0 /*notUnifiedFlag*/,
          0 /*reverseFlag*/,
          0 /*noIndentFlag*/,
          0 /*splitColumn*/,
          0 /*skipRepeatedSteps*/, /* 28-Jun-2013 nm */
          0 /*texFlag*/,
          0 /*htmlFlag*/);
      /* 6/14/98 end */
      continue;
    }

    /* 1-Nov-2013 nm Added REDO */
    if (cmdMatches("REDO")) {
      processUndoStack(&proofInProgress, PUS_REDO, "", 0);
      proofChanged = 1; /* Maybe make this more intelligent some day */
      /* 6/14/98 - Automatically display new unknown steps
         ???Future - add switch to enable/defeat this */
      typeProof(proveStatement,
          1 /*pipFlag*/,
          0 /*startStep*/,
          0 /*endStep*/,
          0 /*endIndent*/,
          1 /*essentialFlag*/,
          0 /*renumberFlag*/,
          1 /*unknownFlag*/,
          0 /*notUnifiedFlag*/,
          0 /*reverseFlag*/,
          0 /*noIndentFlag*/,
          0 /*splitColumn*/,
          0 /*skipRepeatedSteps*/, /* 28-Jun-2013 nm */
          0 /*texFlag*/,
          0 /*htmlFlag*/);
      /* 6/14/98 end */
      continue;
    }

    if (cmdMatches("UNIFY")) {
      m = nmbrLen(proofInProgress.proof); /* Original proof length */
      proofChangedFlag = 0;
      if (cmdMatches("UNIFY STEP")) {

        s = (long)val(fullArg[2]); /* Step number */
        if (s > m || s < 1) {
          print2("?The step must be in the range from 1 to %ld.\n", m);
          continue;
        }

        interactiveUnifyStep(s - 1, 1); /* 2nd arg. means print msg if
                                           already unified */

        /* (The interactiveUnifyStep handles all messages.) */
        /* print2("... */

        autoUnify(1);
        if (proofChangedFlag) {
          proofChanged = 1; /* Cumulative flag */
          processUndoStack(&proofInProgress, PUS_PUSH, fullArgString, 0);
        }
        continue;
      }

      /* "UNIFY ALL" */
      if (!switchPos("/ INTERACTIVE")) {
        autoUnify(1);
        if (!proofChangedFlag) {
          print2("No new unifications were made.\n");
        } else {
          print2(
  "Steps were unified.  SHOW NEW_PROOF / NOT_UNIFIED to see any remaining.\n");
          proofChanged = 1; /* Cumulative flag */
          processUndoStack(&proofInProgress, PUS_PUSH, fullArgString, 0);
        }
      } else {
        q = 0;
        while (1) {
          /* Repeat the unifications over and over until done, since
             a later unification may improve the ability of an aborted earlier
             one to be done without timeout */
          proofChangedFlag = 0; /* This flag is set by autoUnify() and
                                   interactiveUnifyStep() */
          autoUnify(0);
          for (s = m - 1; s >= 0; s--) {
            interactiveUnifyStep(s, 0); /* 2nd arg. means no msg if already
                                           unified */
          }
          autoUnify(1); /* 1 means print congratulations if complete */
          if (!proofChangedFlag) {
            if (!q) {
              print2("No new unifications were made.\n");
            } else {
              /* If q=1, then we are in the 2nd or later pass, which means
                 there was a change in the 1st pass. */
              print2(
  "Steps were unified.  SHOW NEW_PROOF / NOT_UNIFIED to see any remaining.\n");
              proofChanged = 1; /* Cumulative flag */
              processUndoStack(&proofInProgress, PUS_PUSH, fullArgString, 0);
            }
            break; /* while (1) */
          } else {
            q = 1; /* Flag that we're starting a 2nd or later pass */
          }
        } /* End while (1) */
      }
      /* 6/14/98 - Automatically display new unknown steps
         ???Future - add switch to enable/defeat this */
      typeProof(proveStatement,
          1 /*pipFlag*/,
          0 /*startStep*/,
          0 /*endStep*/,
          0 /*endIndent*/,
          1 /*essentialFlag*/,
          0 /*renumberFlag*/,
          1 /*unknownFlag*/,
          0 /*notUnifiedFlag*/,
          0 /*reverseFlag*/,
          0 /*noIndentFlag*/,
          0 /*splitColumn*/,
          0 /*skipRepeatedSteps*/, /* 28-Jun-2013 nm */
          0 /*texFlag*/,
          0 /*htmlFlag*/);
      /* 6/14/98 end */
      continue;
    }

    if (cmdMatches("MATCH")) {

      maxEssential = -1; /* Default:  no maximum */
      i = switchPos("/ MAX_ESSENTIAL_HYP");
      if (i) maxEssential = (long)val(fullArg[i + 1]);

      if (cmdMatches("MATCH STEP")) {

        s = (long)val(fullArg[2]); /* Step number */
        m = nmbrLen(proofInProgress.proof); /* Original proof length */
        if (s > m || s < 1) {
          print2("?The step must be in the range from 1 to %ld.\n", m);
          continue;
        }
        if ((proofInProgress.proof)[s - 1] != -(long)'?') {
          print2(
    "?Step %ld is already assigned.  Only unknown steps can be matched.\n", s);
          continue;
        }

        interactiveMatch(s - 1, maxEssential);
        n = nmbrLen(proofInProgress.proof); /* New proof length */
        if (n != m) {
          if (s != m) {
            printLongLine(cat("Steps ", str(s), ":",
                str(m), " are now ", str(s - m + n), ":", str(n), ".",
                NULL),
                "", " ");
          } else {
            printLongLine(cat("Step ", str(m), " is now step ", str(n), ".",
                NULL),
                "", " ");
          }
        }

        autoUnify(1);
        proofChanged = 1; /* Cumulative flag */
        /* 1-Nov-2013 nm Why is proofChanged set unconditionally above?
           Do we need the processUndoStack() call? */
        processUndoStack(&proofInProgress, PUS_PUSH, fullArgString, 0);

        continue;
      } /* End if MATCH STEP */

      if (cmdMatches("MATCH ALL")) {

        m = nmbrLen(proofInProgress.proof); /* Original proof length */

        k = 0;
        proofChangedFlag = 0;

        if (switchPos("/ ESSENTIAL")) {
          nmbrLet(&nmbrTmp, nmbrGetEssential(proofInProgress.proof));
        }

        for (s = m; s > 0; s--) {
          /* Match only unknown steps */
          if ((proofInProgress.proof)[s - 1] != -(long)'?') continue;
          /* Match only essential steps if specified */
          if (switchPos("/ ESSENTIAL")) {
            if (!nmbrTmp[s - 1]) continue;
          }

          interactiveMatch(s - 1, maxEssential);
          if (proofChangedFlag) {
            k = s; /* Save earliest step changed */
            proofChangedFlag = 0;
          }
          print2("\n");
        }
        if (k) {
          proofChangedFlag = 1; /* Restore it */
          proofChanged = 1; /* Cumulative flag */
          processUndoStack(&proofInProgress, PUS_PUSH, fullArgString, 0);
          print2("Steps %ld and above have been renumbered.\n", k);
        }
        autoUnify(1);

        continue;
      } /* End if MATCH ALL */
    }

    if (cmdMatches("LET")) {

      errorCount = 0;
      nmbrLet(&nmbrTmp, parseMathTokens(fullArg[4], proveStatement));
      if (errorCount) {
        /* Parsing issued error message(s) */
        errorCount = 0;
        continue;
      }

      if (cmdMatches("LET VARIABLE")) {
        if (((vstring)(fullArg[2]))[0] != '$') {
          print2(
   "?The target variable must be of the form \"$<integer>\", e.g. \"$23\".\n");
          continue;
        }
        n = (long)val(right(fullArg[2], 2));
        if (n < 1 || n > pipDummyVars) {
          print2("?The target variable must be between $1 and $%ld.\n",
              pipDummyVars);
          continue;
        }

        replaceDummyVar(n, nmbrTmp);

        autoUnify(1);


        proofChangedFlag = 1; /* Flag to push 'undo' stack */
        proofChanged = 1; /* Cumulative flag */
        processUndoStack(&proofInProgress, PUS_PUSH, fullArgString, 0);

      }

      if (cmdMatches("LET STEP")) {

        /* 14-Sep-2012 nm */
        s = getStepNum(fullArg[1], proofInProgress.proof,
            0 /* ALL not allowed */);
        if (s == -1) continue;  /* Error; message was provided already */

        /************** 14-Sep-2012 replaced by getStepNum()
        s = (long)val(fullArg[2]); /@ Step number @/

        /@ 16-Apr-06 nm Added LET STEP n where n <= 0: 0 = last,
           -1 = penultimate, etc. _unknown_ step @/
        /@ Unlike ASSIGN LAST/FIRST and IMPROVE LAST/FIRST, it probably
           doesn't make sense to add LAST/FIRST to LET STEP since known
           steps can also be LET.  The main purpose of LET STEP n, n<=0, is
           to use with scripting for mmj2 imports. @/
        offset = 0;
        if (s <= 0) {
          offset = - s + 1;
          s = 1; /@ Temp. until we figure out which step @/
        }
        /@ End of 16-Apr-06 @/

        m = nmbrLen(proofInProgress.proof); /@ Original proof length @/
        if (s > m || s < 1) {
          print2("?The step must be in the range from 1 to %ld.\n", m);
          continue;
        }

        /@ 16-Apr-06 nm Added LET STEP n where n <= 0: 0 = last,
           1 = penultimate, etc. _unknown_ step @/
        if (offset > 0) {  /@ step <= 0 @/
          /@ Get the essential step flags @/
          s = 0; /@ Use as flag that step was found @/
          nmbrLet(&essentialFlags, nmbrGetEssential(proofInProgress.proof));
          /@ Scan proof backwards until last essential unknown step found @/
          /@ 16-Apr-06 - count back 'offset' unknown steps @/
          j = offset;
          for (i = m; i >= 1; i--) {
            if (essentialFlags[i - 1]
                && (proofInProgress.proof)[i - 1] == -(long)'?') {
              j--;
              if (j == 0) {
                /@ Found it @/
                s = i;
                break;
              }
            }
          } /@ Next i @/
          if (s == 0) {
            if (offset == 1) {
              print2("?There are no unknown essential steps.\n");
            } else {
              print2("?There are not at least %ld unknown essential steps.\n",
                offset);
            }
            continue;
          }
        } /@ if offset > 0 @/
        /@ End of 16-Apr-06 @/
        ******************** end 14-Sep-2012 deletion ********/

        /* Check to see if the statement selected is allowed */
        if (!checkMStringMatch(nmbrTmp, s - 1)) {
          printLongLine(cat("?Step ", str(s), " cannot be unified with \"",
              nmbrCvtMToVString(nmbrTmp),"\".", NULL), " ", " ");
          continue;
        }

        /* Assign the user string */
        nmbrLet((nmbrString **)(&((proofInProgress.user)[s - 1])), nmbrTmp);

        autoUnify(1);
        proofChangedFlag = 1; /* Flag to push 'undo' stack */
        proofChanged = 1; /* Cumulative flag */
        processUndoStack(&proofInProgress, PUS_PUSH, fullArgString, 0);
      }
      /* 6/14/98 - Automatically display new unknown steps
         ???Future - add switch to enable/defeat this */
      typeProof(proveStatement,
          1 /*pipFlag*/,
          0 /*startStep*/,
          0 /*endStep*/,
          0 /*endIndent*/,
          1 /*essentialFlag*/,
          0 /*renumberFlag*/,
          1 /*unknownFlag*/,
          0 /*notUnifiedFlag*/,
          0 /*reverseFlag*/,
          0 /*noIndentFlag*/,
          0 /*splitColumn*/,
          0 /*skipRepeatedSteps*/, /* 28-Jun-2013 nm */
          0 /*texFlag*/,
          0 /*htmlFlag*/);
      /* 6/14/98 end */
      continue;
    }


    if (cmdMatches("ASSIGN")) {

      /* 10/4/99 - Added LAST - this means the last unknown step shown
         with SHOW NEW_PROOF/ESSENTIAL/UNKNOWN */
      /* 11-Dec-05 nm - Added FIRST - this means the first unknown step shown
         with SHOW NEW_PROOF/ESSENTIAL/UNKNOWN */
      /* 16-Apr-06 nm - Handle nonpositive step number: 0 = last,
         -1 = penultimate, etc.*/
      /* 14-Sep-2012 nm All the above now done by getStepNum() */
      s = getStepNum(fullArg[1], proofInProgress.proof,
          0 /* ALL not allowed */);
      if (s == -1) continue;  /* Error; message was provided already */


      /****** replaced by getStepNum()  nm 14-Sep-2012
      offset = 0; /@ 16-Apr-06 @/
      let(&str1, fullArg[1]); /@ To avoid void pointer problems with fullArg @/
      if (toupper((unsigned char)(str1[0])) == 'L'
          || toupper((unsigned char)(str1[0])) == 'F') {
                                          /@ "ASSIGN LAST" or "ASSIGN FIRST" @/
                                          /@ 11-Dec-05 nm @/
        s = 1; /@ Temporary until we figure out which step @/
        offset = 1;          /@ 16-Apr-06 @/
      } else {
        s = (long)val(fullArg[1]); /@ Step number @/
        if (strcmp(fullArg[1], str(s))) {
          print2("?Expected either a number or FIRST or LAST after ASSIGN.\n");
                                                             /@ 11-Dec-05 nm @/
          continue;
        }
        if (s <= 0) {         /@ 16-Apr-06 @/
          offset = - s + 1;   /@ 16-Apr-06 @/
          s = 1; /@ Temporary until we figure out which step @/ /@ 16-Apr-06 @/
        }                     /@ 16-Apr-06 @/
      }
      ******************** end 14-Sep-2012 deletion ********/

      /* 14-Sep-2012 nm */
      /* Get the unique statement matching the fullArg[2] pattern */
      k = getStatementNum(fullArg[2],
          1/*startStmt*/,
          proveStatement  /*maxStmt*/,
          1/*aAllowed*/,
          1/*pAllowed*/,
          1/*eAllowed*/,
          1/*fAllowed*/,
          1/*efOnlyForMaxStmt*/,
          1/*uniqueFlag*/);
      if (k == -1) {
        continue; /* Error msg was provided if not unique */
      }

      /*********** 14-Sep-2012 replaced by getStatementNum()
      for (i = 1; i <= statements; i++) {
        if (!strcmp(fullArg[2], statement[i].labelName)) {
          /@ If a $e or $f, it must be a hypothesis of the statement
             being proved @/
          if (statement[i].type == (char)e_ || statement[i].type == (char)f_){
            if (!nmbrElementIn(1, statement[proveStatement].reqHypList, i) &&
                !nmbrElementIn(1, statement[proveStatement].optHypList, i))
                continue;
          }
          break;
        }
      }
      if (i > statements) {
        printLongLine(cat("?The statement with label \"",
            fullArg[2],
            "\" was not found or is not a hypothesis of the statement ",
            "being proved.  ",
            "Use SHOW LABELS for a list of valid labels.", NULL), "", " ");
        continue;
      }
      k = i;

      if (k >= proveStatement) {
        print2(
   "?You must specify a statement that occurs earlier the one being proved.\n");
        continue;
      }
      ***************** end of 14-Sep-2012 deletion ************/

      m = nmbrLen(proofInProgress.proof); /* Original proof length */


      /****** replaced by getStepNum()  nm 14-Sep-2012
      if (s > m || s < 1) {
        print2("?The step must be in the range from 1 to %ld.\n", m);
        continue;
      }

      /@ 10/4/99 - For ASSIGN FIRST/LAST command, figure out the last unknown
         essential step @/                      /@ 11-Dec-05 nm - Added LAST @/
      /@if (toupper(str1[0]) == 'L' || toupper(str1[0]) == 'F') {@/
                                /@ "ASSIGN LAST or FIRST" @/ /@ 11-Dec-05 nm @/
      if (offset > 0) {  /@ LAST, FIRST, or step <= 0 @/ /@ 16-Apr-06 @/
        /@ Get the essential step flags @/
        s = 0; /@ Use as flag that step was found @/
        nmbrLet(&essentialFlags, nmbrGetEssential(proofInProgress.proof));
        /@ if (toupper((unsigned char)(str1[0])) == 'L') { @/
        if (toupper((unsigned char)(str1[0])) != 'F') {   /@ 16-Apr-06 @/
          /@ Scan proof backwards until last essential unknown step is found @/
          /@ 16-Apr-06 - count back 'offset' unknown steps @/
          j = offset;      /@ 16-Apr-06 @/
          for (i = m; i >= 1; i--) {
            if (essentialFlags[i - 1]
                && (proofInProgress.proof)[i - 1] == -(long)'?') {
              j--;          /@ 16-Apr-06 @/
              if (j == 0) {  /@ 16-Apr-06 @/
                /@ Found it @/
                s = i;
                break;
              }             /@ 16-Apr-06 @/
            }
          } /@ Next i @/
        } else {
          /@ 11-Dec-05 nm Added ASSIGN FIRST @/
          /@ Scan proof forwards until first essential unknown step is found @/
          for (i = 1; i <= m; i++) {
            if (essentialFlags[i - 1]
                && (proofInProgress.proof)[i - 1] == -(long)'?') {
              /@ Found it @/
              s = i;
              break;
            }
          } /@ Next i @/
        }
        if (s == 0) {
          if (offset == 1) {                                /@ 16-Apr-06 @/
            print2("?There are no unknown essential steps.\n");
          } else {                                          /@ 16-Apr-06 @/
            print2("?There are not at least %ld unknown essential steps.\n",
              offset);                                      /@ 16-Apr-06 @/
          }                                                 /@ 16-Apr-06 @/
          continue;
        }
      }
      ******************** end 14-Sep-2012 deletion ********/

      /* Check to see that the step is an unknown step */
      if ((proofInProgress.proof)[s - 1] != -(long)'?') {
        print2(
        "?Step %ld is already assigned.  You can only assign unknown steps.\n"
            , s);
        continue;
      }


      /* Check to see if the statement selected is allowed */
      if (!checkStmtMatch(k, s - 1)) {
        print2("?Statement \"%s\" cannot be unified with step %ld.\n",
          statement[k].labelName, s);
        continue;
      }

      assignStatement(k /*statement#*/, s - 1 /*step*/);

      n = nmbrLen(proofInProgress.proof); /* New proof length */
      autoUnify(1);

      /* Automatically interact with user if step not unified */
      /* ???We might want to add a setting to defeat this if user doesn't
         like it */
      /* 8-Apr-05 nm Since ASSIGN LAST is often run from a commmand file, don't
         interact if / NO_UNIFY is specified, so response is predictable */
      if (switchPos("/ NO_UNIFY") == 0) {
        interactiveUnifyStep(s - m + n - 1, 2); /* 2nd arg. means print msg if
                                                 already unified */
      } /* if NO_UNIFY flag not set */

      /* 8-Apr-05 nm Commented out:
      if (m == n) {
        print2("Step %ld was assigned statement %s.\n",
          s, statement[k].labelName);
      } else {
        if (s != m) {
          printLongLine(cat("Step ", str(s),
              " was assigned statement ", statement[k].labelName,
              ".  Steps ", str(s), ":",
              str(m), " are now ", str(s - m + n), ":", str(n), ".",
              NULL),
              "", " ");
        } else {
          printLongLine(cat("Step ", str(s),
              " was assigned statement ", statement[k].labelName,
              ".  Step ", str(m), " is now step ", str(n), ".",
              NULL),
              "", " ");
        }
      }
      */
      /* 8-Apr-05 nm Added: */
      /* 1-Nov-2013 nm No longer needed because of UNDO
      printLongLine(cat("To undo the assignment, DELETE STEP ",
              str(s - m + n), " and if needed INITIALIZE, UNIFY.",
              NULL),
              "", " ");
      */

      /* 6/14/98 - Automatically display new unknown steps
         ???Future - add switch to enable/defeat this */
      typeProof(proveStatement,
          1 /*pipFlag*/,
          0 /*startStep*/,
          0 /*endStep*/,
          0 /*endIndent*/,
          1 /*essentialFlag*/,
          0 /*renumberFlag*/,
          1 /*unknownFlag*/,
          0 /*notUnifiedFlag*/,
          0 /*reverseFlag*/,
          0 /*noIndentFlag*/,
          0 /*splitColumn*/,
          0 /*skipRepeatedSteps*/, /* 28-Jun-2013 nm */
          0 /*texFlag*/,
          0 /*htmlFlag*/);
      /* 6/14/98 end */


      proofChangedFlag = 1; /* Flag to push 'undo' stack (future) */
      proofChanged = 1; /* Cumulative flag */
      processUndoStack(&proofInProgress, PUS_PUSH, fullArgString, 0);
      continue;

    } /* cmdMatches("ASSIGN") */


    if (cmdMatches("REPLACE")) {

      /* s = (long)val(fullArg[1]);  obsolete */ /* Step number */

      /* 14-Sep-2012 nm */
      step = getStepNum(fullArg[1], proofInProgress.proof,
          0 /* ALL not allowed */);
      if (step == -1) continue;  /* Error; message was provided already */

      /* This limitation is due to the assignKnownSteps call below which
         does not tolerate unknown steps. */
      /******* 10/20/02  Limitation removed
      if (nmbrElementIn(1, proofInProgress.proof, -(long)'?')) {
        print2("?The proof must be complete before you can use REPLACE.\n");
        continue;
      }
      *******/

      /* 14-Sep-2012 nm */
      /* Get the unique statement matching the fullArg[2] pattern */
      stmt = getStatementNum(fullArg[2],
          1/*startStmt*/,
          proveStatement  /*maxStmt*/,
          1/*aAllowed*/,
          1/*pAllowed*/,
          0/*eAllowed*/, /* Not implemented (yet?) */
          0/*fAllowed*/, /* Not implemented (yet?) */
          1/*efOnlyForMaxStmt*/,
          1/*uniqueFlag*/);
      if (stmt == -1) {
        continue; /* Error msg was provided if not unique */
      }

      /*********** 14-Sep-2012 replaced by getStatementNum()
      for (i = 1; i <= statements; i++) {
        if (!strcmp(fullArg[2], statement[i].labelName)) {
          /@ If a $e or $f, it must be a hypothesis of the statement
             being proved @/
          if (statement[i].type == (char)e_ || statement[i].type == (char)f_){
            if (!nmbrElementIn(1, statement[proveStatement].reqHypList, i) &&
                !nmbrElementIn(1, statement[proveStatement].optHypList, i))
                continue;
          }
          break;
        }
      }
      if (i > statements) {
        printLongLine(cat("?The statement with label \"",
            fullArg[2],
            "\" was not found or is not a hypothesis of the statement ",
            "being proved.  ",
            "Use SHOW LABELS for a list of valid labels.", NULL), "", " ");
        continue;
      }
      k = i;

      if (k >= proveStatement) {


        print2(
   "?You must specify a statement that occurs earlier the one being proved.\n");
        continue;
      }
      ****************************** end of 14-Sep-2012 deletion *********/

      m = nmbrLen(proofInProgress.proof); /* Original proof length */

      /************** 14-Sep-2012 replaced by getStepNum()
      if (s > m || s < 1) {
        print2("?The step must be in the range from 1 to %ld.\n", m);
        continue;
      }
      ************* end of 14-Sep-2012 deletion **************/

      /* Check to see that the step is a known step */
      /* 22-Aug-2012 nm This check was deleted because it is unnecessary  */
      /*
      if ((proofInProgress.proof)[s - 1] == -(long)'?') {
        print2(
        "?Step %ld is unknown.  You can only replace known steps.\n"
            , s);
        continue;
      }
      */

      /* 10/20/02  Set a flag that proof has unknown steps (for autoUnify()
         call below) */
      if (nmbrElementIn(1, proofInProgress.proof, -(long)'?')) {
        p = 1;
      } else {
        p = 0;
      }

      /* Check to see if the statement selected is allowed */
      if (!checkStmtMatch(stmt, step - 1)) {
        print2("?Statement \"%s\" cannot be unified with step %ld.\n",
          statement[stmt].labelName, step);
        continue;
      }

      /* 16-Sep-2012 nm */
      /* Check dummy variable status of step */
      /* For use in message later */
      dummyVarIsoFlag = checkDummyVarIsolation(step - 1);
            /* 0=no dummy vars, 1=isolated dummy vars, 2=not isolated*/

      /* Do the replacement */
      nmbrTmpPtr = replaceStatement(stmt /*statement#*/,
          step - 1 /*step*/,
          proveStatement,
          0,/*scan whole proof to maximize chance of a match*/
          0/*noDistinct*/,
          1/* try to prove $e's */,
          1/*improveDepth*/);
      if (!nmbrLen(nmbrTmpPtr)) {
        print2(
           "?Hypotheses of statement \"%s\" do not match known proof steps.\n",
            statement[stmt].labelName);
        continue;
      }

      /* Get the subproof at step s */
      q = subProofLen(proofInProgress.proof, step - 1);
      deleteSubProof(step - 1);
      addSubProof(nmbrTmpPtr, step - q);

      /* 10/20/02 Replaced "assignKnownSteps" with code from entry of PROVE
         command so REPLACE can be done in partial proofs */
      /*assignKnownSteps(s - q, nmbrLen(nmbrTmpPtr));*/  /* old code */
      /* Assign known subproofs */
      assignKnownSubProofs();
      /* Initialize remaining steps */
      i = nmbrLen(proofInProgress.proof);
      for (j = 0; j < i; j++) {
        if (!nmbrLen((proofInProgress.source)[j])) {
          initStep(j);
        }
      }
      /* Unify whatever can be unified */
      /* If proof wasn't complete before (p = 1), but is now, print congrats
         for user */
      autoUnify((char)p); /* 0 means no "congrats" message */
      /* end 10/20/02 */

      nmbrLet(&nmbrTmpPtr, NULL_NMBRSTRING); /* Deallocate memory */

      n = nmbrLen(proofInProgress.proof); /* New proof length */
      if (nmbrElementIn(1, proofInProgress.proof, -(long)'?')) {
        /* The proof is not complete, so print step numbers that changed */
        if (m == n) {
          print2("Step %ld was replaced with statement %s.\n",
            step, statement[stmt].labelName);
        } else {
          if (step != m) {
            printLongLine(cat("Step ", str(step),
                " was replaced with statement ", statement[stmt].labelName,
                ".  Steps ", str(step), ":",
                str(m), " are now ", str(step - m + n), ":", str(n), ".",
                NULL),
                "", " ");
          } else {
            printLongLine(cat("Step ", str(step),
                " was replaced with statement ", statement[stmt].labelName,
                ".  Step ", str(m), " is now step ", str(n), ".",
                NULL),
                "", " ");
          }
        }
      }
      /*autoUnify(1);*/

      /************ delete 19-Sep-2012 nm - not needed for REPLACE *******
      /@ Automatically interact with user if step not unified @/
      /@ ???We might want to add a setting to defeat this if user doesn't
         like it @/
      if (1 /@ ???Future setting flag @/) {
        interactiveUnifyStep(step - m + n - 1, 2); /@ 2nd arg. means print
                                         msg if already unified @/
      }
      *************** end 19-Sep-2012 deletion ********************/

      proofChangedFlag = 1; /* Flag to push 'undo' stack */
      proofChanged = 1; /* Cumulative flag */
      processUndoStack(&proofInProgress, PUS_PUSH, fullArgString, 0);

      /* 16-Sep-2012 nm */
      /*
      if (dummyVarIsoFlag == 2 && proofChangedFlag) {
        printLongLine(cat(
     "Assignments to shared working variables ($nn) are guesses.  If "
     "incorrect, to undo DELETE STEP ",
              str(step - m + n),
      ", INITIALIZE, UNIFY, then assign them manually with LET ",
      "and try REPLACE again.",
              NULL),
              "", " ");
      }
      */
      /* 25-Feb-2014 nm */
      if (dummyVarIsoFlag == 2 && proofChangedFlag) {
        printLongLine(cat(
     "Assignments to shared working variables ($nn) are guesses.  If "
     "incorrect, UNDO then assign them manually with LET ",
      "and try REPLACE again.",
              NULL),
              "", " ");
      }


      /* 14-Sep-2012 nm - Automatically display new unknown steps
         ???Future - add switch to enable/defeat this */
      if (proofChangedFlag)
        typeProof(proveStatement,
            1 /*pipFlag*/,
            0 /*startStep*/,
            0 /*endStep*/,
            0 /*endIndent*/,
            1 /*essentialFlag*/,
            0 /*renumberFlag*/,
            1 /*unknownFlag*/,
            0 /*notUnifiedFlag*/,
            0 /*reverseFlag*/,
            0 /*noIndentFlag*/,
            0 /*splitColumn*/,
            0 /*skipRepeatedSteps*/, /* 28-Jun-2013 nm */
            0 /*texFlag*/,
            0 /*htmlFlag*/);
      /* 14-Sep-2012 end */


      continue;

    } /* REPLACE */


    if (cmdMatches("IMPROVE")) {

      improveDepth = 0; /* Depth */
      i = switchPos("/ DEPTH");
      if (i) improveDepth = (long)val(fullArg[i + 1]);
      if (switchPos("/ NO_DISTINCT")) p = 1; else p = 0;
                        /* p = 1 means don't try to use statements with $d's */
      /* 22-Aug-2012 nm Added */
      searchAlg = 1; /* Default */
      if (switchPos("/ 1")) searchAlg = 1;
      if (switchPos("/ 2")) searchAlg = 2;
      if (switchPos("/ 3")) searchAlg = 3;
      /* 4-Sep-2012 nm Added */
      searchUnkSubproofs = 0;
      if (switchPos("/ SUBPROOFS")) searchUnkSubproofs = 1;

      /* 14-Sep-2012 nm */
      s = getStepNum(fullArg[1], proofInProgress.proof,
          1 /* ALL not allowed */);
      if (s == -1) continue;  /* Error; message was provided already */

      if (s != 0) {  /* s=0 means ALL */

      /**************** 14-Sep-2012 nm replaced with getStepNum()
      /@ 26-Aug-2006 nm Changed "IMPROVE STEP <step>" to "IMPROVE <step>" @/
      let(&str1, fullArg[1]); /@ To avoid void pointer problems with fullArg @/
      if (toupper((unsigned char)(str1[0])) != 'A') {
        /@ 16-Apr-06 nm - Handle nonpositive step number: 0 = last,
           -1 = penultimate, etc.@/
        offset = 0; /@ 16-Apr-06 @/
        /@ 10/4/99 - Added LAST - this means the last unknown step shown
           with SHOW NEW_PROOF/ESSENTIAL/UNKNOWN @/
        if (toupper((unsigned char)(str1[0])) == 'L'
            || toupper((unsigned char)(str1[0])) == 'F') {
                                        /@ "IMPROVE LAST" or "IMPROVE FIRST" @/
          s = 1; /@ Temporary until we figure out which step @/
          offset = 1;          /@ 16-Apr-06 @/
        } else {
          if (toupper((unsigned char)(str1[0])) == 'S') {
            print2(
           "?\"IMPROVE STEP <step>\" is obsolete.  Use \"IMPROVE <step>\".\n");
            continue;
          }
          s = (long)val(fullArg[1]); /@ Step number @/
          if (strcmp(fullArg[1], str(s))) {
            print2(
                "?Expected a number or FIRST or LAST or ALL after IMPROVE.\n");
            continue;
          }
          if (s <= 0) {         /@ 16-Apr-06 @/
            offset = - s + 1;   /@ 16-Apr-06 @/
            s = 1; /@ Temporary until we figure out step @/ /@ 16-Apr-06 @/
          }                     /@ 16-Apr-06 @/
        }
        /@ End of 26-Aug-2006 change @/

      /@ ------- Old code before 26-Aug-2006 -------
      if (cmdMatches("IMPROVE STEP") || cmdMatches("IMPROVE LAST") ||
          cmdMatches("IMPROVE FIRST")) {                     /# 11-Dec-05 nm #/

        /# 16-Apr-06 nm - Handle nonpositive step number: 0 = last,
           -1 = penultimate, etc.#/
        offset = 0; /# 16-Apr-06 #/
        /# 10/4/99 - Added LAST - this means the last unknown step shown
           with SHOW NEW_PROOF/ESSENTIAL/UNKNOWN #/
        if (cmdMatches("IMPROVE LAST") || cmdMatches("IMPROVE FIRST")) {
                               /# "IMPROVE LAST or FIRST" #/ /# 11-Dec-05 nm #/
          s = 1; /# Temporary until we figure out which step #/
          offset = 1;          /# 16-Apr-06 #/
        } else {
          s = val(fullArg[2]); /# Step number #/
          if (s <= 0) {         /# 16-Apr-06 #/
            offset = - s + 1;   /# 16-Apr-06 #/
            s = 1; /# Temp. until we figure out which step #/ /# 16-Apr-06 #/
          }                     /# 16-Apr-06 #/
        }
        ------- End of old code ------- @/
        **************** end of 14-Sep-2012 nm ************/

        m = nmbrLen(proofInProgress.proof); /* Original proof length */


        /**************** 14-Sep-2012 nm replaced with getStepNum()
        if (s > m || s < 1) {
          print2("?The step must be in the range from 1 to %ld.\n", m);
          continue;
        }


        /@ 10/4/99 - For IMPROVE FIRST/LAST command, figure out the last
           unknown essential step @/           /@ 11-Dec-05 nm - Added FIRST @/
        /@if (cmdMatches("IMPROVE LAST") || cmdMatches("IMPROVE FIRST")) {@/
                               /@ IMPROVE LAST or FIRST @/ /@ 11-Dec-05 nm @/
        if (offset > 0) {  /@ LAST, FIRST, or step <= 0 @/ /@ 16-Apr-06 @/
          /@ Get the essential step flags @/
          s = 0; /@ Use as flag that step was found @/
          nmbrLet(&essentialFlags, nmbrGetEssential(proofInProgress.proof));
          /@if (cmdMatches("IMPROVE LAST")) {@/
          /@if (!cmdMatches("IMPROVE FIRST")) {@/   /@ 16-Apr-06 @/
          if (toupper((unsigned char)(str1[0])) != 'F') { /@ 26-Aug-2006 @/
            /@ Scan proof backwards until last essential unknown step found @/
            /@ 16-Apr-06 - count back 'offset' unknown steps @/
            j = offset;      /@ 16-Apr-06 @/
            for (i = m; i >= 1; i--) {
              if (essentialFlags[i - 1]
                  && (proofInProgress.proof)[i - 1] == -(long)'?') {
                j--;           /@ 16-Apr-06 @/
                if (j == 0) {  /@ 16-Apr-06 @/
                  /@ Found it @/
                  s = i;
                  break;
                }              /@ 16-Apr-06 @/
              }
            } /@ Next i @/
          } else {
            /@ 11-Dec-05 nm Added IMPROVE FIRST @/
            /@ Scan proof forwards until first essential unknown step found @/
            for (i = 1; i <= m; i++) {
              if (essentialFlags[i - 1]
                  && (proofInProgress.proof)[i - 1] == -(long)'?') {
                /@ Found it @/
                s = i;
                break;
              }
            } /@ Next i @/
          }
          if (s == 0) {
            if (offset == 1) {                                /@ 16-Apr-06 @/
              print2("?There are no unknown essential steps.\n");
            } else {                                          /@ 16-Apr-06 @/
              print2("?There are not at least %ld unknown essential steps.\n",
                offset);                                      /@ 16-Apr-06 @/
            }                                                 /@ 16-Apr-06 @/
            continue;
          }
        } /@ if offset > 0 @/
        **************** end of 14-Sep-2012 nm ************/

        /* Get the subproof at step s */
        q = subProofLen(proofInProgress.proof, s - 1);
        nmbrLet(&nmbrTmp, nmbrSeg(proofInProgress.proof, s - q + 1, s));

        /*???Shouldn't this be just known?*/
        /* Check to see that the subproof has an unknown step. */
        if (!nmbrElementIn(1, nmbrTmp, -(long)'?')) {
          print2(
              "?Step %ld already has a proof and cannot be improved.\n",
              s);
          continue;
        }

        /* 25-Aug-2012 nm */
        /* Check dummy variable status of step */
        dummyVarIsoFlag = checkDummyVarIsolation(s - 1);
              /* 0=no dummy vars, 1=isolated dummy vars, 2=not isolated*/
        if (dummyVarIsoFlag == 2) {
          print2(
  "?Step %ld target has shared dummy variables and cannot be improved.\n", s);
          continue; /* Don't try to improve
                                 dummy variables that aren't isolated */
        }

        /********* Deleted old code 25-Aug-2012 nm
        /@ Check to see that the step has no dummy variables. @/
        j = 0; /@ Break flag @/
        for (i = 0; i < nmbrLen((proofInProgress.target)[s - 1]); i++) {
          if (((nmbrString @)((proofInProgress.target)[s - 1]))[i] > mathTokens) {
            j = 1;
            break;
          }
        }
        if (j) {
          print2(
   "?Step %ld target has dummy variables and cannot be improved.\n", s);
          continue;
        }
        ********/


        if (dummyVarIsoFlag == 0) { /* No dummy vars */ /* 25-Aug-2012 nm */
          /* Only use proveFloating if no dummy vars */
          nmbrTmpPtr = proveFloating((proofInProgress.target)[s - 1],
              proveStatement, improveDepth, s - 1, (char)p/*NO_DISTINCT*/);
        } else {
          nmbrTmpPtr = NULL_NMBRSTRING; /* Initialize */ /* 25-Aug-2012 nm */
        }
        if (!nmbrLen(nmbrTmpPtr)) {
          /* A proof for the step was not found with proveFloating(). */

          /* 22-Aug-2012 nm Next, try REPLACE algorithm */
          if (searchAlg == 2 || searchAlg == 3) {
            nmbrTmpPtr = proveByReplacement(proveStatement,
              s - 1/*prfStep*/, /* 0 means step 1 */
              (char)p/*NO_DISTINCT*/, /* 1 means don't try stmts with $d's */
              dummyVarIsoFlag,
              (char)(searchAlg - 2), /*0=proveFloat for $fs, 1=$e's also */
              improveDepth                         /* 4-Sep-2012 */
              );
          }
          if (!nmbrLen(nmbrTmpPtr)) {
            print2("A proof for step %ld was not found.\n", s);
            /* REPLACE algorithm also failed */
            continue;
          }
        }

        /* If q=1, subproof must be an unknown step, so don't bother to
           delete it */
        /*???Won't q always be 1 here?*/
        if (q > 1) deleteSubProof(s - 1);
        addSubProof(nmbrTmpPtr, s - q);
        assignKnownSteps(s - q, nmbrLen(nmbrTmpPtr));
        nmbrLet(&nmbrTmpPtr, NULL_NMBRSTRING);

        n = nmbrLen(proofInProgress.proof); /* New proof length */
        if (m == n) {
          print2("A 1-step proof was found for step %ld.\n", s);
        } else {
          if (s != m || q != 1) {
            printLongLine(cat("A ", str(n - m + 1),
                "-step proof was found for step ", str(s),
                ".  Steps ", str(s), ":",
                str(m), " are now ", str(s - q + 1 - m + n), ":", str(n), ".",
                NULL),
                "", " ");
          } else {
            printLongLine(cat("A ", str(n - m + 1),
                "-step proof was found for step ", str(s),
                ".  Step ", str(m), " is now step ", str(n), ".",
                NULL),
                "", " ");
          }
        }

        autoUnify(1); /* To get 'congrats' message if proof complete */
        proofChanged = 1; /* Cumulative flag */
        processUndoStack(&proofInProgress, PUS_PUSH, fullArgString, 0);

        /* End if s != 0 i.e. not IMPROVE ALL */   /* 14-Sep-2012 nm */
      } else {
        /* Here, getStepNum() returned 0, meaning ALL */  /* 14-Sep-2012 nm */

        /*if (cmdMatches("IMPROVE ALL")) {*/  /* obsolete */

        if (!nmbrElementIn(1, proofInProgress.proof, -(long)'?')) {
          print2("The proof is already complete.\n");
          continue;
        }

        n = 0; /* Earliest step that changed */

        proofChangedFlag = 0;

        for (improveAllIter = 1; improveAllIter <= 4; improveAllIter++) {
                                                           /* 25-Aug-2012 nm */
          if (improveAllIter == 1 && (searchAlg == 2 || searchAlg == 3))
            print2("Pass 1:  Trying to match cut-free statements...\n");
          if (improveAllIter == 2) {
            if (searchAlg == 2) {
              print2("Pass 2:  Trying to match all statements...\n");
            } else {
              print2(
"Pass 2:  Trying to match all statements, with cut-free hypothesis matches...\n"
                  );
            }
          }
          if (improveAllIter == 3 && searchUnkSubproofs)
            print2("Pass 3:  Trying to replace incomplete subproofs...\n");
          if (improveAllIter == 4) {
            if (searchUnkSubproofs) {
              print2("Pass 4:  Repeating pass 1...\n");
            } else {
              print2("Pass 3:  Repeating pass 1...\n");
            }
          }
          /* improveAllIter = 1: run proveFloating only */
          /* improveAllIter = 2: run proveByReplacement on unknown steps */
          /* improveAllIter = 3: run proveByReplacement on steps with
                                   incomplete subproofs */
          /* improveAllIter = 4: if something changed, run everything again */

          if (improveAllIter == 3 && !searchUnkSubproofs) continue;

          m = nmbrLen(proofInProgress.proof); /* Original proof length */

          for (s = m; s > 0; s--) {

            proofStepUnk = ((proofInProgress.proof)[s - 1] == -(long)'?')
                ? 1 : 0;   /* 25-Aug-2012 nm Added for clearer code */

            /* 22-Aug-2012 nm I think this is really too conservative, even
               with the old algorithm, but keep it to imitate the old one */
            if (improveAllIter == 1 || searchAlg == 1) { /* 22-Aug-2012 nm */
              /* If the step is known and unified, don't do it, since nothing
                 would be accomplished. */
              if (!proofStepUnk) {
                if (nmbrEq((proofInProgress.target)[s - 1],
                    (proofInProgress.source)[s - 1])) continue;
              }
            }

            /* Get the subproof at step s */
            q = subProofLen(proofInProgress.proof, s - 1);
            if (proofStepUnk && q != 1) {
              bug(1120); /* 25-Aug-2012 nm Consistency check */
            }
            nmbrLet(&nmbrTmp, nmbrSeg(proofInProgress.proof, s - q + 1, s));

            /* Improve only subproofs with unknown steps */
            if (!nmbrElementIn(1, nmbrTmp, -(long)'?')) continue;

            nmbrLet(&nmbrTmp, NULL_NMBRSTRING); /* No longer needed - dealloc */

            /* 25-Aug-2012 nm */
            /* Check dummy variable status of step */
            dummyVarIsoFlag = checkDummyVarIsolation(s - 1);
                  /* 0=no dummy vars, 1=isolated dummy vars, 2=not isolated*/
            if (dummyVarIsoFlag == 2) continue; /* Don't try to improve
                                     dummy variables that aren't isolated */

            /********* Deleted old code now done by checkDummyVarIsolation()
                       25-Aug-2012 nm
            /@ Check to see that the step has no dummy variables. @/
            j = 0; /@ Break flag @/
            for (i = 0; i < nmbrLen((proofInProgress.target)[s - 1]); i++) {
              if (((nmbrString @)((proofInProgress.target)[s - 1]))[i] >
                  mathTokens) {
                j = 1;
                break;
              }
            }
            if (j) {
              /@ Step has dummy variables and cannot be improved. @/
              continue;
            }
            ********/

            if (dummyVarIsoFlag == 0
                && (improveAllIter == 1
                  || improveAllIter == 4)) {
                /* No dummy vars */ /* 25-Aug-2012 nm */
              /* Only use proveFloating if no dummy vars */
              nmbrTmpPtr = proveFloating((proofInProgress.target)[s - 1],
                  proveStatement, improveDepth, s - 1, (char)p/*NO_DISTINCT*/);
            } else {
              nmbrTmpPtr = NULL_NMBRSTRING; /* Init */ /* 25-Aug-2012 nm */
            }
            if (!nmbrLen(nmbrTmpPtr)) {
              /* A proof for the step was not found with proveFloating(). */

              /* 22-Aug-2012 nm Next, try REPLACE algorithm */
              if ((searchAlg == 2 || searchAlg == 3)
                  && ((improveAllIter == 2 && proofStepUnk)
                    || (improveAllIter == 3 && !proofStepUnk)
                    /*|| improveAllIter == 4*/)) {
                nmbrTmpPtr = proveByReplacement(proveStatement,
                  s - 1/*prfStep*/, /* 0 means step 1 */
                  (char)p/*NO_DISTINCT*/, /* 1 means don't try stmts w/ $d's */
                  dummyVarIsoFlag,
                  (char)(searchAlg - 2),/*searchMethod: 0 or 1*/
                  improveDepth                         /* 4-Sep-2012 */
                  );

              }
              if (!nmbrLen(nmbrTmpPtr)) {
                /* REPLACE algorithm also failed */
                continue;
              }
            }

            /* If q=1, subproof must be an unknown step, so don't bother to
               delete it */
            if (q > 1) deleteSubProof(s - 1);
            addSubProof(nmbrTmpPtr, s - q);
            assignKnownSteps(s - q, nmbrLen(nmbrTmpPtr));
            print2("A proof of length %ld was found for step %ld.\n",
                nmbrLen(nmbrTmpPtr), s);
            if (nmbrLen(nmbrTmpPtr) || q != 1) n = s - q + 1;
                                               /* Save earliest step changed */
            nmbrLet(&nmbrTmpPtr, NULL_NMBRSTRING);
            proofChangedFlag = 1;
            s = s - q + 1; /* Adjust step position to account for deleted subpr */
          } /* Next step s */

          if (proofChangedFlag) {
            autoUnify(0); /* 0 = No 'Congrats' if done */
          }

          if (!proofChangedFlag
              && ( (improveAllIter == 2 && !searchUnkSubproofs)
                 || improveAllIter == 3
                 || searchAlg == 1)) {
            print2("No new subproofs were found.\n");
            break; /* out of improveAllIter loop */
          }
          if (proofChangedFlag) {
            proofChanged = 1; /* Cumulative flag */
          }

          if (!nmbrElementIn(1, proofInProgress.proof, -(long)'?')) {
            break; /* Proof is complete */
          }

          if (searchAlg == 1) break; /* Old algorithm does just 1st pass */

        } /* Next improveAllIter */

        if (proofChangedFlag) {
          if (n > 0) {
            /* n is the first step number changed.  It will be 0 if
               the numbering didn't change e.g. a $e was assigned to
               an unknown step. */
            print2("Steps %ld and above have been renumbered.\n", n);
          }
          processUndoStack(&proofInProgress, PUS_PUSH, fullArgString, 0);
        }
        if (!nmbrElementIn(1, proofInProgress.proof, -(long)'?')) {
          /* This is a redundant call; its purpose is just to give
             the message if the proof is complete */
          autoUnify(1); /* 1 = 'Congrats' if done */
        }

      } /* End if IMPROVE ALL */

      /* 6/14/98 - Automatically display new unknown steps
         ???Future - add switch to enable/defeat this */
      if (proofChangedFlag)
        typeProof(proveStatement,
            1 /*pipFlag*/,
            0 /*startStep*/,
            0 /*endStep*/,
            0 /*endIndent*/,
            1 /*essentialFlag*/,
            0 /*renumberFlag*/,
            1 /*unknownFlag*/,
            0 /*notUnifiedFlag*/,
            0 /*reverseFlag*/,
            0 /*noIndentFlag*/,
            0 /*splitColumn*/,
            0 /*skipRepeatedSteps*/, /* 28-Jun-2013 nm */
            0 /*texFlag*/,
            0 /*htmlFlag*/);
      /* 6/14/98 end */

      continue;

    }




    if (cmdMatches("MINIMIZE_WITH")) {
      /* q = 0; */ /* Line length */  /* 25-Jun-2014 deleted */
      prntStatus = 0; /* Status flag to help determine messages
                         0 = no statement was matched during scan
                         1 = a statement was matched but no shorter proof
                         2 = shorter proof found */
      /* verboseMode = (switchPos("/ BRIEF") == 0); */ /* Non-verbose mode */
      /* 4-Feb-2013 nm VERBOSE is now default */
      verboseMode = (switchPos("/ VERBOSE") != 0); /* Verbose mode */
      /* 30-Jan-06 nm Added single-character-match wildcard argument */
      if (!(instr(1, fullArg[1], "*") || instr(1, fullArg[1], "?"))) i = 1;
          /* 16-Feb-05 If no wildcard was used, switch to non-verbose mode
             since there is no point to it and an annoying extra blank line
             results */
      allowGrowthFlag = (switchPos("/ ALLOW_GROWTH") != 0);
                  /* Mode to replace even if it doesn't reduce proof length */
      /* 25-Jun-2014 nm /NO_DISTINCT is obsolete
      noDistinctFlag = (switchPos("/ NO_DISTINCT") != 0);
      */
                                          /* Skip trying statements with $d */
      exceptPos = switchPos("/ EXCEPT"); /* Statement match to skip */
                                                               /* 7-Jan-06 */

      /* 20-May-2013 nm */
      forbidMatchPos = switchPos("/ FORBID");
      if (forbidMatchPos != 0) {
        let(&forbidMatchList, fullArg[forbidMatchPos + 1]);
      } else {
        let(&forbidMatchList, "");
      }

      /* 20-May-2013 nm */
      noNewAxiomsMatchPos = switchPos("/ NO_NEW_AXIOMS_FROM");
      if (noNewAxiomsMatchPos != 0) {
        let(&noNewAxiomsMatchList, fullArg[noNewAxiomsMatchPos + 1]);
      } else {
        let(&noNewAxiomsMatchList, "");
      }

      mathboxFlag = (switchPos("/ INCLUDE_MATHBOXES") != 0); /* 28-Jun-2011 */
      /* 25-Jun-2014 nm /REVERSE is obsolete
      forwFlag = (switchPos("/ REVERSE") != 0); /@ 10-Nov-2011 nm @/
      */
      if (sandboxStmt == 0) { /* Look up "mathbox" label if it hasn't been */
        sandboxStmt = lookupLabel("mathbox");
        if (sandboxStmt == -1)
          sandboxStmt = statements + 1;  /* Default beyond db end if none */
      }


      /* 25-Jun-2014 nm */
      /* If a single statement is specified, don't bother to do certain
         actions or print some of the messages */
      hasWildCard = (instr(1, fullArg[1], "*")
                || instr(1, fullArg[1], "?")
                || instr(1, fullArg[1], ","));

      proofChangedFlag = 0;

      /* Added 14-Aug-2012 nm */
      /* Always scan statements in current mathbox, even if
         "/ INCLUDE_MATHBOXES" is omitted */
      thisMathboxStmt = sandboxStmt;
                /* Will become start of current (proveStatement's) mathbox */
      if (proveStatement > sandboxStmt) {
        /* We're in a mathbox */
        for (k = proveStatement; k >= sandboxStmt; k--) {
          let(&str1, left(statement[k].labelSectionPtr,
              statement[k].labelSectionLen));
          /* Heuristic to match beginning of mathbox */
          if (instr(1, str1, "Mathbox for") != 0) {
             /* Found beginning of current mathbox */
             thisMathboxStmt = k;
             break;
          }
        }
      }

      /* 25-Jun-2014 nm */
      copyProofStruct(&saveOrigProof, proofInProgress);

      /* 25-Jun-2014 nm Get the current (original) compressed proof length
         to compare it when a shorter non-compressed proof is found, to see
         if the compressed proof also decreased in size */
      nmbrLet(&nmbrSaveProof, proofInProgress.proof);
      nmbrLet(&nmbrSaveProof, nmbrSquishProof(proofInProgress.proof));
      /* We only care about length; str1 will be discarded */
      let(&str1, compressProof(nmbrSaveProof,
          proveStatement, /* statement being proved */
          0 /* Normal (not "fast") compression */
          ));
      origCompressedLength = (long)strlen(str1);
      print2(
"Bytes refer to compressed proof size, steps to uncompressed length.\n");

      /* 25-Jun-2014 nm forwRevPass outer loop added */
      /* Scan forward, then reverse, then pick best result */
      for (forwRevPass = 1; forwRevPass <= 2; forwRevPass++) {

        /* 25-Jun-2014 nm */
        if (forwRevPass == 1) {
          if (hasWildCard) print2("Scanning forward through statements...\n");
          forwFlag = 1;
        } else {
          /* If growth allowed, don't bother with reverse pass */
          if (allowGrowthFlag) break;
          /* If nothing was found on forward pass, don't bother with rev pass */
          if (!proofChangedFlag) break;
          /* If only one statement was specified, don't bother with rev pass */
          if (!hasWildCard) break;
          print2("Scanning backward through statements...\n");
          forwFlag = 0;
          /* Save proof and length from 1st pass; re-initialize */
          copyProofStruct(&save1stPassProof, proofInProgress);
          forwardLength = nmbrLen(proofInProgress.proof);
          forwardCompressedLength = oldCompressedLength;
          /* Start over from original proof */
          copyProofStruct(&proofInProgress, saveOrigProof);
        }

        /* 20-May-2013 nm */
        /*
        if (forbidMatchList[0]) { /@ User provided a /FORBID list @/
          /@ Save the proof structure in case we have to revert a
             forbidden match. @/
          copyProofStruct(&saveProofForReverting, proofInProgress);
        }
        */
        /* 25-Jun-2014 nm */
        copyProofStruct(&saveProofForReverting, proofInProgress);

        oldCompressedLength = origCompressedLength;

        /* for (k = 1; k < proveStatement; k++) { */
        /* 10-Nov-2011 nm */
        /* We use bottom-up scanning as the default (forwFlag=0) since empirically
           it seems to lead to shorter proofs */
        /* If forwFlag is 0, scan from proveStatement-1 to 1
           If forwFlag is 1, scan from 1 to proveStatement-1 */
        for (k = forwFlag ? 1 : (proveStatement - 1);
             k * (forwFlag ? 1 : -1) < (forwFlag ? proveStatement : 0);
             k = k + (forwFlag ? 1 : -1)) {
          /* 28-Jun-2011 */
          /* Scan mathbox statements only if INCLUDE_MATHBOXES specified */
          /*if (!mathboxFlag && k >= sandboxStmt) continue;*/
          /* 14-Aug-2012 nm */
          if (!mathboxFlag && k >= sandboxStmt && k < thisMathboxStmt) continue;

          if (statement[k].type != (char)p_ && statement[k].type != (char)a_)
            continue;
          /* 30-Jan-06 nm Added single-character-match wildcard argument */
          if (!matchesList(statement[k].labelName, fullArg[1], '*', '?'))
            continue;
          /* 25-Jun-2014 nm /NO_DISTINCT is obsolete
          if (noDistinctFlag) {
            /@ Skip the statement if it has a $d requirement.  This option
               prevents illegal minimizations that would violate $d requirements
               since MINIMIZE_WITH does not check for $d violations. @/
            if (nmbrLen(statement[k].reqDisjVarsA)) {
              /@ 30-Jan-06 nm Added single-character-match wildcard argument @/
              if (!(instr(1, fullArg[1], "@") || instr(1, fullArg[1], "?")))
                print2("?\"%s\" has a $d requirement\n", fullArg[1]);
              continue;
            }
          }
          */

          /* 7-Jan-06 nm - Added EXCEPT switch */
          if (exceptPos != 0) {
            /* Skip any match to the EXCEPT argument */
            /* 30-Jan-06 nm Added single-character-match wildcard argument */
            if (matchesList(statement[k].labelName, fullArg[exceptPos + 1],
                '*', '?'))
              continue;
          }

          /* 20-May-2013 nm */
          if (forbidMatchList[0]) { /* User provided a /FORBID list */
            /* First, we check to make sure we're not trying a statement
               in the forbidMatchList directly (traceProof() won't find
               this) */
            if (matchesList(statement[k].labelName, forbidMatchList, '*', '?'))
              continue;
          }

          /* Print individual labels */
          if (prntStatus == 0) prntStatus = 1; /* Matched at least one */
          /* 25-Jun-2014 nm Don't list matched statements anymore
          if (verboseMode) {
            q = q + (long)strlen(statement[k].labelName) + 1;
            if (q > 72) {
              q = (long)strlen(statement[k].labelName) + 1;
              print2("\n");
            }
            print2("%s ",statement[k].labelName);
          }
          */

          m = nmbrLen(proofInProgress.proof); /* Original proof length */
          nmbrLet(&nmbrTmp, proofInProgress.proof);
          minimizeProof(k /* trial statement */,
              proveStatement /* statement being proved in MM-PA */,
              (char)allowGrowthFlag /* allowGrowthFlag */);

          n = nmbrLen(proofInProgress.proof); /* New proof length */
          if (!nmbrEq(nmbrTmp, proofInProgress.proof)) {
            /* The proof got shorter (or it changed if ALLOW_GROWTH) */

            /* 20-May-2013 nm Because of the slow speed of traceBack(),
               we only want to check the /FORBID list in the relatively
               rare case where a minimization occurred.  If the FORBID
               list is matched, we then need to revert back to the
               original proof. */
            if (forbidMatchList[0]) { /* User provided a /FORBID list */
              if (statement[k].type == (char)p_) {
                /* We only care about tracing $p statements */
                /* See if the TRACE_BACK list includes a match to the
                   /FORBID argument */
                if (traceProof(k,
                    0, /*essentialFlag*/
                    0, /*axiomFlag*/
                    forbidMatchList,
                    "", /*traceToList*/ /* 18-Jul-2015 nm */
                    1 /* testOnlyFlag */)) {
                  /* Yes, a forbidden statement occurred in traceProof() */
                  /* Revert the proof to before minimization */
                  copyProofStruct(&proofInProgress, saveProofForReverting);
                  /* Skip further printout and flag setting */
                  continue; /* Continue at 'Next k' loop end below */
                }
              }
            }


            /* 22-Nov-2014 nm Because of the slow speed of traceBack(),
               we only want to check the /NO_NEW_AXIOMS_FROM list in the
               relatively rare case where a minimization occurred.  If the
               NO_NEW_AXIOMS_FROM condition applies, we then need to revert
               back to the original proof. */
            if (noNewAxiomsMatchList[0]) { /* User provided /NO_NEW_AXIOMS_FROM */
              /* If we haven't called trace yet for the theorem being proved,
                 do it now. */
              if (traceProofFlags[0] == 0) {
                traceProofWork(proveStatement,
                    1 /*essentialFlag*/,
                    "", /*traceToList*/ /* 18-Jul-2015 nm */
                    &traceProofFlags, /* y/n list of flags */
                    &nmbrTmp /* unproved list - ignored */);
                nmbrLet(&nmbrTmp, NULL_NMBRSTRING); /* Discard */
              }
              let(&traceTrialFlags, "");
              traceProofWork(k,
                  1 /*essentialFlag*/,
                  "", /*traceToList*/ /* 18-Jul-2015 nm */
                  &traceTrialFlags, /* Y/N list of flags */
                  &nmbrTmp /* unproved list - ignored */);
              nmbrLet(&nmbrTmp, NULL_NMBRSTRING); /* Discard */
              j = 1; /* 1 = ok to use trial statement */
              for (i = 1; i < proveStatement; i++) {
                if (statement[i].type != (char)a_) continue; /* Not $a */
                if (traceProofFlags[i] == 'Y') continue;
                         /* If the axiom is already used by the proof, we
                            don't care if the trial statement depends on it */
                if (matchesList(statement[i].labelName, noNewAxiomsMatchList,
                    '*', '?') != 1) {
                  /* If the axiom isn't in the list to avoid, we don't
                     care if the trial statement depends on it */
                  continue;
                }
                if (traceTrialFlags[i] == 'Y') {
                  /* The trial statement uses an axiom that the current
                     proof should avoid, so we abort it */
                  j = 0; /* 0 = don't use trial statement */
                  break;
                }
              }
              if (j == 0) {
                /* A forbidden axiom is used by the trial proof */
                /* Revert the proof to before minimization */
                copyProofStruct(&proofInProgress, saveProofForReverting);
                /* Skip further printout and flag setting */
                continue; /* Continue at 'Next k' loop end below */
              }
            } /* end if noNewAxiomsMatchList[0] */


            /* 25-Jun-2014 nm Make sure the compressed proof length
               decreased, otherwise revert.  Also, we will use the
               compressed proof for the $d check next */
            if (nmbrLen(statement[k].reqDisjVarsA) || !allowGrowthFlag) {
              nmbrLet(&nmbrSaveProof, proofInProgress.proof);
              nmbrLet(&nmbrSaveProof, nmbrSquishProof(proofInProgress.proof));
              let(&str1, compressProof(nmbrSaveProof,
                  proveStatement, /* statement being proved in MM-PA */
                  0 /* Normal (not "fast") compression */
                  ));
              newCompressedLength = (long)strlen(str1);
              if (!allowGrowthFlag && newCompressedLength > oldCompressedLength) {
                /* The compressed proof length increased, so don't use it.
                   (If it stayed the same, we will use it because the uncompressed
                   length did decrease.) */
                /* Revert the proof to before minimization */
                if (verboseMode) {
                  print2(
 "Reverting \"%s\": Uncompressed steps:  old = %ld, new = %ld\n",
                      statement[k].labelName,
                      m, n );
                  print2(
 "    but compressed size:  old = %ld bytes, new = %ld bytes\n",
                      oldCompressedLength, newCompressedLength);
                }
                copyProofStruct(&proofInProgress, saveProofForReverting);
                /* Skip further printout and flag setting */
                continue; /* Continue at 'Next k' loop end below */
              }
            } /* if (nmbrLen(statement[k].reqDisjVarsA) || !allowGrowthFlag) */

            /* 25-Jun-2014 nm */
            /* Make sure there are no $d violations, otherwise revert */
            /* This requires the str1 from above */
            if (nmbrLen(statement[k].reqDisjVarsA)) {
              /* There is currently no way to verify a proof that doesn't
                 read and parse the source directly.  This should be
                 changed in the future to make the program more modular.  But
                 for now, we temporarily zap the source with new compressed
                 proof and see if there are any $d violations by looking at
                 the error message output */
              saveZappedProofSectionPtr
                  = statement[proveStatement].proofSectionPtr;
              saveZappedProofSectionLen
                  = statement[proveStatement].proofSectionLen;
              /* (search for "chr(1)" above for explanation) */
              let(&str1, cat(chr(1), "\n", str1, " $.\n", NULL));
              statement[proveStatement].proofSectionPtr = str1 + 1; /* Compressed
                                                     proof generated above */
              statement[proveStatement].proofSectionLen =
                  newCompressedLength + 4;
              outputToString = 1; /* Suppress error messages */
              /* i = parseProof(proveStatement); */
              /* if (i != 0) bug(1121); */
              /* i = verifyProof(proveStatement); */
              /* if (i != 0) bug(1122); */
              /* 15-Apr-2015 nm parseProof, verifyProof, cleanWkrProof must be
                 called in sequence to assign the wrkProof structure, verify
                 the proof, and deallocate the wrkProof structure.  Either none
                 of them or all of them must be called. */
              parseProof(proveStatement);
              verifyProof(proveStatement); /* Must be called even if error
                                  occurred in parseProof, to init RPN stack */
              /* 15-Apr-2015 nm - don't change proof if there is an error
                 (which could be pre-existing). */
              i = (wrkProof.errorSeverity > 1);
              /**** Here we look at the screen output sent to a string.
                    This is rather crude, and someday the ability to
                    check proofs and $d violations should be modularized *****/
              j = instr(1, printString,
                  "There is a disjoint variable ($d) violation");
              outputToString = 0; /* Restore to normal output */
              let(&printString, ""); /* Clear out the stored error messages */
              cleanWrkProof(); /* Deallocate verifyProof storage */
              statement[proveStatement].proofSectionPtr
                  = saveZappedProofSectionPtr;
              statement[proveStatement].proofSectionLen
                  = saveZappedProofSectionLen;
              if (i != 0 || j != 0) {
                /* There was a verify proof error (j!=0) or $d violation (i!=0)
                   so don't used minimized proof */
                /* Revert the proof to before minimization */
                copyProofStruct(&proofInProgress, saveProofForReverting);
                /* Skip further printout and flag setting */
                continue; /* Continue at 'Next k' loop end below */
              }
            } /* if (nmbrLen(statement[k].reqDisjVarsA)) */

            /* 25-Jun-2014 nm - not needed since trials now suppressed */
            /*
            if (verboseMode) {
              print2("\n");
            }
            */

            /*if (nmbrLen(statement[k].reqDisjVarsA) || !allowGrowthFlag) {*/
            if (!allowGrowthFlag) {
              /* Note:  this is the length BEFORE indentation and wrapping,
                 so it is less than SHOW PROOF ... /SIZE */
              if (newCompressedLength < oldCompressedLength) {
                print2(
     "Proof of \"%s\" decreased from %ld to %ld bytes using \"%s\".\n",
                    statement[proveStatement].labelName,
                    oldCompressedLength, newCompressedLength,
                    statement[k].labelName);
              } else {
                if (newCompressedLength > oldCompressedLength) bug(1123);
                print2(
     "Proof of \"%s\" stayed at %ld bytes using \"%s\".\n",
                    statement[proveStatement].labelName,
                    oldCompressedLength,
                    statement[k].labelName);
                print2(
    "    (Uncompressed steps decreased from %ld to %ld).\n",
                    m, n );
              }
              /* (We don't care about compressed length if ALLOW_GROWTH) */
              oldCompressedLength = newCompressedLength;
            }

            if (n < m && (allowGrowthFlag || verboseMode)) {
              print2(
      "%sProof of \"%s\" decreased from %ld to %ld steps using \"%s\".\n",
                (allowGrowthFlag ? "" : "    "),
                statement[proveStatement].labelName,
                m, n, statement[k].labelName);
            }
            /* ALLOW_GROWTH possibility */
            if (m < n) print2(
      "Proof of \"%s\" increased from %ld to %ld steps using \"%s\".\n",
                statement[proveStatement].labelName,
                m, n, statement[k].labelName);
            /* ALLOW_GROWTH possibility */
            if (m == n) print2(
                "Proof of \"%s\" remained at %ld steps using \"%s\".\n",
                statement[proveStatement].labelName,
                m, statement[k].labelName);
            /* Distinct variable warning (obsolete) */
            /*
            if (nmbrLen(statement[k].reqDisjVarsA)) {
              printLongLine(cat("Note: \"", statement[k].labelName,
                  "\" has $d constraints.",
                  "  SAVE NEW_PROOF then VERIFY PROOF to check them.",
                  NULL), "", " ");
            }
            */
            /* q = 0; */ /* Line length for label list */ /* 25-Jun-2014 del */
            prntStatus = 2; /* Found one */
            proofChangedFlag = 1;

            /* 20-May-2012 nm */
            /*
            if (forbidMatchList[0]) { /@ User provided a /FORBID list @/
              /@ Save the changed proof in case we have to restore
                 it later @/
              copyProofStruct(&saveProofForReverting, proofInProgress);
            }
            */
            /* 25-Jun-2014 nm */
            /* Save the changed proof in case we have to restore
               it later */
            copyProofStruct(&saveProofForReverting, proofInProgress);

          }

        } /* Next k (statement) */
        /* 25-Jun-2014 nm - not needed since trials now suppressed */
        /*
        if (verboseMode) {
          if (prntStatus) print2("\n");
        }
        */

        if (proofChangedFlag && forwRevPass == 2) {
          /* 25-Jun-2014 nm */
          /* Check whether the reverse pass found a better proof than the
             forward pass */
          if (verboseMode) {
            print2(
"Forward vs. backward: %ld vs. %ld bytes; %ld vs. %ld steps\n",
                      forwardCompressedLength,
                      oldCompressedLength,
                      forwardLength,
                      nmbrLen(proofInProgress.proof));
          }
          if (oldCompressedLength < forwardCompressedLength
               || (oldCompressedLength == forwardCompressedLength &&
                   nmbrLen(proofInProgress.proof) < forwardLength)) {
            /* The reverse pass was better */
            print2("The backward scan results were used.\n");
          } else {
            copyProofStruct(&proofInProgress, save1stPassProof);
            print2("The forward scan results were used.\n");
          }
        }

      } /* next forwRevPass */

      if (prntStatus == 1 && !allowGrowthFlag)
        print2("No shorter proof was found.\n");
      if (prntStatus == 1 && allowGrowthFlag)
        print2("The proof was not changed.\n");
      if (!prntStatus /* && !noDistinctFlag */)
        print2("?No earlier $p or $a label matches \"%s\".\n", fullArg[1]);
      /* 25-Jun-2014 nm /NO_DISTINCT is obsolete
      if (!prntStatus && noDistinctFlag) {
        /@ 30-Jan-06 nm Added single-character-match wildcard argument @/
        if (instr(1, fullArg[1], "@") || instr(1, fullArg[1], "?"))
          print2("?No earlier $p or $a label (without $d) matches \"%s\".\n",
              fullArg[1]);
      }
      */
      /* 28-Jun-2011 nm */
      if (!mathboxFlag && proveStatement >= sandboxStmt) {
        print2(
  "(Other mathboxes were not checked.  Use / INCLUDE_MATHBOXES to include them.)\n");
      }

      /* 20-May-2013 nm */
      if (forbidMatchList[0]) { /* User provided a /FORBID list */
        /*deallocProofStruct(&saveProofForReverting);*/ /* Deallocate memory */
        let(&forbidMatchList, ""); /* Deallocate memory */
      }

      /* 22-Nov-2014 nm */
      if (noNewAxiomsMatchList[0]) { /* User provided /NO_NEW_AXIOMS_FROM list */
        let(&noNewAxiomsMatchList, ""); /* Deallocate memory */
        let(&traceProofFlags, ""); /* Deallocate memory */
        let(&traceTrialFlags, ""); /* Deallocate memory */
      }

      /* 25-Jun-2014 nm */
      deallocProofStruct(&saveProofForReverting); /* Deallocate memory */
      deallocProofStruct(&saveOrigProof); /* Deallocate memory */
      deallocProofStruct(&save1stPassProof); /* Deallocate memory */

      if (proofChangedFlag) {
        proofChanged = 1; /* Cumulative flag */
        processUndoStack(&proofInProgress, PUS_PUSH, fullArgString, 0);
      }
      continue;

    } /* End if MINIMIZE_WITH */



    if (cmdMatches("DELETE STEP") || (cmdMatches("DELETE ALL"))) {

      if (cmdMatches("DELETE STEP")) {
        s = (long)val(fullArg[2]); /* Step number */
      } else {
        s = nmbrLen(proofInProgress.proof);
      }
      if ((proofInProgress.proof)[s - 1] == -(long)'?') {
        print2("?Step %ld is unknown and cannot be deleted.\n", s);
        continue;
      }
      m = nmbrLen(proofInProgress.proof); /* Original proof length */
      if (s > m || s < 1) {
        print2("?The step must be in the range from 1 to %ld.\n", m);
        continue;
      }

      deleteSubProof(s - 1);
      n = nmbrLen(proofInProgress.proof); /* New proof length */
      if (m == n) {
        print2("Step %ld was deleted.\n", s);
      } else {
        if (n > 1) {
          printLongLine(cat("A ", str(m - n + 1),
              "-step subproof at step ", str(s),
              " was deleted.  Steps ", str(s), ":",
              str(m), " are now ", str(s - m + n), ":", str(n), ".",
              NULL),
              "", " ");
        } else {
          print2("The entire proof was deleted.\n");
        }
      }

      /* 6/14/98 - Automatically display new unknown steps
         ???Future - add switch to enable/defeat this */
      typeProof(proveStatement,
          1 /*pipFlag*/,
          0 /*startStep*/,
          0 /*endStep*/,
          0 /*endIndent*/,
          1 /*essentialFlag*/,
          0 /*renumberFlag*/,
          1 /*unknownFlag*/,
          0 /*notUnifiedFlag*/,
          0 /*reverseFlag*/,
          0 /*noIndentFlag*/,
          0 /*splitColumn*/,
          0 /*skipRepeatedSteps*/, /* 28-Jun-2013 nm */
          0 /*texFlag*/,
          0 /*htmlFlag*/);
      /* 6/14/98 end */

      proofChanged = 1; /* Cumulative flag */
      processUndoStack(&proofInProgress, PUS_PUSH, fullArgString, 0);

      continue;

    }

    if (cmdMatches("DELETE FLOATING_HYPOTHESES")) {

      /* Get the essential step flags */
      nmbrLet(&nmbrTmp, nmbrGetEssential(proofInProgress.proof));

      m = nmbrLen(proofInProgress.proof); /* Original proof length */

      n = 0; /* Earliest step that changed */
      proofChangedFlag = 0;

      for (s = m; s > 0; s--) {

        /* Skip essential steps and unknown steps */
        if (nmbrTmp[s - 1] == 1) continue; /* Not floating */
        if ((proofInProgress.proof)[s - 1] == -(long)'?') continue; /* Unknown */

        /* Get the subproof length at step s */
        q = subProofLen(proofInProgress.proof, s - 1);

        deleteSubProof(s - 1);

        n = s - q + 1; /* Save earliest step changed */
        proofChangedFlag = 1;
        s = s - q + 1; /* Adjust step position to account for deleted subpr */
      } /* Next step s */

      if (proofChangedFlag) {
        print2("All floating-hypothesis steps were deleted.\n");

        if (n) {
          print2("Steps %ld and above have been renumbered.\n", n);
        }

        /* 6/14/98 - Automatically display new unknown steps
           ???Future - add switch to enable/defeat this */
        typeProof(proveStatement,
            1 /*pipFlag*/,
            0 /*startStep*/,
            0 /*endStep*/,
            0 /*endIndent*/,
            1 /*essentialFlag*/,
            0 /*renumberFlag*/,
            1 /*unknownFlag*/,
            0 /*notUnifiedFlag*/,
            0 /*reverseFlag*/,
            0 /*noIndentFlag*/,
            0 /*splitColumn*/,
            0 /*skipRepeatedSteps*/, /* 28-Jun-2013 nm */
            0 /*texFlag*/,
            0 /*htmlFlag*/);
        /* 6/14/98 end */

        proofChanged = 1; /* Cumulative flag */
        processUndoStack(&proofInProgress, PUS_PUSH, fullArgString, 0);
      } else {
        print2("?There are no floating-hypothesis steps to delete.\n");
      }

      continue;

    } /* End if DELETE FLOATING_HYPOTHESES */

    if (cmdMatches("INITIALIZE")) {

      if (cmdMatches("INITIALIZE ALL")) {
        i = nmbrLen(proofInProgress.proof);

        /* Reset the dummy variable counter (all will be refreshed) */
        pipDummyVars = 0;

        /* Initialize all steps */
        for (j = 0; j < i; j++) {
          initStep(j);
        }

        /* Assign known subproofs */
        assignKnownSubProofs();

        print2("All steps have been initialized.\n");
        proofChanged = 1; /* Cumulative flag */
        processUndoStack(&proofInProgress, PUS_PUSH, fullArgString, 0);
        continue;
      }

        /* Added 16-Apr-06 nm */
      if (cmdMatches("INITIALIZE USER")) {
        i = nmbrLen(proofInProgress.proof);
        /* Delete all LET STEP assignments */
        for (j = 0; j < i; j++) {
          nmbrLet((nmbrString **)(&((proofInProgress.user)[j])),
              NULL_NMBRSTRING);
        }
        print2(
      "All LET STEP user assignments have been initialized (i.e. deleted).\n");
        proofChanged = 1; /* Cumulative flag */
        processUndoStack(&proofInProgress, PUS_PUSH, fullArgString, 0);
        continue;
      }
      /* End 16-Apr-06 */

      /* cmdMatches("INITIALIZE STEP") */
      s = (long)val(fullArg[2]); /* Step number */
      if (s > nmbrLen(proofInProgress.proof) || s < 1) {
        print2("?The step must be in the range from 1 to %ld.\n",
            nmbrLen(proofInProgress.proof));
        continue;
      }

      initStep(s - 1);

      /* Also delete LET STEPs, per HELP INITIALIZE */          /* 16-Apr-06 */
      nmbrLet((nmbrString **)(&((proofInProgress.user)[s - 1])),  /* 16-Apr-06 */
              NULL_NMBRSTRING);                                 /* 16-Apr-06 */

      print2(
          "Step %ld and its hypotheses have been initialized.\n",
          s);

      proofChanged = 1; /* Cumulative flag */
      processUndoStack(&proofInProgress, PUS_PUSH, fullArgString, 0);
      continue;

    }


    if (cmdMatches("SEARCH")) {
      if (switchPos("/ ALL")) {
        m = 1;  /* Include $e, $f statements */
      } else {
        m = 0;  /* Show $a, $p only */
      }

      /* 14-Apr-2008 nm added */
      if (switchPos("/ JOIN")) {
        joinFlag = 1;  /* Join $e's to $a,$p for matching */
      } else {
        joinFlag = 0;  /* Search $a,$p by themselves */
      }

      if (switchPos("/ COMMENTS")) {
        n = 1;  /* Search comments */
      } else {
        n = 0;  /* Search statement math symbols */
      }

      let(&str1, fullArg[2]); /* String to match */

      if (n) { /* COMMENTS switch */
        /* Trim leading, trailing spaces; reduce white space to space;
           convert to upper case */
        let(&str1, edit(str1, 8 + 16 + 128 + 32));
      } else { /* No COMMENTS switch */
        /* Trim leading, trailing spaces; reduce white space to space */
        let(&str1, edit(str1, 8 + 16 + 128));

        /* Change all spaces to double spaces */
        q = (long)strlen(str1);
        let(&str3, space(q + q));
        s = 0;
        for (p = 0; p < q; p++) {
          str3[p + s] = str1[p];
          if (str1[p] == ' ') {
            s++;
            str3[p + s] = str1[p];
          }
        }
        let(&str1, left(str3, q + s));

        /* 30-Jan-06 nm Added single-character-match wildcard argument "$?"
           (or "?" for convenience).  Use ASCII 3 for the exactly-1-char
           wildcard character.  This is a single-character
           match, not a single-token match:  we need "??" to match "ph". */
        while (1) {
          p = instr(1, str1, "$?");
          if (!p) break;
          let(&str1, cat(left(str1, p - 1), chr(3), right(str1, p + 2), NULL));
        }
        /* Allow just "?" for convenience. */
        while (1) {
          p = instr(1, str1, "?");
          if (!p) break;
          let(&str1, cat(left(str1, p - 1), chr(3), right(str1, p + 1), NULL));
        }
        /* End of 30-Jan-06 addition */

        /* Change wildcard to ASCII 2 (to be different from printable chars) */
        /* 1/3/02 (Why are we matching with and without space? I'm not sure.)*/
        /* 30-Jan-06 nm Answer:  We need the double-spacing, and the removal
           of space in the "with spaces" case, so that "ph $ ph" will match
           "ph  ph" (0-token case) - "ph  $  ph" won't match this in the
           (character-based, not token-based) matches().  The "with spaces"
           case is for matching whole tokens, whereas the "without spaces"
           case is for matching part of a token. */
        while (1) {
          p = instr(1, str1, " $* ");
          if (!p) break;
          /* This removes the space before and after the $* */
          let(&str1, cat(left(str1, p - 1), chr(2), right(str1, p + 4), NULL));
        }
        while (1) {
          p = instr(1, str1, "$*");
          if (!p) break;
          /* This simply replaces $* with chr(2) */
          let(&str1, cat(left(str1, p - 1), chr(2), right(str1, p + 2), NULL));
        }
        /* 1/3/02  Also allow a plain $ as a wildcard, for convenience. */
        while (1) {
          p = instr(1, str1, " $ ");
          if (!p) break;
          /* 30-Jan-06 nm Bug fix - changed "2" to "3" below in order to
             properly match 0 tokens */
          let(&str1, cat(left(str1, p - 1), chr(2), right(str1, p + 3), NULL));
        }
        while (1) {
          /* Note: the "$" shortcut must be done last to avoid picking up
             "$*" and "$?". */
          p = instr(1, str1, "$");
          if (!p) break;
          let(&str1, cat(left(str1, p - 1), chr(2), right(str1, p + 1), NULL));
        }

        /* Add wildcards to beginning and end to match middle of any string */
        let(&str1, cat(chr(2), " ", str1, " ", chr(2), NULL));
      } /* End no COMMENTS switch */

      for (i = 1; i <= statements; i++) {
        if (!statement[i].labelName[0]) continue; /* No label */
        if (!m && statement[i].type != (char)p_ &&
            statement[i].type != (char)a_) {
          continue; /* No /ALL switch */
        }
        /* 30-Jan-06 nm Added single-character-match wildcard argument */
        if (!matchesList(statement[i].labelName, fullArg[1], '*', '?'))
          continue;
        if (n) { /* COMMENTS switch */
          let(&str2, "");
          str2 = getDescription(i); /* str2 must be deallocated here */
          /* Strip linefeeds and reduce spaces; cvt to uppercase */
          j = instr(1, edit(str2, 4 + 8 + 16 + 128 + 32), str1);
          if (!j) { /* No match */
            let(&str2, "");
            continue;
          }
          /* Strip linefeeds and reduce spaces */
          let(&str2, edit(str2, 4 + 8 + 16 + 128));
          j = j + ((long)strlen(str1) / 2); /* Center of match location */
          p = screenWidth - 7 - (long)strlen(str(i)) - (long)strlen(statement[i].labelName);
                        /* Longest comment portion that will fit in one line */
          q = (long)strlen(str2); /* Length of comment */
          if (q <= p) { /* Use entire comment */
            let(&str3, str2);
          } else {
            if (q - j <= p / 2) { /* Use right part of comment */
              let(&str3, cat("...", right(str2, q - p + 4), NULL));
            } else {
              if (j <= p / 2) { /* Use left part of comment */
                let(&str3, cat(left(str2, p - 3), "...", NULL));
              } else { /* Use middle part of comment */
                let(&str3, cat("...", mid(str2, j - p / 2, p - 6), "...",
                    NULL));
              }
            }
          }
          print2("%s\n", cat(str(i), " ", statement[i].labelName, " $",
              chr(statement[i].type), " \"", str3, "\"", NULL));
          let(&str2, "");
        } else { /* No COMMENTS switch */
          let(&str2,nmbrCvtMToVString(statement[i].mathString));

          /* 14-Apr-2008 nm JOIN flag */
          tmpFlag = 0; /* Flag that $p or $a is already in string */
          if (joinFlag && (statement[i].type == (char)p_ ||
              statement[i].type == (char)a_)) {
            /* If $a or $p, prepend $e's to string to match */
            k = nmbrLen(statement[i].reqHypList);
            for (j = k - 1; j >= 0; j--) {
              p = statement[i].reqHypList[j];
              if (statement[p].type == (char)e_) {
                let(&str2, cat("$e ",
                    nmbrCvtMToVString(statement[p].mathString),
                    tmpFlag ? "" : cat(" $", chr(statement[i].type), NULL),
                    " ", str2, NULL));
                tmpFlag = 1; /* Flag that a $p or $a was added */
              }
            }
          }

          /* Change all spaces to double spaces */
          q = (long)strlen(str2);
          let(&str3, space(q + q));
          s = 0;
          for (p = 0; p < q; p++) {
            str3[p + s] = str2[p];
            if (str2[p] == ' ') {
              s++;
              str3[p + s] = str2[p];
            }
          }
          let(&str2, left(str3, q + s));

          let(&str2, cat(" ", str2, " ", NULL));
          /* 30-Jan-06 nm Added single-character-match wildcard argument */
          /* We should use matches() and not matchesList() here, because
             commas can be legal token characters in math symbols */
          if (!matches(str2, str1, 2/* ascii 2 0-or-more-token match char*/,
              3/* ascii 3 single-token-match char*/))
            continue;
          let(&str2, edit(str2, 8 + 16 + 128)); /* Trim leading, trailing
              spaces; reduce white space to space */
          printLongLine(cat(str(i)," ",
              statement[i].labelName,
              tmpFlag ? "" : cat(" $", chr(statement[i].type), NULL),
              " ", str2,
              NULL), "    ", " ");
        } /* End no COMMENTS switch */
      } /* Next i */
      continue;
    }


    if (cmdMatches("SET ECHO")) {
      if (cmdMatches("SET ECHO ON")) {
        commandEcho = 1;
        /* 15-Jun-2009 nm Added "!" (see 15-Jun-2009 note above) */
        print2("!SET ECHO ON\n");
        print2("Command line echoing is now turned on.\n");
      } else {
        commandEcho = 0;
        print2("Command line echoing is now turned off.\n");
      }
      continue;
    }

    if (cmdMatches("SET MEMORY_STATUS")) {
      if (cmdMatches("SET MEMORY_STATUS ON")) {
        print2("Memory status display has been turned on.\n");
        print2("This command is intended for debugging purposes only.\n");
        memoryStatus = 1;
      } else {
        memoryStatus = 0;
        print2("Memory status display has been turned off.\n");
      }
      continue;
    }


    if (cmdMatches("SET JEREMY_HENTY_FILTER")) {
      if (cmdMatches("SET JEREMY_HENTY_FILTER ON")) {
        print2("The unification equivalence filter has been turned on.\n");
        print2("This command is intended for debugging purposes only.\n");
        hentyFilter = 1;
      } else {
        print2("This command is intended for debugging purposes only.\n");
        print2("The unification equivalence filter has been turned off.\n");
        hentyFilter = 0;
      }
      continue;
    }


    if (cmdMatches("SET EMPTY_SUBSTITUTION")) {
      if (cmdMatches("SET EMPTY_SUBSTITUTION ON")) {
        minSubstLen = 0;
        print2("Substitutions with empty symbol sequences is now allowed.\n");
        continue;
      }
      if (cmdMatches("SET EMPTY_SUBSTITUTION OFF")) {
        minSubstLen = 1;
        printLongLine(cat("The ability to substitute empty expressions",
            " for variables  has been turned off.  Note that this may",
            " make the Proof Assistant too restrictive in some cases.",
            NULL),
            "", " ");
        continue;
      }
    }


    if (cmdMatches("SET SEARCH_LIMIT")) {
      s = (long)val(fullArg[2]); /* Timeout value */
      print2("IMPROVE search limit has been changed from %ld to %ld\n",
          userMaxProveFloat, s);
      userMaxProveFloat = s;
      continue;
    }

    if (cmdMatches("SET WIDTH")) { /* 18-Nov-85 nm Was SCREEN_WIDTH */
      s = (long)val(fullArg[2]); /* Screen width value */
      if (s >= PRINTBUFFERSIZE - 1) {
        print2(
"?Maximum screen width is %ld.  Recompile with larger PRINTBUFFERSIZE in\n",
            (long)(PRINTBUFFERSIZE - 2));
        print2("mminou.h if you need more.\n");
        continue;
      }
      print2("Screen width has been changed from %ld to %ld\n",
          screenWidth, s);
      screenWidth = s;
      continue;
    }


    if (cmdMatches("SET HEIGHT")) {  /* 18-Nov-05 nm Added */
      s = (long)val(fullArg[2]); /* Screen height value */
      if (s < 2) s = 2;  /* Less than 2 makes no sense */
      print2("Screen height has been changed from %ld to %ld\n",
          screenHeight + 1, s);
      /* screenHeight is one less than the physical screen to account for the
         prompt line after pausing. */
      screenHeight = s - 1;
      continue;
    }


    /* 1-Nov-2013 nm Added UNDO */
    if (cmdMatches("SET UNDO")) {
      s = (long)val(fullArg[2]); /* Maximum UNDOs */
      if (s < 0) s = 0;  /* Less than 0 UNDOs makes no sense */
      /* Reset the stack size if it changed */
      if (processUndoStack(NULL, PUS_GET_SIZE, "", 0) != s) {
        print2(
            "The maximum number of UNDOs was changed from %ld to %ld\n",
            processUndoStack(NULL, PUS_GET_SIZE, "", 0), s);
        processUndoStack(NULL, PUS_NEW_SIZE, "", s);
        if (PFASmode == 1) {
          /* If we're in the Proof Assistant, assign the first stack
             entry with the current proof (the stack was erased) */
          processUndoStack(&proofInProgress, PUS_PUSH, "", 0);
        }
      } else {
        print2("The maximum number of UNDOs was not changed.\n");
      }
      continue;
    }


    if (cmdMatches("SET UNIFICATION_TIMEOUT")) {
      s = (long)val(fullArg[2]); /* Timeout value */
      print2("Unification timeout has been changed from %ld to %ld\n",
          userMaxUnifTrials,s);
      userMaxUnifTrials = s;
      continue;
    }


    if (cmdMatches("OPEN LOG")) {
        /* Open a log file */
        let(&logFileName, fullArg[2]);
        logFilePtr = fSafeOpen(logFileName, "w");
        if (!logFilePtr) continue; /* Couldn't open it (err msg was provided) */
        logFileOpenFlag = 1;
        print2("The log file \"%s\" was opened %s %s.\n",logFileName,
            date(),time_());
        continue;
    }

    if (cmdMatches("CLOSE LOG")) {
        /* Close the log file */
        if (!logFileOpenFlag) {

          print2("?Sorry, there is no log file currently open.\n");
        } else {
          print2("The log file \"%s\" was closed %s %s.\n",logFileName,
              date(),time_());
          fclose(logFilePtr);
          logFileOpenFlag = 0;
        }
        let(&logFileName,"");
        continue;
    }

    if (cmdMatches("OPEN TEX") || cmdMatches("OPEN HTML")) {
      if (cmdMatches("OPEN HTML")) {
        print2("?OPEN HTML is obsolete - use SHOW STATEMENT * / HTML\n");
        continue;
      }
      if (texDefsRead) {
        /* Current limitation - can only read .def once */
        if (cmdMatches("OPEN HTML") != htmlFlag) {
          print2("?You cannot use both LaTeX and HTML in the same session.\n");
          print2(
              "?You must EXIT and restart Metamath to switch to the other.\n");
          continue;
        }
      }
      htmlFlag = cmdMatches("OPEN HTML");

      /* Open a TeX file */
      let(&texFileName,fullArg[2]);
      if (switchPos("/ NO_HEADER")) {
        texHeaderFlag = 0;
      } else {
        texHeaderFlag = 1;
      }
      /* 14-Sep-2010 nm Added OLD_TEX */
      if (switchPos("/ OLD_TEX")) {
        oldTexFlag = 1;
      } else {
        oldTexFlag = 0;
      }
      texFilePtr = fSafeOpen(texFileName,"w");
      if (!texFilePtr) continue; /* Couldn't open it (err msg was provided) */
      texFileOpenFlag = 1;
      print2("Created %s output file \"%s\".\n",
          htmlFlag ? "HTML" : "LaTeX", texFileName);
      printTexHeader(texHeaderFlag);
      oldTexFlag = 0;
      continue;
    }

    if (cmdMatches("CLOSE TEX") || cmdMatches("CLOSE HTML")) {
      if (cmdMatches("CLOSE HTML")) {
        print2("?CLOSE HTML is obsolete - use SHOW STATEMENT * / HTML\n");
        continue;
      }
      /* Close the TeX file */
      if (!texFileOpenFlag) {
        print2("?Sorry, there is no %s file currently open.\n",
            htmlFlag ? "HTML" : "LaTeX");
      } else {
        print2("The %s output file \"%s\" has been closed.\n",
            htmlFlag ? "HTML" : "LaTeX", texFileName);
        printTexTrailer(texHeaderFlag);
        fclose(texFilePtr);
        texFileOpenFlag = 0;
      }
      let(&texFileName,"");
      continue;
    }

    /* Similar to Unix 'more' */
    if (cmdMatches("MORE")) {
      list1_fp = fSafeOpen(fullArg[1], "r");
      if (!list1_fp) continue; /* Couldn't open it (error msg was provided) */
      while (1) {
        if (!linput(list1_fp, NULL, &str1)) break; /* End of file */
        /* Print a line on the screen */
        if (!print2("%s\n", str1)) break; /* User typed Q */
      }


      fclose(list1_fp);
      continue;
    } /* end MORE */


    if (cmdMatches("FILE SEARCH")) {
      /* Search the contents of a file and type on the screen */

      type_fp = fSafeOpen(fullArg[2], "r");
      if (!type_fp) continue; /* Couldn't open it (error msg was provided) */
      fromLine = 0;
      toLine = 0;
      searchWindow = 0;
      i = switchPos("/ FROM_LINE");
      if (i) fromLine = (long)val(fullArg[i + 1]);
      i = switchPos("/ TO_LINE");
      if (i) toLine = (long)val(fullArg[i + 1]);
      i = switchPos("/ WINDOW");
      if (i) searchWindow = (long)val(fullArg[i + 1]);
      /*??? Implement SEARCH /WINDOW */
      if (i) print2("Sorry, WINDOW has not be implemented yet.\n");

      let(&str2, fullArg[3]); /* Search string */
      let(&str2, edit(str2, 32)); /* Convert to upper case */

      tmpFlag = 0;

      /* Search window buffer */
      pntrLet(&pntrTmp, pntrSpace(searchWindow));

      j = 0; /* Line # */
      m = 0; /* # matches */
      while (linput(type_fp, NULL, &str1)) {
        j++;
        if (j > toLine && toLine != 0) break;
        if (j >= fromLine || fromLine == 0) {
          let(&str3, edit(str1, 32)); /* Convert to upper case */
          if (instr(1, str3, str2)) { /* Match occurred */
            if (!tmpFlag) {
              tmpFlag = 1;
              print2(
                    "The line number in the file is shown before each line.\n");
            }
            m++;
            if (!print2("%ld:  %s\n", j, left(str1,
                MAX_LEN - (long)strlen(str(j)) - 3))) break;
          }
        }
        for (k = 1; k < searchWindow; k++) {
          let((vstring *)(&pntrTmp[k - 1]), pntrTmp[k]);
        }
        if (searchWindow > 0)
            let((vstring *)(&pntrTmp[searchWindow - 1]), str1);
      }
      if (!tmpFlag) {
        print2("There were no matches.\n");
      } else {
        if (m == 1) {
          print2("There was %ld matching line in the file %s.\n", m,
              fullArg[2]);
        } else {
          print2("There were %ld matching lines in the file %s.\n", m,
              fullArg[2]);
        }
      }

      fclose(type_fp);

      /* Deallocate search window buffer */
      for (i = 0; i < searchWindow; i++) {
        let((vstring *)(&pntrTmp[i]), "");
      }
      pntrLet(&pntrTmp, NULL_PNTRSTRING);


      continue;
    }


    if (cmdMatches("SET UNIVERSE") || cmdMatches("ADD UNIVERSE") ||
        cmdMatches("DELETE UNIVERSE")) {

      /*continue;*/ /* ???Not implemented */
    } /* end if xxx UNIVERSE */



    if (cmdMatches("SET DEBUG FLAG")) {
      print2("Notice:  The DEBUG mode is intended for development use only.\n");
      print2("The printout will not be meaningful to the user.\n");
      i = (long)val(fullArg[3]);
      if (i == 4) db4 = 1;  /* Not used */
      if (i == 5) db5 = 1;  /* mmpars.c statistics; mmunif.c overview */
      if (i == 6) db6 = 1;  /* mmunif.c details */
      if (i == 7) db7 = 1;  /* mmunif.c more details; mmveri.c */
      if (i == 8) db8 = 1;  /* mmpfas.c unification calls */
      if (i == 9) db9 = 1;  /* memory */ /* use SET MEMORY_STATUS ON instead */
      continue;
    }
    if (cmdMatches("SET DEBUG OFF")) {
      db4 = 0;
      db5 = 0;
      db6 = 0;
      db7 = 0;
      db8 = 0;
      db9 = 0;
      print2("The DEBUG mode has been turned off.\n");
      continue;
    }

    if (cmdMatches("ERASE")) {
      if (sourceChanged) {
        print2("Warning:  You have not saved changes to the source.\n");
        str1 = cmdInput1("Do you want to ERASE anyway (Y, N) <N>? ");
        if (str1[0] != 'y' && str1[0] != 'Y') {
          print2("Use WRITE SOURCE to save the changes.\n");
          continue;
        }
        sourceChanged = 0;
      }
      eraseSource();
      showStatement = 0;
      proveStatement = 0;
      print2("Metamath has been reset to the starting state.\n");
      continue;
    }

    if (cmdMatches("VERIFY PROOF")) {
      if (switchPos("/ SYNTAX_ONLY")) {
        verifyProofs(fullArg[2],0); /* Parse only */
      } else {
        verifyProofs(fullArg[2],1); /* Parse and verify */
      }
      continue;
    }

    print2("?This command has not been implemented.\n");
    continue;

  }
} /* substitute */


/* Compare strings via pointers */
int qsortStringCmp(const void *p1, const void *p2)
{
  vstring tmp = "";
  long i1, i2;
  int r;
  /* Returns -1 if p1 < p2, 0 if equal, 1 if p1 > p2 */
  if (qsortKey[0] == 0) {
    /* No key, use full line */
    return strcmp(*(char * const *)p1, *(char * const *)p2);
  } else {
    i1 = instr(1, *(char * const *)p1, qsortKey);
    i2 = instr(1, *(char * const *)p2, qsortKey);
    r = strcmp(
        right(*(char * const *)p1, i1),
        right(*(char * const *)p2, i2));
    let(&tmp, ""); /* Deallocate temp string stack */
    return r;
  }
}

