home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 5 / FreshFish_July-August1994.bin / bbs / misc / chembalance-3.0.lha / ChemBalance-3.0 / ChemBalance next >
Text File  |  1994-03-29  |  74KB  |  2,870 lines

  1. /********************************************************/
  2. /*                                                      */
  3. /*   This is ChemBalance, v3.0 which balances           */
  4. /*   chemical equations.  You can input up to nine      */
  5. /*   terms total. This program is developed by Patrick  */
  6. /*   Reany, who reserves copyright to it. However,      */
  7. /*   permission is granted to all to use it free of     */
  8. /*   cost. All that's asked is the maintenance of the   */
  9. /*   title, "ChemBalance", the version number and       */
  10. /*   the author's name. Version 2.0 can be found on     */
  11. /*   Fred Fish #916. This version allows for multiple   */
  12. /*   generalized hydrations.                            */
  13. /*                                                      */
  14. /********************************************************/
  15.  
  16.  
  17.        /***************************************/
  18.        /*                                     */
  19.        /*            ChemBalance, v3.0        */
  20.        /*                                     */
  21.        /*             Copyright 1994          */
  22.        /*                                     */
  23.        /***************************************/
  24.  
  25.  
  26. /********************************************************/
  27. /*     Prelims    Prelims    Prelims    Prelims         */
  28. /********************************************************/
  29.  
  30. /*    options    */
  31.  
  32.    options prompt "--->  " 
  33.  
  34. /*  error signals  */
  35.  
  36.    signal on ioerr
  37. /*   signal on novalue */
  38.    signal on error
  39.  
  40. signal bypass  /* jump to Initializations */
  41.  
  42.    exitscript:    say "Exiting script."; exit
  43.  
  44.    error  : call report(sigl, "ERROR");  signal exitscript
  45.    novalue: call report(sigl, "NOVALUE");  signal exitscript
  46.    ioerr  : call report(sigl, "IOERR");  signal exitscript
  47.  
  48.    report: procedure expose rc
  49.       parse arg sl, text
  50.       pen1 = '1b'x"[31m"; pen2 = '1b'x"[32m"
  51.       say pen2 || text || pen1 "trap (rc =" rc", sigl =" sl") ."
  52.    return
  53.  
  54.  
  55. bypass:
  56.  
  57. /* call close("STDOUT")
  58. if open("STDOUT",'vdk:checkfile','w') then
  59.   say "Checkfile open......" */
  60.  
  61. /********************************************************/
  62. /*          Initializations         Initializations     */
  63. /********************************************************/
  64.  
  65. address command 'echo "*ec"'
  66. say
  67. say "This is ChemBalance, version 3.0:"
  68. say
  69.  
  70. /*    constants     */
  71.  
  72.    true  = 1
  73.    false = 0
  74.    lowerletters = xrange('a','z')
  75.    upperletters = xrange('A','Z')
  76.    digits       = xrange('0','9')
  77.  
  78.  
  79. /*     variables     */
  80.  
  81.    errortesting = false 
  82.    looking_for_nonregular_solutions = false
  83.  
  84. restart:
  85.  
  86.    cycle_count = 0 
  87.    #elements.@side.1 = 0 /* This will count # of elements */
  88.    #elements.@side.2 = 0
  89.    elements_list.@side.1 = "" 
  90.    elements_list.@side.2 = ""
  91.    do i=1 to 3
  92.      what_side.2.term_list.i.element_count = 0
  93.    end
  94.  
  95.    /* These last two initializations begin the lists*/
  96.    /* that will contain all elements on each side. */
  97.    /* Each list will contain only one entry for each */
  98.    /* element on the respective sides. */
  99.  
  100.   if looking_for_nonregular_solutions then
  101.     looking_for_nonregular_solutions = false
  102.   /*  trace ?r  */
  103.  
  104.    do i=1 to 20
  105.       UDcoef.i = 0    /* UD  = user defined */
  106.    end
  107.    UDCcount = 0    /* UDC = user defined coefficient */
  108.    UDCineffect = false
  109.    doing_negative_values = false
  110.  
  111. /*************************************************************/
  112. /*     Main      Main       Main       Main        Main      */
  113. /*************************************************************/
  114.  
  115. call look_for_syntax_errors /* Not ARexx syntax errors, */
  116.        /*   but user-input errors.  */
  117.  
  118. begin_nonregular:
  119. /* trace ?r */
  120. if looking_for_nonregular_solutions then
  121.   do
  122.     parse var $unequation LHS "=" RHS
  123.     elements_list.@side.1 = "" 
  124.     elements_list.@side.2 = ""
  125.     #elements.@side.1 = 0
  126.     #elements.@side.2 = 0
  127.     cycle_count = 0 
  128.     do i=1 to 10
  129.       UDcoef.i = 0 
  130.     end
  131.     UDCcount = 0
  132.     UDCineffect = false
  133.     doing_negative_values = false
  134.     call more_errors() /* needed to initialize some values */
  135.   end
  136.  
  137. call get_started
  138.  
  139. restart2:
  140. RHSprime = ""     /* The variables hold the homogeneous forms */
  141. LHSprime = ""     /* to be put into make_new_equation.        */
  142. if errortesting then 
  143.   if ~UDCineffect then do
  144.     say "Just obeyed call to 'restart2'"
  145.     say "Just before 'process(LHS)':"
  146.     say "    LHS = " LHS
  147.     say "    RHS = " RHS
  148.   end
  149. /* trace ?r */
  150. call process(LHS,1)
  151. if errortesting then do
  152.     say "EO process(LHS) ********************** "
  153.   end
  154. call process(RHS,2)
  155. if errortesting then do
  156.     say "EO process(RHS) ********************** "
  157.   end
  158.  
  159. if ~doing_negative_values then 
  160.   if belated_error() then do
  161.      say
  162.      say "ERROR: Each element does not appear on both sides."
  163.      say "Check for spelling errors."
  164.      say 
  165.      signal restart
  166.     end
  167. if UDCineffect then do
  168.   UDCineffect = false
  169.   call make_new_equation
  170.  
  171. /*  trace ?r  */
  172.  
  173.   LHS! = LHS
  174.   RHS! = RHS
  175.   #LH_terms = words(LHS)
  176.   #RH_terms = words(RHS)
  177.   #total_terms = #LH_terms + #RH_terms 
  178.   elements_list =  elements_list.@side.2
  179.   signal restart2
  180. end
  181.  
  182. /*  trace ?r  */
  183. call calculate_answer
  184. if errortesting then say "EO calculate_answer"
  185. call print_answer(#total_terms-1)
  186. call verify_solution
  187.  
  188. if exists(interval) then do
  189.     finish_time = time()
  190.     say "time interval = "interval(start_time,finish_time)
  191.     end
  192.   else do
  193.     say "finish_time = " time()
  194.     say "start_time  = " start_time
  195.   end
  196.  
  197. signal restart  /*********           End Main        **********/
  198.  
  199.  
  200. /*************************************************************/
  201. /*                                                           */
  202. /*   Functions      Functions      Functions      Functions  */
  203. /*                                                           */
  204. /*************************************************************/
  205.  
  206.  
  207. /*************************************************************/
  208. /*     look_for_syntax_errors     look_for_syntax_errors     */
  209. /*************************************************************/
  210.  
  211. look_for_syntax_errors:
  212. /* trace ?r */
  213.  
  214. errors = true
  215.  
  216. do while errors /**/
  217.   say " "
  218.   say "Input unbalanced equation..."
  219.   say " "
  220.   parse pull unequation
  221.   start_time = time()
  222.   if unequation ~= "" then
  223.    if ~syntax_OK(unequation) then do
  224.        say "Use only alphanumeric, parens, `+', `=', '*', or `_'"
  225.         signal restart
  226.        end
  227.      else if upper(unequation)='QUIT' then do
  228.            say
  229.            say "Quiting ChemBalance."
  230.            say
  231.           exit; end
  232.        else
  233.         if ~elements_OK(unequation) then do
  234.            say
  235.            say "Elements must be single capital letters"
  236.            say "or a capital letter and a small case letter."
  237.            say
  238.            say "Perhaps you wrote a capital letter in "
  239.            say " lower case."
  240.            say
  241.            signal restart; end 
  242.          else 
  243.          if ~parse_OK(unequation) then do
  244.             say error_msg; signal restart; end
  245.           else
  246.            if more_errors() then do
  247.              say error_msg; signal restart; end
  248.             else
  249.              if ~underscoresOK(unequation) then do
  250.                 say error_msg;  signal restart; end
  251.                 else errors = false
  252. end /**/
  253. return
  254.  
  255. /*************************************************************/
  256. /*  get_started    get_started    get_started    get_started */
  257. /*************************************************************/
  258.  
  259. get_started:
  260. if errortesting then do
  261.      say "  LHS = "LHS
  262.    end
  263. if looking_for_nonregular_solutions then do
  264.   originalLHS = LHS
  265.   originalRHS = RHS
  266. end
  267. LHS! = LHS
  268. RHS! = RHS
  269. /* trace ?r */
  270. original_eq = " "||LHS||" "||RHS
  271. call get_original_stripped_eq()
  272. say original_stripped_eq
  273.  
  274. #originally_on_left = words(LHS)
  275. #originally_on_right = words(RHS)
  276. call preprocess(LHS,1)/*Puts all terms with coef on right side */
  277. call preprocess(RHS,2)
  278. eq = LHS||" "||RHS
  279. #Lterms = words(LHS)
  280. #Rterms = words(RHS)
  281. if #Lterms+#Rterms>20 then do
  282.   say "You can only use up to 20 terms. Sorry."
  283.   signal restart
  284. end
  285. if ~parensOK(eq) then do
  286.    say; say error_msg;  signal restart
  287.   end 
  288. call process_for_UDCs /*This strips off the UD coefficients */
  289. if errortesting then do
  290.   say "Just after 'process_for_UDCs':"
  291.   say "    LHS = " LHS
  292.   say "    RHS = " RHS
  293. end
  294.  
  295.  
  296. call convert_asterisk(1)
  297. if errortesting then do
  298.      say "  LHS = " LHS
  299.    end
  300. call convert_asterisk(2)
  301. if errortesting then do
  302.      say "  RHS = " RHS
  303.    end
  304. return
  305.  
  306. get_original_stripped_eq:
  307. if looking_for_nonregular_solutions then
  308.     do
  309.       /* trace ?r */
  310.       original_stripped_eq=$originalL||" "||$originalR
  311.       return
  312.     end
  313.   else do /**/
  314.     os_eq=original_eq
  315.     do_again:
  316.     do i=2 to length(os_eq) /****/
  317.       position=pos(char(os_eq,i),digits)
  318.       if position>0 & pos(char(os_eq,i-1)," ")>0 then
  319.         do /******/
  320.           os_eq=delstr(os_eq,i,1)
  321.           signal do_again
  322.         end /******/
  323.     end /****/
  324.     original_stripped_eq=os_eq
  325.     return
  326.   end /**/
  327.  
  328.  
  329. /*************************************************************/
  330. /*    syntax_OK     syntax_OK      syntax_OK    syntax_OK    */
  331. /*************************************************************/
  332.  
  333.  
  334. syntax_OK: procedure expose errortesting true false lowerletters upperletters digits
  335. /*  trace ?r */
  336. if errortesting then say "entering 'syntax_OK'"
  337. equation = arg(1)
  338. misc         = '()+=*_ '
  339.  
  340. error = false
  341. do i=1 to length(equation) while ~error
  342.   ch  = char(equation,i)
  343.   in_lowers = pos(ch,lowerletters)
  344.   in_uppers = pos(ch,upperletters)
  345.   in_numeric= pos(ch,digits)
  346.   in_misc   = pos(ch,misc)
  347.   if in_lowers + in_uppers + in_misc + in_numeric = 0
  348.      then error = true
  349. end
  350.  
  351. return ~error
  352.  
  353.  
  354. /*************************************************************/
  355. /*        elements_OK     elements_OK      elements_OK       */
  356. /*************************************************************/
  357.  
  358.  
  359. elements_OK: procedure expose errortesting true false lowerletters upperletters digits
  360.  
  361. /*  trace ?r */
  362.  
  363. equation = arg(1)
  364. error = false
  365. if first_char_lower() then error = true
  366.   else do
  367.     do i=2 to length(equation) while ~error
  368.       ch  = char(equation,i)
  369.       ch! = char(equation,i-1)
  370.       if pos(ch,lowerletters)>0 & pos(ch!,upperletters)=0 then
  371.          error = true
  372.     end
  373.   end
  374.  
  375. return ~error
  376.  
  377. first_char_lower:
  378. if pos(char(equation,1),lowerletters)>0 then return true
  379.   else return false
  380.  
  381.  
  382.  
  383. /*************************************************************/
  384. /*      parse_OK      parse_OK      parse_OK     parse_OK    */
  385. /*************************************************************/
  386.  
  387.  
  388. parse_OK: procedure expose errortesting error_msg RHS LHS digits
  389.  
  390. equation = arg(1)
  391.  
  392. if errortesting then say "entering 'parse_OK'"
  393.  
  394. if index(equation,"=")=0 then do
  395.   error_msg = "Where is the '=' sign?"
  396.   return 0
  397.   end
  398. else do
  399.   leftmostleftpart = ""
  400.   leftmostrightpart = ""
  401.   parse var equation leftside"="rightside
  402.   do while index(leftside,"+") > 0
  403.     parse var leftside leftmostpart"+"leftside
  404.     leftmostleftpart = leftmostleftpart||" "||leftmostpart
  405.     end
  406.   LHS = leftmostleftpart ||" "|| leftside
  407.  
  408.   do while index(rightside,"+") > 0
  409.     parse var rightside leftmostpart"+"rightside
  410.     leftmostrightpart = leftmostrightpart||" "||leftmostpart
  411.     end
  412.   RHS = leftmostrightpart ||" "|| rightside
  413.   say LHS RHS
  414.   return 1
  415. end
  416.  
  417.  
  418. /*************************************************************/
  419. /*       more_errors     more_errors     more_errors         */
  420. /*************************************************************/
  421.  
  422.  
  423. more_errors: 
  424.  
  425.  
  426. #LH_terms = words(LHS)
  427. #RH_terms = words(RHS)
  428. #total_terms = #LH_terms + #RH_terms 
  429. if #total_terms < 3 then do
  430.     error_msg = "Equation must have 3 or more terms total."
  431.     goof = true
  432.     end
  433.   else goof = false
  434.  
  435.  
  436. do i=1 to 2
  437.     if i=1 then #terms = #LH_terms
  438.           else #terms = #RH_terms
  439.     do j=1 to #terms
  440.        what_side.i.term_list.j.element_count = 0
  441.     end
  442. end
  443.  
  444. return goof
  445.  
  446.  
  447.  
  448. /*************************************************************/
  449. /*     underscoresOK     underscoresOK     underscoresOK     */
  450. /*************************************************************/
  451.  
  452. underscoresOK: procedure expose errortesting error_msg true false digits upperletters lowerletters
  453.  
  454. /*  trace ?r */
  455.  
  456. if errortesting then say "Entering `underscoresOK'"
  457.  
  458. equation = arg(1)
  459.  
  460. error = false
  461. do i=1 to words(equation) while ~error /**/
  462.   more_underscores = true
  463.   present_word = word(equation,i)
  464.   do while more_underscores & ~error /***/
  465.     position = pos("_",present_word)
  466.     if position=0 then
  467.         more_underscores = false
  468.      else
  469.       if position=1 then do
  470.         error_msg= "Underscore can not be first char in word."
  471.         error = true
  472.         end
  473.        else
  474.         if position=length(present_word) then
  475.            do
  476.              error_msg=,
  477.              "You can't place underscore at end of term."
  478.              error = true
  479.            end
  480.           else
  481.            do /****/
  482.             ch = char(present_word,position-1)
  483.             if ch~=')' & pos(upper(ch),upperletters)=0 then
  484.                do
  485.                 error_msg="underscore must be preceded by",
  486.                   ||" either a ')' or an alphabetic char."
  487.                 error = true; break
  488.                end
  489.              else
  490.               do /*****/
  491.                ch = char(present_word,position+1)
  492.                if pos(ch,digits)=0 then do
  493.                  error_msg="Must have digit immediately after",
  494.                    ||" an underscore.";  error = true; end
  495.                 else
  496.                   parse var present_word stuff '_' present_word
  497.               end /*****/
  498.            end /****/
  499.   end /***/
  500.   if error then break
  501. end /**/
  502.  
  503. return ~error
  504.  
  505.  
  506. /*************************************************************/
  507. /*    parensOK   parensOK   parensOK   parensOK   parensOK   */
  508. /*************************************************************/
  509.  
  510. parensOK: procedure expose error_msg errortesting true false digits
  511.  
  512. if errortesting then
  513.   say "Entering 'parensOK'."
  514.  
  515. /*  trace ?r */
  516.  
  517. equation = arg(1)
  518.  
  519. error = false
  520. do ii=1 to words(equation) while ~error /**/
  521.   term = word(equation,ii)
  522.   len = length(term)
  523.   if char(term,len)=')'|char(term,len)='(' then do
  524.      error = true; error_msg='Cannot have parenthesis as',
  525.      ||' last char in term.'
  526.      signal jump1
  527.    end
  528. end
  529. do ii=1 to words(equation) while ~error /**/
  530.   current_term = word(equation,ii)
  531.   R_index = pos(")",current_term)
  532.   L_index = pos("(",current_term)
  533.   len = length(current_term)
  534.   if R_index=len | L_index=len then do
  535.     error = true; error_msg='Cannot have parenthesis as',
  536.      ||' last char in term.'
  537.     end
  538.    else do /***/
  539.     #R = how_many(")",current_term)
  540.     #L = how_many("(",current_term)
  541.     if #R>0 then do
  542.       lastposRparen = 0
  543.       do i#=1 to #R
  544.         do until char(current_term,i#)=")"
  545.           i#=i#+1
  546.         end
  547.         if char(current_term,i#+1)~="_" then do
  548.           error = true
  549.           error_msg="Parenthesis error! Right parens",
  550.             ||" must have '_' following!"
  551.           break
  552.           end
  553.         else
  554.           lastposRparen = i#
  555.       end
  556.     end
  557.  
  558.     if #R=0 & #L=0 then iterate ii
  559.      else if #R=1 & #L=1 then do
  560.           if R_index>=L_index+2 then iterate ii
  561.               else do error_msg= "Mismatched parentheses!"
  562.                    error=true; break; end
  563.           end
  564.        else if #R~=#L then do
  565.             error_msg= "Mismatched parentheses!"; error=true; end
  566.         
  567.    end /***/
  568.  
  569. end /**/
  570. jump1:
  571. return ~error
  572.  
  573.  
  574.  
  575. /*************************************************************/
  576. /*      how_many     how_many     how_many     how_many      */
  577. /*************************************************************/
  578.  
  579. how_many: procedure
  580. /* trace ?r */
  581. char   = arg(1)
  582. string = arg(2)
  583.  
  584. count = 0
  585. do while pos(char,string)>0 
  586.   count = count + 1
  587.   len = length(string)
  588.   string = right(string,len-pos(char,string))
  589. end
  590.  
  591. return count
  592.  
  593.  
  594.  
  595. /*************************************************************/
  596. /*      belated_error     belated_error     belated_error    */
  597. /*************************************************************/
  598.  
  599. belated_error:
  600.  
  601. /* This checks to see if elements are same on both sides. */
  602.  
  603. /*  trace ?r */
  604.  
  605. if errortesting then say "entering 'belated_error'"
  606.  
  607. goof = false
  608. original_elements_list = elements_list.@side.1
  609. list.1 = sort(elements_list.@side.1)
  610. list.2 = sort(elements_list.@side.2)
  611. if compress(list.1)~=compress(list.2) then goof = true
  612.  
  613. return goof
  614.  
  615.  
  616.  
  617. /*************************************************************/
  618. /*     char     char     char     char     char     char     */
  619. /*************************************************************/
  620.  
  621. char: procedure
  622.  
  623. /* trace ?r */
  624.  
  625. string = arg(1)
  626. indx   = arg(2)
  627. if indx > length(string) then return 0
  628.   else return substr(string,indx,1)
  629.  
  630.  
  631.  
  632. /*************************************************************/
  633. /*     sort     sort     sort     sort     sort     sort     */
  634. /*************************************************************/
  635.  
  636. sort: procedure expose errortesting List_of_elements
  637.  
  638. if errortesting then say "Entering function 'sort'"
  639. if ~exists('ram:sort') then
  640.        address command 'copy c:sort to ram:sort'
  641.  
  642. List_of_elements = arg(1)
  643. if errortesting then do
  644.   say "List of elements = " arg(1)
  645. end
  646. file = 'ram:list'
  647. ramfile = 'ramfile'
  648.  
  649. /*  trace ?r */
  650.  
  651. if open(ramfile,file,'w') then do
  652.   @count = words(List_of_elements)
  653.   do i=1 to @count
  654.     call writeln(ramfile,word(List_of_elements,i))
  655.   end
  656.   call close(ramfile)
  657.   address command 'ram:sort ram:list ram:mylist'
  658.  
  659.   elementslist = ""
  660.   myfile = 'ram:mylist'
  661.   ram_myfile ='ram_myfile'
  662.   if open(ram_myfile,myfile,'r')then do
  663.     do i=1 to @count
  664.        next_element = readln(ram_myfile)
  665.        if next_element~='(' & next_element~=')' then
  666.          elementslist = elementslist || next_element
  667.     end
  668.     call close(ram_myfile)
  669.   end
  670. end
  671. return elementslist
  672.  
  673.  
  674.  
  675. /*************************************************************/
  676. /*   convert_asterisk   convert_asterisk   convert_asterisk  */
  677. /*************************************************************/
  678.  
  679. convert_asterisk: procedure expose LHS RHS errortesting true false digits
  680.  
  681. /* trace ?r */
  682.  
  683. if errortesting then
  684.     say "Entering convert_asterisk."
  685.  
  686. if arg(1)=1 then string = LHS
  687.      else string = RHS
  688. newstring = ""
  689. do z=1 to words(string)
  690.   tempstring = fix_next_term(word(string,z))
  691.   newstring = newstring||" "||tempstring
  692. end
  693. if arg(1) = 1 then LHS = newstring
  694.   else RHS = newstring
  695.  
  696. /* end convert_asterisk */
  697. return
  698.  
  699.  
  700. fix_next_term: procedure expose true false digits errortesting
  701.  
  702. string = arg(1)
  703. /*  trace ?r */
  704. if pos("*",string)=0 then more_asterisks = false
  705.     else more_asterisks = true
  706. do while more_asterisks /**/
  707.   if pos("*",string)=0 then more_asterisks = false
  708.     else do /***/
  709.       parse var string LHstuff "*" RHstuff
  710.       factor = 1
  711.       if pos(left(RHstuff,1),digits) > 0 then do /**+**/
  712.           factor = left(RHstuff,1)
  713.           RHstuff = delstr(RHstuff,1,1)
  714.           end /**+**/
  715.       if pos(left(RHstuff,1),digits) > 0 then do /*****/
  716.           factor = factor||left(RHstuff,1,1)
  717.           RHstuff = delstr(RHstuff,1,1)
  718.           end /*****/
  719.       if pos("*",RHstuff)>0 then do
  720.           parse var RHstuff middlestuff "*" farrightstuff
  721.           left = LHstuff||"("||middlestuff||")"
  722.           string = left||"_"||factor||"*"||farrightstuff
  723.         end
  724.       else
  725.         do /*****/
  726.            string = LHstuff||"("||RHstuff||")"||"_"||factor
  727.         end /*****/
  728.       if errortesting then say " string = " string
  729.     end /***/
  730. end/**/
  731. return string
  732.  
  733.  
  734. /*************************************************************/
  735. /*         preprocess     preprocess     preprocess          */
  736. /*************************************************************/
  737.  
  738.  
  739. preprocess: /* puts all coefficioned terms to far right */
  740.  
  741. if errortesting then say "entering 'preprocess'"
  742.  
  743. /*  trace ?r */
  744.  
  745. sideterms = arg(1)
  746. side#$ = arg(2)
  747. Lstuff = " "
  748. Rstuff = " "
  749. #terms = words(sideterms)
  750. rightmostterms = ""
  751. do i=1 to #terms
  752.   parse var sideterms leftmostterm  sideterms
  753.   if pos(char(leftmostterm,1),digits)=0 then
  754.     Lstuff = Lstuff||" "||leftmostterm
  755.   else
  756.     rightmostterms = rightmostterms||" "||leftmostterm
  757. end
  758. if side#$ = 1 then LHS = Lstuff||" "||rightmostterms
  759.   else RHS = Lstuff||" "||rightmostterms
  760.  
  761. return /* from: preprocess */
  762.                                                
  763.                                                
  764.                                                
  765.                                                
  766. /*************************************************************/
  767. /*         make_new_equation       make_new_equation         */
  768. /*************************************************************/
  769.  
  770.  
  771. make_new_equation:
  772.  
  773. /* Since ChemBalance cannot do nested parens I decided to deal with UDC's this way:
  774. 1) preprocess the equation to eliminate asterisks and parens, but don't send the result to 'add_to_elements_list' yet.
  775. 2) Take each term that has subscripts and convert it to a paren pair.
  776. For example, consider converting the term 4Ca(OH)_2*3H_2O first to 4CaO_2H_2H_6O_3 and then to (CaO_2H_2H_6O_3)_4. Now this latter form is run thru the mill as though it were the original term. It's clever but it's also complicated, for the program's procedures must constantly decide they're doing negative values and whether UDCineffect.                                       */
  777.  
  778.  
  779. if errortesting then say "entering 'make_new_equation'"
  780.  
  781. /*  trace ?r */
  782. if errortesting then
  783.   do i=1 to #Rterms+#Lterms
  784.     say "UDcoef."i" = " UDcoef.i
  785.   end
  786.  
  787. newLHS = " "
  788. #Lterms = words(LHS)
  789. transferterm = ""
  790. do i=1 to #Lterms
  791.   parse var LHS leftmostterm  LHS
  792.   if UDcoef.i ~= 0 then
  793.     transferterm = transferterm,
  794.          ||"["||compress(leftmostterm)||"]_"||UDcoef.i
  795.   else
  796.     newLHS = newLHS||" "||leftmostterm
  797. end
  798. LHS = newLHS
  799.  
  800. newRHS = " "
  801. #Rterms = words(RHS)
  802. termstomoveright = " "
  803. do i=#Lterms+1 to #Rterms+#Lterms
  804.   parse var RHS leftmostterm  RHS
  805.   if UDcoef.i ~= 0 then
  806.     termstomoveright = termstomoveright,
  807.           ||"("||compress(leftmostterm)||")_"||UDcoef.i
  808.   else
  809.     newRHS = newRHS||" "||leftmostterm
  810. end
  811. RHS = newRHS||" "||termstomoveright||transferterm
  812.  
  813. return
  814.  
  815.  
  816.  
  817. /*************************************************************/
  818. /*         process_for_UDCs        process_for_UDCs          */
  819. /*************************************************************/
  820.  
  821.  
  822. process_for_UDCs: /* This strips off all coefficients and puts them in the array UDcoef.i */
  823.  
  824. if errortesting then say "entering 'process_for_UDCs'"
  825.  
  826. /*  trace ?r */
  827.  
  828. if errortesting then say "eq = " eq
  829. Lstuff = " "
  830. do ii=1 to words(eq) /**/
  831.   if errortesting then say "ii = " ii
  832.   if pos(left(word(eq,ii),1),xrange("1","9"))=0 then
  833.         Lstuff = Lstuff||" "||word(eq,ii)
  834.     else do /***/
  835.       UDCcount = UDCcount + 1
  836.       UDC = ""
  837.       more_digits = true
  838.       ith_term = word(eq,ii)
  839.       do while more_digits /*^^*/
  840.         UDC = UDC||char(ith_term,1)
  841.         ith_term = right(ith_term,length(ith_term)-1)
  842.         if pos(char(ith_term,1),digits) = 0 then
  843.           more_digits = false
  844.       end /*^^*/
  845.       UDcoef.ii = UDC
  846.       Lstuff = Lstuff||" "||ith_term
  847.     end /***/
  848. end /**/
  849. /*  trace ?r */
  850. if errortesting then
  851.   do i=1 to 10
  852.     say "UDcoef.i = " UDcoef.i
  853.   end
  854. /* trace ?r */
  855. newequation = Lstuff
  856. LHS = " "
  857. do kk=1 to #Lterms
  858.   LHS = LHS||" "||word(newequation,kk)
  859. end
  860. RHS = " "
  861. do kk=1 to #Rterms
  862.   RHS = RHS||" "||word(newequation,kk+#Lterms)
  863. end
  864. select
  865.   when UDCcount = 0 then do
  866.        UDCineffect = false
  867.        doing_negative_value = false
  868.        end
  869.   when UDCcount = 1 then do /**/
  870.     UDCineffect = false
  871.     say
  872.     say "If you use any user-defined coefficient then"
  873.     say "use at least two."
  874.     say
  875.     signal restart
  876.     end /**/
  877.   otherwise do
  878. /*  If words(eq)=3 then do
  879.       
  880.       say "You must solve this easy one without UDCs."
  881.       say
  882.       UDCineffect = false
  883.       signal restart
  884.       end
  885.   else do*/
  886.       UDCineffect = true
  887.       doing_negative_values = true
  888.     end
  889. end /* select */
  890. newequation = LHS||" "||RHS
  891.  
  892. return /* from process_for_UDCs */
  893.  
  894.  
  895. /*************************************************************/
  896. /*       process     process      process      process       */
  897. /*************************************************************/
  898.  
  899.  
  900. process:
  901.  
  902. if errortesting then say "entering 'process'"
  903.  
  904. /* This procedure removes the underscores to parens and to brackets but leaves the underscores to the individual elements for another procedure.                                         */
  905.  
  906.  
  907. /* if ~UDCineffect then trace ?r */
  908.  
  909. terms    = arg(1)
  910. ith_side = arg(2)
  911. #terms   = words(terms)
  912. If errortesting then say "terms = " terms
  913. /*  trace ?r  */
  914. do @w=1 to #terms
  915.    term = word(terms,@w)
  916.    do while pos(")",term) > 0
  917.       call process_parens
  918.       end
  919.    do while pos("]",term) > 0
  920.       call process_brackets
  921.       end
  922.    if ~UDCineffect then do /**/
  923.      do while index(term,"_") > 0 /***/
  924.         if errortesting then do
  925.           say "@w =  " @w
  926.           say "term_w = " term
  927.           say
  928.         end
  929.         call remove_underscore(term,ith_side,@w)
  930.         if Lterm ~= "" then
  931.              call process_residue(Lterm,ith_side,@w)
  932.       end /***/
  933.       if term ~= "" then
  934.          call process_residue(term,ith_side,@w)
  935.    end  /**/
  936. end
  937.  
  938.  
  939. return /* from: process */
  940.  
  941.  
  942.  
  943. /*************************************************************/
  944. /*     process_parens    process_parens   process_parens     */
  945. /*************************************************************/
  946.  
  947.  
  948. process_parens:
  949.  
  950. /*  trace ?r */
  951. if errortesting then say "entering 'process_parens'"
  952.  
  953. /* elegant!!! */
  954. parse var term Lstuff "(" innerstuff ")_" Rstuff
  955. factor = ""
  956. original_innerstuff = innerstuff
  957. do while pos(left(Rstuff,1),digits) > 0
  958.   factor = factor||left(Rstuff,1)
  959.   Rstuff = right(Rstuff,length(Rstuff)-1)
  960. end 
  961.  
  962. changes_were_made = true
  963. do while changes_were_made
  964.   changes_were_made = false
  965.   innerstufflength = length(innerstuff)
  966.   do t=1 to innerstufflength-1
  967.     if pos(upper(char(innerstuff,t)),upperletters) > 0
  968.      then do
  969.       nextchar! = char(innerstuff,t+1)
  970.       if nextchar! ~="_" & pos(nextchar!,lowerletters) = 0
  971.        then do
  972.           innerstuff = insert("_1",innerstuff,t)
  973.           changes_were_made = true
  974.         end
  975.      end
  976.   end
  977. end
  978.  
  979. if pos(upper(char(innerstuff,length(innerstuff))),upperletters)>0
  980.  then innerstuff = innerstuff || "_1"
  981. /*  trace ?r  */
  982. more_underscores! = true
  983. pos_last_underscore = 0
  984. do while more_underscores! /**/
  985.   pos_underscore = pos("_",innerstuff,pos_last_underscore+1)
  986.   if pos_underscore=0 then more_underscores! = false
  987.     else do /***/
  988.       pos_last_underscore = pos_underscore
  989.       more_digits! = true
  990.       subscript = ""
  991.       do count!=1 to length(innerstuff) while more_digits! /****/
  992.          if pos_underscore+count! > length(innerstuff)
  993.              then do
  994.                more_digits! = false
  995.                more_underscores! = false
  996.                end
  997.           else do /*****/
  998.              negpresent = false
  999.              char! = char(innerstuff,pos_underscore+count!)
  1000.              if pos(char!,digits)=0 then do
  1001.                 more_digits! = false
  1002.                 say "        Hi there!"
  1003.                 end
  1004.               else
  1005.                 subscript=subscript||char!
  1006.           end /*****/
  1007.           if negpresent then say "subscript = " subscript
  1008.        end /****/
  1009.      end /***/
  1010.   newsubscript = convert!(factor*subscript)
  1011.   innerstuff=left(innerstuff,pos_underscore),
  1012.    ||newsubscript||right(innerstuff,length(innerstuff),
  1013.                          -pos_underscore-length(subscript))
  1014.  
  1015. if errortesting then say " innerstuff = " innerstuff
  1016. end /**/
  1017.  
  1018.  
  1019. term = Lstuff||innerstuff||Rstuff
  1020. if doing_negative_values & UDCineffect then do /**/
  1021.   select
  1022.     when ith_side = 1 then do /************/
  1023.      /*  trace ?r */
  1024.        if @w=1 then do /***/
  1025.           LHSprime = term
  1026.           if #terms > 1 then
  1027.             do p=2 to #terms
  1028.               LHSprime = LHSprime||" "||word(LHS,p)
  1029.             end
  1030.           end /***/
  1031.         else do /*^*/
  1032.           do p=1 to @w-1
  1033.             LHSprime = LHSprime||" "||word(LHS,p)
  1034.           end
  1035.           LHSprime = LHSprime||" "||term
  1036.           if #terms > @w then
  1037.           do p=@w to #terms
  1038.             LHSprime = LHSprime||" "||word(LHS,p)
  1039.           end
  1040.         end /*^*/
  1041.       LHS = LHSprime
  1042.       if errortesting then say "LHSprime = " LHS
  1043.     end/************/
  1044.   when ith_side = 2 then do/************/
  1045.      /*  trace ?r */
  1046.        if @w=1 then do /***/
  1047.           RHSprime = term
  1048.           if #terms > 1 then
  1049.             do p=2 to #terms
  1050.               RHSprime = RHSprime||" "||word(RHS,p)
  1051.             end
  1052.           end /***/
  1053.         else do /*^*/
  1054.           RHSprime = ""
  1055.           do p=1 to @w-1
  1056.             RHSprime = RHSprime||" "||word(RHS,p)
  1057.           end
  1058.           RHSprime = RHSprime||" "||term
  1059.           if #terms > @w then
  1060.           do p=@w to #terms
  1061.             RHSprime = RHSprime||" "||word(RHS,p)
  1062.           end
  1063.         end /*^*/
  1064.       RHS = RHSprime
  1065.       if errortesting then say "RHSprime = " RHS
  1066.   end /*************/
  1067.   end /* select */
  1068. end
  1069. return /* from: process_parens */
  1070.  
  1071.  
  1072.  
  1073.  
  1074.  
  1075. /*************************************************************/
  1076. /*   process_brackets  process_brackets  process_brackets    */
  1077. /*************************************************************/
  1078.  
  1079.  
  1080. process_brackets:
  1081.  
  1082. /*  trace ?r */
  1083. if errortesting then say "entering 'process_parens'"
  1084.  
  1085.  
  1086. parse var term Lstuff "[" innerstuff "]_" Rstuff
  1087. factor = left(Rstuff,1)
  1088. Rlen = length(Rstuff)
  1089.  
  1090. Rstuff = right(Rstuff,Rlen-1)
  1091. changes_were_made = true
  1092. do while changes_were_made
  1093.   changes_were_made = false
  1094.   innerstufflength = length(innerstuff)
  1095.   do t=1 to innerstufflength-1
  1096.     if pos(upper(char(innerstuff,t)),upperletters) > 0
  1097.      then do
  1098.       nextchar! = char(innerstuff,t+1)
  1099.       if nextchar! ~="_" & pos(nextchar!,lowerletters) = 0
  1100.        then do
  1101.           innerstuff = insert("_1",innerstuff,t)
  1102.           changes_were_made = true
  1103.         end
  1104.      end
  1105.   end
  1106. end
  1107.  
  1108. if pos(upper(char(innerstuff,length(innerstuff))),upperletters)>0
  1109.  then innerstuff = innerstuff || "_1"
  1110. /*  trace ?r  */
  1111. more_underscores! = true
  1112. pos_last_underscore = 0
  1113. do while more_underscores! /**/
  1114.   pos_underscore = pos("_",innerstuff,pos_last_underscore+1)
  1115.   if pos_underscore=0 then more_underscores! = false
  1116.     else do /***/
  1117.       pos_last_underscore = pos_underscore
  1118.       more_digits! = true
  1119.       subscript = ""
  1120.       do count!=1 to length(innerstuff) while more_digits! /****/
  1121.          if pos_underscore+count! > length(innerstuff)
  1122.              then do
  1123.                more_digits! = false
  1124.                more_underscores! = false
  1125.                end
  1126.           else do /*****/
  1127.              char! = char(innerstuff,pos_underscore+count!)
  1128.              if pos(char!,digits)=0 then
  1129.                 more_digits! = false
  1130.               else
  1131.                 subscript=subscript||char!
  1132.           end /*****/
  1133.        end /****/
  1134.      end /***/
  1135.   newsubscript = "-"||convert!(factor*subscript)
  1136.   innerstuff=left(innerstuff,pos_underscore),
  1137.       ||newsubscript||right(innerstuff,length(innerstuff),
  1138.                         -pos_underscore-length(subscript))
  1139.  
  1140. if errortesting then say " innerstuff = " innerstuff
  1141. end /**/
  1142.  
  1143. term = Lstuff || innerstuff || Rstuff
  1144.  
  1145. return /* from: process_brackets */
  1146.  
  1147.  
  1148.  
  1149.  
  1150. /*************************************************************/
  1151. /*      convert!     convert!     convert!     convert!      */
  1152. /*************************************************************/
  1153.  
  1154.  
  1155. convert!: procedure
  1156.  
  1157. num = arg(1)
  1158. /* trace ?r */
  1159. select
  1160.   when num > 0 & num < 10 then do
  1161.        digit.1 = num
  1162.        digit.2 = "NIL"
  1163.        end
  1164.   when num >= 10 & num < 100 then do
  1165.        x = num//10
  1166.        y = trunc(num/10)
  1167.        digit.1 = x
  1168.        digit.2 = y
  1169.        digit.3 = "NIL"
  1170.        end
  1171.   when num >= 100 & num < 1000 then do
  1172.        z = trunc(num/100)
  1173.        y = trunc((num - 100*z)/10)
  1174.        x = num - 100*z - 10*y
  1175.        digit.1 = x
  1176.        digit.2 = y
  1177.        digit.3 = z
  1178.        digit.4 = "NIL"
  1179.        end
  1180.   otherwise do
  1181.        say
  1182.        say "Subscript value outside of valid range: 1--999"
  1183.        say
  1184.        signal restart; end
  1185. end
  1186.  
  1187. i = 1
  1188. rebuilt = ""
  1189. do until digit.i="NIL"
  1190.   rebuilt = digit.i || rebuilt
  1191.   i = i + 1
  1192. end
  1193.  
  1194. return rebuilt /* from: convert! */
  1195.  
  1196.  
  1197.  
  1198.  
  1199.  
  1200. /*************************************************************/
  1201. /*         remove_underscore        remove_underscore        */
  1202. /*************************************************************/
  1203.  
  1204.  
  1205. remove_underscore:
  1206.  
  1207. if errortesting then say "entering 'remove_underscores'"
  1208. /*  trace ?r */
  1209. _term  = arg(1)
  1210. _i     = arg(2)
  1211. _term# = arg(3)
  1212.  
  1213. parse var _term Lterm"_"Rterm
  1214. char = right(Lterm,1)
  1215. if upper(char) ~= char then do
  1216.  
  1217.  
  1218. next_element = right(Lterm,2)
  1219.         Lterm = left(Lterm,length(Lterm)-2) /* residue */
  1220.         end
  1221.      else do
  1222.         next_element = right(Lterm,1)
  1223.         Lterm = left(Lterm,length(Lterm)-1) /* residue */
  1224.      end
  1225.      
  1226. operant_term = Rterm
  1227. nextchar = left(operant_term,1)
  1228. #digits = 0
  1229. signal off novalue /*******************/
  1230. do while index('-0123456789',nextchar) > 0 then
  1231.    #digits = #digits + 1
  1232.    operant_term = right(operant_term,length(operant_term)-1)
  1233.    nextchar = left(operant_term,1)
  1234. end
  1235. signal on novalue
  1236. subscript = left(Rterm,#digits)
  1237.  
  1238. call add_to_elements_list(next_element,subscript,_i,_term#)
  1239. term = right(Rterm,length(Rterm)-#digits)
  1240.  
  1241. return /* from: remove_underscore */
  1242.  
  1243.  
  1244. /*************************************************************/
  1245. /*    process_residue   process_residue   process_residue    */
  1246. /*************************************************************/
  1247.  
  1248.  
  1249. process_residue:
  1250.  
  1251. if errortesting then say "entering 'process_residue'"
  1252. /*  trace ?r */
  1253.  
  1254. residue     = arg(1)
  1255. side_number = arg(2)
  1256. term_no     = arg(3)
  1257.  
  1258. subscript = 1
  1259. do while residue ~= ""
  1260.   len = length(residue)
  1261.   char = right(residue,1)
  1262.   if upper(char) ~= char then do
  1263.         next_element = right(residue,2)
  1264.         residue = left(residue,len-2)
  1265.       end
  1266.     else do
  1267.         next_element = right(residue,1)
  1268.         residue = left(residue,len-1)
  1269.     end
  1270.   next_el = next_element
  1271.   sub = subscript
  1272.   call add_to_elements_list(next_el,sub,side_number,term_no)
  1273. end
  1274. return /* from: process_residue */
  1275.  
  1276.  
  1277.  
  1278.  
  1279. /*************************************************************/
  1280. /*      add_to_elements_list         add_to_elements_list    */
  1281. /*************************************************************/
  1282.  
  1283.  
  1284. add_to_elements_list:
  1285.  
  1286.  
  1287. if errortesting then say "entering 'add_to_elements_list'"
  1288.  
  1289. /* As the function 'process_residue' goes element by element thru the terms of both sides of the equation, it sends the element and cound (from the subscript) to this function, which adds that count to the appropriate entry of the coefficient matrix (say C for symbolic representation) to be used to solve for x the matix equation Cx = const., where const. is a 'column' matrix.                                                      */
  1290.  
  1291. /*  trace ?r */
  1292.  
  1293. @element    = arg(1)
  1294. @q          = arg(2)
  1295. which_side  = arg(3)
  1296. @term#      = arg(4)
  1297.  
  1298. if errortesting then do
  1299.   say "@element    = " arg(1)
  1300.   say "@q          = " arg(2)
  1301.   say "which_side  = " arg(3)
  1302.   say "@term#      = " arg(4)
  1303. end
  1304. elementfound = false
  1305.  
  1306. q = what_side.which_side.term_list.@term#.element_count
  1307.  
  1308. if q > 0 then
  1309.  do y=1 to q while ~elementfound
  1310.    if errortesting then
  1311.       say "side.which_side.term_pos.@term#.element.y = ",
  1312.         side.which_side.term_pos.@term#.element.y
  1313.    if side.which_side.term_pos.@term#.element.y = @element
  1314.     then do
  1315.      $c = side.which_side.term_pos.@term#.element.y.count
  1316.      side.which_side.term_pos.@term#.element.y.count = $c + @q
  1317.      if errortesting then do
  1318.         say "                 $c = " $c
  1319.         say "                 @q = " @q
  1320.         say "      element count = " $c+@q
  1321.        end
  1322.      elementfound = true
  1323.      if y=q then do
  1324.        #nil_el = y + 1
  1325.        side.which_side.term_pos.@term#.element.#nil_el='NIL'
  1326.        side.which_side.term_pos.@term#.element.#nil_el.count=0
  1327.      end
  1328.    end
  1329.  end
  1330. /* trace ?r */
  1331. if ~elementfound then do
  1332.  /* Element not found in i,jth term: add it to term */
  1333.     q = q + 1
  1334.     what_side.which_side.term_list.@term#.element_count = q
  1335.     side.which_side.term_pos.@term#.element.q = @element
  1336.     side.which_side.term_pos.@term#.element.q.count = @q
  1337.     #nil_el = q + 1
  1338.     side.which_side.term_pos.@term#.element.#nil_el = 'NIL'
  1339. end
  1340. elements_list =  elements_list.@side.which_side
  1341. if errortesting then do 
  1342.     say
  1343.     say "add to elements list:"
  1344.     say "elements_list = " elements_list.@side.which_side
  1345.   end
  1346. w = which_side
  1347. if compress(elements_list.@side.w)="" then do/*.....*/
  1348.    #elements.@side.w = #elements.@side.w + 1
  1349.    elements_list.@side.w=elements_list.@side.w||" "||@element
  1350.    if errortesting then do
  1351.       say "elements_list = " elements_list.@side.w
  1352.       say "No. of elements = " #elements.@side.w
  1353.       end
  1354.     end/*.....*/
  1355.   else
  1356.      do/**/
  1357.        /* trace ?r */
  1358.        elementfound = false
  1359.        do z=1 to words(elements_list.@side.w) while ~elementfound
  1360.          if @element~=word(elements_list.@side.w,z) then iterate
  1361.             else  elementfound = true
  1362.        end
  1363.        if ~elementfound then  call add_element()
  1364.      end/**/
  1365.  
  1366. return /* from: add_to_elements_list */
  1367.  
  1368.  
  1369. add_element:
  1370.  
  1371. #elements.@side.w = #elements.@side.w + 1
  1372. elements_list.@side.w=elements_list.@side.w,
  1373.      ||" "||@element
  1374. if errortesting then do
  1375.     say "elements_list = " elements_list.@side.w
  1376.     say "No. of elements = " #elements.@side.w
  1377.   end
  1378. return
  1379.  
  1380.  
  1381.  
  1382.  
  1383.  
  1384. /*************************************************************/
  1385. /*   calculate_answer   calculate_answer   calculate_answer  */
  1386. /*************************************************************/
  1387.  
  1388.  
  1389. calculate_answer:/* This uses Cramer's rule. */
  1390.  
  1391. if errortesting then say "entering 'calculate answer'"
  1392.  
  1393. /* trace ?r */
  1394.  
  1395. if ~doing_negative_values then
  1396.   elements_list = original_elements_list
  1397. #equations_needed = words(LHS) + words(RHS) - 1 
  1398. #elements = words(elements_list)
  1399. if errortesting then do
  1400.   say "#elements = " #elements
  1401.   say "elements_list = " elements_list
  1402. end
  1403. if #equations_needed > #elements then
  1404.   if ~doing_negative_values then
  1405.    do
  1406.     if ~looking_for_nonregular_solutions then
  1407.        signal decide_on_nonregular()
  1408.    end
  1409. /*  trace ?r */
  1410.  rrr = 0
  1411.  do for #equations_needed/**/
  1412.     rrr = rrr + 1
  1413.     current_element = word(elements_list,rrr)
  1414.     if errortesting then
  1415.          say "elements_list = " elements_list
  1416.     do ii=1 to 2 /***/
  1417.       if ii=1 then number_terms = #LH_terms
  1418.             else number_terms = #RH_terms
  1419.       do jj=1 to number_terms /****/
  1420.         if ii=1 then pp = jj 
  1421.            else  pp = jj + #LH_terms
  1422.         elementfound = false
  1423.         more_elements = true
  1424.         kk = 0
  1425.         do while ~elementfound & more_elements  /*****/
  1426.            kk = kk + 1
  1427.            if errortesting then do
  1428.               say "kth element i,jth term: "
  1429.               say  side.ii.term_pos.jj.element.kk
  1430.             end 
  1431.            zz = side.ii.term_pos.jj.element.kk
  1432.            if zz='NIL' then  more_elements = false
  1433.              else do
  1434.               zz_count = side.ii.term_pos.jj.element.kk.count
  1435.               if zz=current_element then do
  1436.                  coef.row.rrr.column.pp = (-1)**(ii+1)*zz_count
  1437.                  elementfound = true
  1438.                  end
  1439.              end
  1440.          end/*****/
  1441.          if ~elementfound then coef.row.rrr.column.pp = 0
  1442.       end/****/
  1443.     end/***/
  1444. end/**/
  1445. select
  1446.    when #equations_needed=1 then call calculate1
  1447.    otherwise call calculate(#equations_needed)
  1448. end
  1449.  
  1450. return /* from: calculate_answer */
  1451.  
  1452. decide_on_nonregular:
  1453. say 
  1454. say "This equation has too few elements, compared to"
  1455. say "the no. of terms present, to determine the coef's."
  1456. say "Algorithm needs at least as many as one less"
  1457. say "than the total number of terms on both sides."
  1458. say 
  1459. say "If you want computer to try to solve this then enter"
  1460. say "the start and finish integers (1 <= min <= max <= 99)."
  1461. say "Enter just the minimum number for all coefficients if you prefer."
  1462. say "Or to do it yourself, hit return and re-enter the"
  1463. say "equation with 2 or more coefficients."
  1464. say
  1465. pull response
  1466. /* trace ?r */
  1467. if response = "" then signal restart
  1468.   else do /**/
  1469.     looking_for_nonregular_solutions = true
  1470.     select
  1471.       when words(response)=1 then
  1472.         do /***/
  1473.          /* trace ?r */
  1474.          strip_response = word(response,1)
  1475.          if datatype(strip_response)='NUM' then
  1476.             do /****/
  1477.              if strip_response>0 & strip_response<100 then
  1478.                 do
  1479.                   $max = strip_response
  1480.                   $min = 1
  1481.                 end
  1482.                else
  1483.                  do $max = 30; $min = 1; break; end
  1484.             end /****/
  1485.           else
  1486.             do $max = 30; $min = 1; break; end
  1487.         end /***/
  1488.       when words(response)=1 | words(response)=2 then
  1489.         do /* smile */
  1490.           response1 = word(response,1)
  1491.           response2 = word(response,2)
  1492.           if datatype(response1)='NUM' then do /*+*/
  1493.             if datatype(response2)='NUM' then
  1494.               select
  1495.                   when response1>0 & response1<100 then
  1496.                   do
  1497.                     $min = response1
  1498.                     if response2>=response1 & response2<100 then
  1499.                         $max = response2
  1500.                       else $max = $min + 30
  1501.                   end
  1502.                 otherwise
  1503.                  do $min = 1; $max = 30; break; end
  1504.                end /* select */
  1505.              else
  1506.                do $min = 1; $max = 30; break; end
  1507.           end /*+*/
  1508.         end /* smile */
  1509.       otherwise do $min = 1; $max = 30; break; end
  1510.     end /* select */
  1511.   end /**/
  1512. call find_nonregular_solutions($min,$max)
  1513. looking_for_nonregular_solutions = false
  1514. signal restart
  1515. return
  1516.  
  1517. /*************************************************************/
  1518. /*   calculate1    calculate1    calculate1    calculate1    */
  1519. /*************************************************************/
  1520.  
  1521.  
  1522. calculate1:
  1523.  
  1524. /*  trace ?r */
  1525.  
  1526.  
  1527. if errortesting then say "entering 'calculate1'"
  1528.  
  1529. xx.1 = trunc(1.0*coef.row.1.column.2)
  1530. xx.2 = trunc(1.0*coef.row.1.column.1)
  1531.  
  1532. return /* from: calculate1 */
  1533.  
  1534.  
  1535.  
  1536.  
  1537. /*************************************************************/
  1538. /*   calculate     calculate     calculate     calculate     */
  1539. /*************************************************************/
  1540.  
  1541.  
  1542. calculate: /*procedure expose coef. xx. errortesting true false*/
  1543.  
  1544. /*  trace ?r */
  1545.  
  1546. #eqs_needed = arg(1)
  1547.  
  1548.  
  1549. say "entering calculate" #eqs_needed
  1550.  
  1551.   coeff = ""
  1552.   do i=1 to #eqs_needed
  1553.     do j=1 to #eqs_needed
  1554.       coeff = coeff||coef.row.i.column.j||" "
  1555.     end
  1556.   end
  1557.  
  1558. detmnt = trunc(det(#eqs_needed,coeff))
  1559. interpret "detmnt"#eqs_needed "= detmnt"
  1560.  
  1561. if errortesting then say "det = " detmnt
  1562.  
  1563. do j=1 to #eqs_needed
  1564.     #eqs_plus = #eqs_needed + 1
  1565.     const.j = coef.row.j.column.#eqs_plus
  1566. end
  1567.  
  1568. if detmnt=0 then do /**/
  1569.          call det_err_msg()
  1570.          signal jump
  1571.   end /**/
  1572.   else do
  1573. /*       coeff = ""
  1574.        do i=1 to #eqs_needed
  1575.           do j=1 to #eqs_needed
  1576.             coeff = coeff||m.row.i.column.j||" "
  1577.           end
  1578.         end */
  1579.         do i=1 to #eqs_needed
  1580.           coefficient = coeff
  1581.           do j=1 to #eqs_needed
  1582.             coefficient=,
  1583.               replace(const.j,coefficient,(j-1)*#eqs_needed+i)
  1584.           end
  1585.           xx.i = trunc(det(#eqs_needed,coefficient))
  1586.        end
  1587. jump:
  1588. return /* from: calculate */
  1589.  
  1590.  
  1591.  
  1592.  
  1593.  
  1594.  
  1595. /*************************************************************/
  1596. /*  det_err_msg   det_err_msg   det_err_msg   det_err_msg    */
  1597. /*************************************************************/
  1598.  
  1599. det_err_msg:
  1600.  
  1601. /* trace ?r */
  1602.  
  1603.      say "We have encountered a 'det = 0' problem."
  1604.      say "We'll try cyclic permuting of the elements_list."
  1605.      cycle_count = cycle_count + 1
  1606.      if cycle_count > #total_terms-1 then do
  1607.            say " Sorry, permutation failed."
  1608.            say " It could be that this equation will",
  1609.              "not balance."
  1610.            if doing_negative_values then do
  1611.                say "Try assigning different coefficients."
  1612.                say " "
  1613.                end
  1614.              else signal decide_on_nonregular
  1615.            signal restart
  1616.          end
  1617.        else do /*....*/
  1618.          parse var elements_list  first_term elements_list
  1619.          elements_list = elements_list||" "||first_term
  1620.          say " "
  1621.          say elements_list
  1622.          say
  1623.          original_elements_list = elements_list
  1624.          call calculate_answer
  1625.        end /*....*/
  1626. return
  1627.  
  1628.  
  1629. /*************************************************************/
  1630. /*      det      det      det      det      det      det     */
  1631. /*************************************************************/
  1632.  
  1633. det: procedure
  1634.  
  1635. /*  trace ?r */
  1636. size = arg(1)
  1637. str_of_coefs = arg(2)
  1638. /*say "det " arg(1)*/
  1639.  
  1640. do i=1 to size
  1641.   do j=1 to size
  1642.     coef.row.i.column.j = word(str_of_coefs,(i-1)*size+j)
  1643.   end
  1644. end
  1645.  
  1646. if size=2 then do
  1647.   A = coef.row.1.column.1*coef.row.2.column.2
  1648.   B = coef.row.1.column.2*coef.row.2.column.1
  1649.   return A - B
  1650. end
  1651.  
  1652. /* Here we setup to expand by minors. */
  1653. /*  trace ?r */
  1654. if size>=3 then do 
  1655.     /* The next sections determine which row has the most*/
  1656.         /* zeros on it. */
  1657.     do i=1 to size
  1658.       count.i = 0
  1659.       do j=1 to size
  1660.         if coef.row.i.column.j=0 then
  1661.            count.i = count.i + 1
  1662.       end
  1663.     end
  1664.     index_most_zeros_so_far = 1
  1665.     do i=2 to size
  1666.       if count.i>count.index_most_zeros_so_far
  1667.         then index_most_zeros_so_far = i
  1668.     end
  1669.     expansion_row = index_most_zeros_so_far
  1670.     end
  1671.   else expansion_row = 1
  1672.  
  1673. do nullcolumn=1 to size /**/
  1674.   pivot=coef.row.expansion_row.column.nullcolumn
  1675.   if pivot~=0 then do
  1676.     do i=1 to size-1 /***/
  1677.       if i<expansion_row then row# = i
  1678.             else row# = i+1
  1679.       do j=1 to size-1 /****/
  1680.         if j < nullcolumn then col# = j
  1681.             else col# = j+1
  1682.         m.row.i.col.j=coef.row.row#.column.col#
  1683.       end /****/
  1684.     end /***/
  1685.    /*  trace  ?r */
  1686.     minor = ""
  1687.     do i=1 to size-1
  1688.       do j=1 to size-1
  1689.         minor = minor||m.row.i.col.j||" "
  1690.       end
  1691.     end
  1692.     term.nullcolumn=(-1.0)**(nullcolumn+expansion_row),
  1693.                       *pivot*det(size-1,minor)
  1694.   end
  1695.   else
  1696.     term.nullcolumn=0
  1697. end /**/
  1698. ans = 0
  1699. do i=1 to size
  1700.   ans = ans + term.i
  1701. end
  1702. return ans
  1703.  
  1704.  
  1705.  
  1706.  
  1707.  
  1708.  
  1709. /*************************************************************/
  1710. /*       print_answer      print_answer      print_answer    */
  1711. /*************************************************************/
  1712.  
  1713. print_answer:
  1714.  
  1715. if errortesting then say "entering 'print_answer'"
  1716.  
  1717. /*  trace ?r */
  1718.  
  1719. det# = arg(1)
  1720. if #Lterms+#Rterms >= 7 then two_lines_output=true
  1721.     else two_lines_output=false
  1722. answer = ""
  1723. do q=1 to #total_terms /**/
  1724.     select
  1725.       when q = #LH_terms then post = " = "
  1726.       when q = #total_terms then post = ""
  1727.       otherwise post = " + "
  1728.     end
  1729.     if q <= #LH_terms then do
  1730.         p = q
  1731.         @_side = LHS
  1732.         end
  1733.      else do
  1734.         p = q - #LH_terms
  1735.         @_side = RHS
  1736.      end
  1737.      if #total_terms = 2 then
  1738.            max_count = #total_terms
  1739.        else
  1740.            max_count = #total_terms - 1
  1741.      if (q <= max_count) then
  1742.        if doing_negative_values then do /***/
  1743.         /*  trace ?r  */
  1744.          select
  1745.            when q<=#LH_terms then
  1746.              best_term = word(newequation,q)
  1747.            when q>#LH_terms & q<=#total_terms then do
  1748.              adjustment = #originally_on_left - #LH_terms
  1749.              best_term = word(newequation,q+adjustment)
  1750.              end
  1751.            otherwise
  1752.              best_term = word(@_side,p)
  1753.          end /* select */
  1754.          if (abs(xx.q)=1) then
  1755.              answer = answer || best_term || post
  1756.            else
  1757.               answer = answer||abs(xx.q)||best_term||post
  1758.          end /***/
  1759.        else do
  1760.          /* trace ?r */
  1761.           if (abs(xx.q)=1) then
  1762.              answer = answer || word(@_side,p) || post
  1763.            else
  1764.               answer = answer||abs(xx.q)||word(@_side,p)||post
  1765.         end
  1766. end /**/
  1767. /* trace ?r */
  1768. if det# >= 4 then det = value("detmnt"||det#)
  1769.   else
  1770.    if #equations_needed=1 then
  1771.       det = 1
  1772.     else
  1773.       det = detmnt
  1774.  
  1775. /* trace ?r */
  1776. if abs(det)=1 then
  1777.        answer = answer || word(RHS,#RH_terms)
  1778.    else
  1779.        answer = answer || abs(det) || word(RHS,#RH_terms)
  1780. if #LH_terms = 0 then answer = " = "||answer
  1781. say " "
  1782. say "Balanced equation (?) :    "
  1783. say
  1784. if ~two_lines_output then say answer
  1785.   else do
  1786.     parse var answer newL "=" newR
  1787.     say " "newL "= "
  1788.     say "      " newR
  1789.   end
  1790. say " "
  1791. say " "
  1792.  
  1793. /* Here we try to reduce the equation if possible */
  1794. do z=1 to 1
  1795.   xxx.1 = abs(xx.1)
  1796.   xxx.2 = abs(xx.2)
  1797.   select
  1798.     when #total_terms=2 then NOP
  1799.     when #total_terms=3 then do
  1800.       xxx.3 = abs(det)
  1801.       end
  1802.     when #total_terms=4 then do
  1803.       xxx.3 = abs(xx.3)
  1804.       xxx.4 = abs(det)
  1805.       end
  1806.     when #total_terms=5 then do
  1807.       xxx.3 = abs(xx.3)
  1808.       xxx.4 = abs(xx.4)
  1809.       xxx.5 = abs(det)
  1810.       end
  1811.     otherwise do
  1812.         do m=3 to #total_terms-1
  1813.           xxx.m = abs(xx.m)
  1814.         end
  1815.         xxx.#total_terms = abs(det)
  1816.       end
  1817.   end /* select */
  1818.  
  1819.  
  1820.   @min_value = min_value()
  1821.   call reduce_eq(@min_value)
  1822.  
  1823.   answer = ""
  1824.   max_count = #total_terms /* - 1 */
  1825.   do q=1 to max_count
  1826.       select
  1827.         when q = #LH_terms then post = " = "
  1828.         when q = max_count then post = ""
  1829.         otherwise post = " + "
  1830.       end
  1831.       if q <= #LH_terms then do
  1832.           p = q
  1833.           @_side = LHS!
  1834.           end
  1835.        else do
  1836.           p = q - #LH_terms
  1837.           @_side = RHS!
  1838.        end
  1839.        if doing_negative_values then do /***/
  1840.          select
  1841.            when q<=#LH_terms then
  1842.              best_term = word(newequation,q)
  1843.            when q>#LH_terms & q<#total_terms then do
  1844.              adjustment = #originally_on_left - #LH_terms
  1845.              best_term = word(newequation,q+adjustment)
  1846.              end
  1847.            otherwise
  1848.              best_term = word(@_side,p)
  1849.          end /* select */
  1850.          if xxx.q=1 then
  1851.              answer = answer || best_term || post
  1852.            else
  1853.               answer = answer||xxx.q||best_term||post
  1854.          end /***/
  1855.        else
  1856.          if xxx.q=1 then
  1857.              answer = answer || word(@_side,p) || post
  1858.            else
  1859.              answer = answer || xxx.q || word(@_side,p) || post
  1860.   end
  1861.   if #LH_terms=0 then answer = " = "||answer
  1862.   say 
  1863.   say "Reduced equation (?) :    "
  1864.   say
  1865.    if ~two_lines_output then say " " answer
  1866.     else do
  1867.       parse var answer newL "=" newR
  1868.       say " "newL "= "
  1869.       say "      " newR
  1870.     end
  1871.   say 
  1872.   say 
  1873. /*  trace ?r */
  1874.   if looking_for_nonregular_solutions then
  1875.       signal verify_solution
  1876. end
  1877.  
  1878. return /* from: print_answer */
  1879.  
  1880.  
  1881.  
  1882. /*************************************************************/
  1883. /*   reduce_eq     reduce_eq     reduce_eq     reduce_eq     */
  1884. /*************************************************************/
  1885.  
  1886.  
  1887. reduce_eq: procedure expose xxx. #total_terms true false errortesting
  1888.  
  1889. /* trace ?r */
  1890.  
  1891.  
  1892. if errortesting then say "Entering `reduce_eq'"
  1893.  
  1894. min_value = arg(1)
  1895. /* say "min_value = " min_value */
  1896. do n=1 to min_value /**/
  1897.   if errortesting then say "  n = " n 
  1898.   i = 1
  1899.   do while i <= min_value/2+1
  1900.  /***/
  1901.     matchfailed = false
  1902.     i = i + 1
  1903.     if errortesting then      say "     i = " i 
  1904.     do j=1 to #total_terms while ~matchfailed
  1905.       if i * trunc(1.0*(xxx.j/i))~=xxx.j then matchfailed = true
  1906.     end
  1907.     if ~matchfailed then
  1908.       do k=1 to #total_terms /****/
  1909.           xxx.k = xxx.k/i
  1910.       end /****/
  1911.   end /***/
  1912. end /**/
  1913. return /* from: reduce_eq */
  1914.  
  1915.  
  1916. /*************************************************************/
  1917. /*     min_value    min_value     min_value     min_value    */
  1918. /*************************************************************/
  1919.  
  1920. min_value: 
  1921.  
  1922. if errortesting then say "Entering 'min_value'"
  1923.  
  1924. /*  trace ?r  */
  1925.  
  1926. select
  1927.    when #total_terms=2 then
  1928.        min_val = min(xxx.1,xxx.2)
  1929.    when #total_terms=3 then
  1930.        min_val = min(xxx.1,xxx.2,xxx.3)
  1931.    when #total_terms=4 then
  1932.        min_val = min(xxx.1,xxx.2,xxx.3,xxx.4)
  1933.    when #total_terms=5 then
  1934.        min_val = min(xxx.1,xxx.2,xxx.3,xxx.4,xxx.5)
  1935.    when #total_terms=6 then
  1936.        min_val = min(xxx.1,xxx.2,xxx.3,xxx.4,xxx.5,xxx.6)
  1937.    when #total_terms=7 then
  1938.        min_val = min(xxx.1,xxx.2,xxx.3,xxx.4,xxx.5,xxx.6,xxx.7)
  1939.    when #total_terms=8 then do
  1940.         /* trace ?r */
  1941.        min_val =,
  1942.          min(xxx.1,xxx.2,xxx.3,xxx.4,xxx.5,xxx.6,xxx.7,xxx.8)
  1943.        end
  1944.    when #total_terms=9 then
  1945.      min_val =,
  1946.       min(xxx.1,xxx.2,xxx.3,xxx.4,xxx.5,xxx.6,xxx.7,xxx.8,xxx.9)
  1947.   when #total_terms=10 then
  1948.      min_val=min(xxx.1,xxx.2,,
  1949.         xxx.3,xxx.4,xxx.5,xxx.6,xxx.7,xxx.8,xxx.9,xxx.10)
  1950.  when #total_terms=20 then do
  1951.      min_val1 = min(xxx.1,xxx.2,xxx.3,xxx.4,xxx.5)
  1952.      min_val2 = min(xxx.6,xxx.7,xxx.8,xxx.9,xxx.10)
  1953.      min_val3 = min(xxx.11,xxx.12,xxx.13,xxx.14,xxx.15)
  1954.      min_val4 = min(xxx.16,xxx.17,xxx.18,xxx.19,xxx.20)
  1955.      min_val = min(min_val1,min_val2,min_val3,min_val4)
  1956.      end
  1957.    otherwise do
  1958.       say "Sorry, only nine terms max for input!"
  1959.       signal restart
  1960.    end
  1961. end
  1962.  
  1963. return min_val
  1964.  
  1965.  
  1966.  
  1967. /*************************************************************/
  1968. /*    #elements_side     #elements_side     #elements_side   */
  1969. /*************************************************************/
  1970.  
  1971.  
  1972.  
  1973. #elements_side:
  1974.  
  1975. if errortesting then say "Entering #elements_side."
  1976.  
  1977. /*  trace ?r */
  1978.  
  1979. side# = arg(1)
  1980.  
  1981. if side# = 1 then #terms = words(LHS)
  1982.     else #terms = words(RHS)
  1983. sum = 0
  1984. do ii=1 to #terms /**/
  1985.   more_elements_in_term = true
  1986.   jj = 1
  1987.   do while more_elements_in_term /***/
  1988.     if side.side#.term_pos.ii.element.jj=currentelement then do
  1989.       if side#=1 then kk = ii
  1990.            else kk = ii + words(LHS)
  1991.       sum = sum+side.side#.term_pos.ii.element.jj.count*(xxx.kk)
  1992.     end
  1993.     jj = jj + 1
  1994.     if side.side#.term_pos.ii.element.jj = "NIL" then
  1995.          more_elements_in_term = false
  1996.   end /***/
  1997. end /**/
  1998. if errortesting then do
  1999.   say
  2000.   say "sum = " sum " for " currentelement
  2001. end
  2002. return sum /* from: #elements_side */
  2003.  
  2004.  
  2005.  
  2006. /*************************************************************/
  2007. /*          verify_solution          verify_solution         */
  2008. /*************************************************************/
  2009.  
  2010.  
  2011.  
  2012. verify_solution:
  2013.  
  2014. if errortesting then say "Entering verify_solution."
  2015.  
  2016. matchfailed = false
  2017.  
  2018. /*  trace ?r */
  2019. /* call print_original_equation */
  2020. if ~doing_negative_values then
  2021.   if ~looking_for_nonregular_solutions then
  2022.      elements_list=original_elements_list
  2023. #elements = words(elements_list)
  2024. if errortesting then say "element list = " elements_list
  2025.  
  2026. zero_found = false /* zero coefficient */
  2027. do z=1 to #elements while ~zero_found
  2028.   if xxx.z=0 then do
  2029.     zero_found = true
  2030.     matchfailed = true
  2031.   end
  2032. end
  2033.  
  2034. if ~matchfailed then
  2035.   do i=1 to #elements  while ~matchfailed 
  2036.     currentelement = word(elements_list,i)
  2037.     if #elements_side(1)~=#elements_side(2) then 
  2038.         matchfailed = true
  2039.   end
  2040. if ~matchfailed then do
  2041.       say "Solution is verified."
  2042.       say
  2043.       say
  2044.       if doing_negative_values then call print_original_equation
  2045.       end
  2046.    else do
  2047.      if looking_for_nonregular_solutions then
  2048.        do
  2049.          /* trace ?r */
  2050.          if doing_for_three then signal return_to_loop_for_three
  2051.            else  signal return_to_loop_for_two
  2052.        end
  2053.      say "Verifying solution:"
  2054.      say
  2055.      say "Sorry! Verification FAILED!"
  2056.      
  2057.      say "Solution above failed for element '"currentelement"'."
  2058.      if ~doing_negative_values then do
  2059.         say "Your original equation cannot be balanced!!"
  2060.         say
  2061.  
  2062.         end
  2063.       else do
  2064.         say
  2065.         say "  Your original equation either cannot be balanced"
  2066.         say "    with the coefficients you chose or "
  2067.         say "      you have unbalanced elements!!"
  2068.         say
  2069.         end
  2070.    end
  2071. return /* from: verify_solution */
  2072.  
  2073.  
  2074.  
  2075. /*************************************************************/
  2076. /*     rightmostchar     rightmostchar      rightmostchar    */
  2077. /*************************************************************/
  2078.  
  2079. rightmostchar:
  2080.  
  2081. if pos(right(arg(1),1),arg(2)) > 0 then return 1
  2082. else return 0
  2083.  
  2084.  
  2085.  
  2086. /*************************************************************/
  2087. /*      all_but_rightmostchar      all_but_rightmostchar     */
  2088. /*************************************************************/
  2089.  
  2090. all_but_rightmostchar:
  2091.  
  2092. return left(arg(1),length(arg(1))-1)
  2093.  
  2094.  
  2095.  
  2096. /*************************************************************/
  2097. /*      stripped     stripped     stripped     stripped      */
  2098. /*************************************************************/
  2099.  
  2100. stripped: procedure expose true false
  2101.  
  2102. string = arg(1)
  2103.  
  2104. indigits = xrange("0","9")
  2105. more_digits_to_do = true
  2106. do while string~="" & more_digits_to_do
  2107.   if firstchar(string,indigits) then
  2108.     string = allbutleftchar(string)
  2109.   else
  2110.     more_digits_to_do = false
  2111. end
  2112.  
  2113. return string
  2114.  
  2115.  
  2116.  
  2117.  
  2118. /*************************************************************/
  2119. /*     print_original_equation   print_original_equation     */
  2120. /*************************************************************/
  2121.  
  2122.  
  2123.  
  2124. print_original_equation:
  2125.  
  2126. if errortesting then say "Entering print_original_equation."
  2127. /*  trace ?r */
  2128.  
  2129. if errortesting then say "newequation = " newequation
  2130. #original_total_terms = #originally_on_left+#originally_on_right
  2131. coef_removed = false
  2132. indigits = xrange("0","9")
  2133. parens_exist = (pos("(",answer)>0)
  2134. if #equations_needed=1 then
  2135.      last_coef = xxx.2
  2136.   else
  2137.      last_coef = xxx.#total_terms /* of the revised equation */
  2138.  
  2139. if pos("=",answer)=0 then do
  2140.   LHS = ""
  2141.   RHS = answer
  2142.   end
  2143. else
  2144.   parse var answer LHS "=" RHS
  2145. if errortesting then 
  2146.   say "#originally_on_left = " #originally_on_left
  2147. i = 1
  2148. do while (UDcoef.i~=0) && (i<=#originally_on_left+1)
  2149.   i = i + 1
  2150. end
  2151. if pos("[",RHS) > 0 then morebrackets = true
  2152.   else morebrackets = false
  2153. do while morebrackets
  2154.   parse var RHS RHS "["termtomove "]_"ultraright
  2155.   if ~parens_exist then
  2156.     if ~ coef_removed then do
  2157.       do while rightmostchar(RHS,indigits)
  2158.          RHS = all_but_rightmostchar(RHS)
  2159.       end
  2160.       coef_removed = true
  2161.     end
  2162.   coef = last_coef*subscr(ultraright)
  2163.   if coef = 1 then coef = ""
  2164.   if compress(LHS)="" then do
  2165.     LHS = coef||word(newequation,i)
  2166.     i = i + 1
  2167.     end
  2168.   else do
  2169.     LHS = LHS||" + "||coef||word(newequation,i)
  2170.     i = i + 1
  2171.     end
  2172.   if pos("[",ultraright) = 0 then
  2173.     morebrackets = false
  2174.   RHS = RHS||ultraright
  2175. end
  2176.  
  2177. i = #originally_on_left + 1
  2178. do while UDcoef.i~=0 && i<=#original_total_terms
  2179.   i = i + 1
  2180. end
  2181. if pos("(",RHS) > 0 then moreparens = true
  2182.   else moreparens=false
  2183. counttimesthru = 0
  2184. do while moreparens
  2185.   parse var RHS RHS "("termtofix ")_"ultraright
  2186.   if parens_exist then
  2187.     If ~coef_removed then do
  2188.       do while rightmostchar(RHS,indigits)
  2189.          RHS = all_but_rightmostchar(RHS)
  2190.       end
  2191.       coef_removed = true
  2192.     end
  2193.   counttimesthru = counttimesthru + 1
  2194.   coef = last_coef*subscr(ultraright)
  2195.   if coef = 1 then coef = ""
  2196.   select
  2197.     when counttimesthru=1 then do
  2198.        RHS = RHS||coef||word(newequation,i)||ultraright
  2199.        i = i + 1
  2200.        end
  2201.     otherwise do
  2202.       RHS = RHS||" + "||coef||word(newequation,i)||ultraright
  2203.       i = i + 1
  2204.       end
  2205.   end /* select */
  2206.   if pos("(",ultraright)  = 0 then
  2207.     moreparens = false
  2208. end
  2209.  
  2210. if counttimesthru = 0 then
  2211.   do while rightmostchar(RHS,'+') | rightmostchar(RHS," ")
  2212.      RHS = all_but_rightmostchar(RHS)
  2213.   end
  2214.  
  2215. say "Penultimate equation..."
  2216. say
  2217. say "  " LHS||" = "||RHS
  2218. say
  2219. if doing_negative_values | looking_for_nonregular_solutions then
  2220.    call print_in_original_order()
  2221.  
  2222. return /* from: print_original_equation */
  2223.  
  2224.  
  2225. print_in_original_order:
  2226. previous_eq = LHS||" "||RHS
  2227. call drop_plusses()
  2228. call strip_previous_eq()
  2229. do k=1 to words(stripped_previous_eq)
  2230.   stripped_term.k = word(stripped_previous_eq,k)
  2231. end
  2232. call setup_final_equation()
  2233. say "Last equation (I promise!)..."
  2234. /*  trace ?r */
  2235. final_LHS=term.1
  2236. first_Rterm# = #Lterms+1
  2237. final_RHS = " = "||term.first_Rterm#
  2238. do i=2 to #Lterms
  2239.   final_LHS=final_LHS||" + "term.i
  2240. end
  2241.  
  2242. do i=#Lterms+2 to #total_words
  2243.   final_RHS=final_RHS||" + "term.i
  2244. end
  2245. say
  2246. if #Lterms+#Rterms>=7 then say "   " final_LHS||final_RHS
  2247.   else do
  2248.     say " " final_LHS
  2249.     say "    " final_RHS
  2250.     end
  2251. say
  2252. if looking_for_nonregular_solutions then do
  2253.   call writeln(outputfile,final_LHS||final_RHS)
  2254.   call writeln(outputfile," ")
  2255.   if doing_for_two then
  2256.     signal return_to_loop_for_two
  2257.    else
  2258.     signal return_to_loop_for_three
  2259. end
  2260. return
  2261.  
  2262.  
  2263. drop_plusses:
  2264. if pos("+",previous_eq)>0 then do
  2265.   parse var previous_eq leftstuff "+" rightstuff
  2266.   previous_eq = leftstuff||rightstuff
  2267.   call drop_plusses
  2268. end
  2269. return
  2270.  
  2271.  
  2272. strip_previous_eq:
  2273. eq=" "||previous_eq
  2274. do_once_again:
  2275. do i=2 to length(eq)
  2276.   position=pos(char(eq,i),digits)
  2277.   if position>0 & pos(char(eq,i-1)," ")>0 then do
  2278.     eq=delstr(eq,i,1)
  2279.     signal do_once_again
  2280.     end
  2281. end
  2282. stripped_previous_eq=eq
  2283. return
  2284.  
  2285. setup_final_equation:
  2286. /* trace ?r */
  2287. #words_previous_eq = words(stripped_previous_eq)
  2288. #total_words=words(original_stripped_eq)
  2289. do i=1 to #total_words
  2290.   match_found = false
  2291.   do j=1 to #words_previous_eq while ~match_found
  2292.     if word(original_stripped_eq,i)=stripped_term.j,
  2293.       then do
  2294.         term.i = word(previous_eq,j)
  2295.         match_found = true
  2296.       end
  2297.   end
  2298. end
  2299.  
  2300. return
  2301.  
  2302.  
  2303. /*************************************************************/
  2304. /*     subscr      subscr    subscr     subscr    subscr     */
  2305. /*************************************************************/
  2306.  
  2307.  
  2308.  
  2309. subscr:
  2310.  
  2311. if errortesting then say "Entering subscr"
  2312. /*  trace ?r  */
  2313.  
  2314. rightstuff = arg(1)
  2315. factor = ""
  2316. do while pos(left(rightstuff,1),xrange("0","9"))>0
  2317.   factor = factor||left(rightstuff,1)
  2318.   rightstuff = right(rightstuff,length(rightstuff)-1)
  2319. end
  2320. /* if factor="" then factor = 1 */
  2321. ultraright = rightstuff
  2322.  
  2323. return factor
  2324.     
  2325. /*************************************************************/
  2326. /*                                                           */
  2327. /*      `Nonregular' Functions    `Nonregular' Functions     */
  2328. /*                                                           */
  2329. /*************************************************************/
  2330.  
  2331. find_nonregular_solutions:
  2332.  
  2333. /* Nonregular functions are functions that are used to find nonregular solutions, which are solutions that are not unique for 
  2334. a given unbalanced equation. These solutions are placed in the file `ram:solutions'. */
  2335.  
  2336. /*  trace ?r */
  2337.  
  2338. if errortesting then say "Entering `find_nonregular_solutions'"
  2339.  
  2340. looking_for_nonregular_solutions = true
  2341. bare_eq = LHS!||" "||RHS!
  2342. total#terms = words(bare_eq)
  2343. original_eq = unequation
  2344. outputfile = 'outputfile'
  2345. call open(outputfile,"ram:solutions",'w')
  2346.  
  2347. do indexx=1 to #elements
  2348.   curr_el = word(elements_list,indexx)
  2349.   say "elements_list = " elements_list
  2350.   say "Current element = " curr_el
  2351.   say
  2352.   N = #term_with_element(curr_el)
  2353.   select
  2354.     when N=3 then
  2355.       do
  2356.         call do_for_three(curr_el)
  2357.         say "Move to next element:"
  2358.         say
  2359.         /* trace ?r */
  2360.       end
  2361.     when N=2 then call do_for_two(curr_el)
  2362.     otherwise
  2363.       do
  2364.         say
  2365.         say "Element " curr_el "had term count > 3."
  2366.       end 
  2367.   end /* select */
  2368. end
  2369. looking_for_nonregular_solutions = false
  2370. /* call close(outputfile) */
  2371. signal restart 
  2372. return
  2373.  
  2374.  
  2375.  
  2376.  
  2377. #term_with_element: procedure expose total#terms bare_eq
  2378.  
  2379. cur_el = arg(1)
  2380. count = 0
  2381. do i=1 to total#terms
  2382.   if pos(cur_el,word(bare_eq,i))>0 then count = count + 1
  2383. end
  2384. return count
  2385.  
  2386.  
  2387.  
  2388. do_for_two:
  2389. /*
  2390. if errortesting then say "Entering `do_for_two'"
  2391.  
  2392.  trace ?r 
  2393.  
  2394. doing_for_two   = true
  2395. doing_for_three = false
  2396.  
  2397. first  = term#with(curr_el,0)
  2398. second = term#with(curr_el,first)
  2399. el_found = false
  2400. do z=1 to #elements while ~el_found
  2401.   if side.1.term_pos.first.element.z=curr_el then do
  2402.       el_found = true
  2403.       V1 = side.1.term_pos.first.element.z.count
  2404.     end
  2405. end
  2406. $second = second-#Lterms
  2407. el_found = false
  2408. do z=1 to #elements while ~el_found
  2409.   if side.2.term_pos.$second.element.z=curr_el then
  2410.     do
  2411.       el_found = true
  2412.       V2 = side.2.term_pos.$second.element.z.count
  2413.     end
  2414. end
  2415. interpret "$string2 = " V2"||word(LHS!,first)"
  2416. interpret "$string1 = " V1"||word(RHS!,$second)"
  2417. newLHS = replace(" "||$string2,LHS!,first)
  2418. newRHS = replace(" "||$string1,RHS!,$second)
  2419. $unequation = newLHS||" = "||newRHS
  2420.  
  2421. signal begin_nonregular
  2422.  
  2423. return_to_loop_for_two:
  2424. */
  2425. return
  2426.  
  2427.  
  2428.  
  2429.  
  2430. term#with: procedure expose bare_eq total#terms
  2431.  
  2432. el    = arg(1)
  2433. start = arg(2)+1
  2434. do i=start to total#terms
  2435.   if pos(el,word(bare_eq,i))>0 then return i
  2436. end
  2437.  
  2438.  
  2439.  
  2440.  
  2441. replace: procedure
  2442.  
  2443. new_word  = arg(1)
  2444. str       = arg(2)
  2445. position  = arg(3)
  2446. newstring = ""
  2447. len       = words(str)
  2448. do i=1 to position-1
  2449.   newstring = newstring||" "||word(str,i)
  2450. end
  2451. newstring = newstring||" "||new_word
  2452. do i=position+1 to len
  2453.   newstring = newstring||" "||word(str,i)
  2454. end
  2455. return newstring
  2456.  
  2457.  
  2458.  
  2459.  
  2460.  
  2461. do_for_three:
  2462.  
  2463. if errortesting then say "Entering `do_for_three'"
  2464. /* trace ?r */
  2465. current_el = arg(1)
  2466. call init_values()
  2467. /*  trace ?r */
  2468. rr = $min
  2469. return_to_loop_at_rr:
  2470. if rr<=$max then do /**/
  2471. call second_init_values()
  2472.  /*   trace ?r */
  2473.   ss = 1
  2474.   return_to_loop_at_ss:
  2475.   if ss<=$max then do /****/
  2476.     if ss*middlevalue<total_in_max_term then do /******/
  2477.         ch = char(word(order_by_quantity,1),2)
  2478.         select
  2479.           when ch = '1' then leastvalue = Q1
  2480.           when ch = '2' then leastvalue = Q2
  2481.           when ch = '3' then leastvalue = Q3
  2482.         end
  2483.         tt = 1
  2484.         return_to_loop_for_three:
  2485.         /* trace ?r */
  2486.         say rr ss tt
  2487.         call set_terms_for_both_sides()
  2488.         if tt<=$max then
  2489.           do
  2490.             if tt*leastvalue<total_in_max_term then
  2491.               do
  2492.                 call $calc()
  2493.               end
  2494.             tt = tt + 1
  2495.             signal return_to_loop_for_three
  2496.           end
  2497.         else
  2498.           do
  2499.             ss = ss + 1
  2500.             signal return_to_loop_at_ss
  2501.           end
  2502.     end /******/
  2503.     ss = ss + 1
  2504.     signal return_to_loop_at_ss
  2505.   end /****/
  2506.   rr = rr + 1
  2507.   interpret "MAXV = " max_value$
  2508.   interpret "midval = " middlevalue
  2509.   interpret "leastval = " leastvalue
  2510.   if rr*MAXV <= $max*(midval+leastval) then
  2511.      signal return_to_loop_at_rr
  2512.    else return
  2513. end /**/
  2514. return
  2515.  
  2516.  
  2517.  
  2518.  
  2519. OBQ:
  2520.  
  2521. if Q1<=Q2 then
  2522.    do /**/
  2523.     if Q2<=Q3 then return "Q1 Q2 Q3"
  2524.      else
  2525.       do /***/
  2526.         if Q3<=Q1 then return "Q3 Q1 Q2"
  2527.            else return "Q1 Q3 Q2"
  2528.       end /***/
  2529.    end /**/
  2530.   else
  2531.    if Q3<=Q2 then return "Q3 Q2 Q1"
  2532.      else do
  2533.       if Q1<=Q3 then return "Q2 Q1 Q3"
  2534.          else return "Q2 Q3 Q1"
  2535.       end
  2536.  
  2537.  
  2538.  
  2539. Q: procedure expose side. #elements true false
  2540. $side = arg(1)
  2541. $term = arg(2)
  2542. $el   = arg(3)
  2543.  
  2544. el_found = false
  2545. do z=1 to #elements while ~el_found
  2546.   if side.$side.term_pos.$term.element.z=$el then
  2547.     do
  2548.       el_found = true
  2549.       return side.$side.term_pos.$term.element.z.count
  2550.     end
  2551. end
  2552. return
  2553.  
  2554.  
  2555.  
  2556. init_values:
  2557.  
  2558. /*  trace ?r */
  2559. doing_for_three = true
  2560. doing_for_two   = false
  2561.  
  2562. /*  trace ?r */
  2563. L_el_list =  elements_list.@side.1
  2564. R_el_list =  elements_list.@side.2
  2565. say "R_el_list = " R_el_list
  2566. say "L_el_list = " L_el_list
  2567. position_found = false
  2568. do ii=1 to #elements until position_found
  2569.   if word(L_el_list,ii)=current_el then do
  2570.       Rpos = ii
  2571.       position_found = true
  2572.   end
  2573. end
  2574.  
  2575. first  = term#with(current_el,0)
  2576. second = term#with(current_el,first)
  2577. third  = term#with(current_el,second)
  2578. /* trace ?r */
  2579. Q1     = Q(1,first,current_el)
  2580.  
  2581. if second<=#Lterms then do
  2582.     word#2 = second
  2583.     side#2 = 1
  2584.    end
  2585.   else do
  2586.     word#2 = second - #Lterms
  2587.     side#2 = 2
  2588.    end
  2589. Q2 = Q(side#2,word#2,current_el)
  2590.  $third = third-#Lterms
  2591. Q3 = Q(2,$third,current_el)
  2592. say Q1
  2593. say Q2
  2594. say Q3
  2595. /*  trace ?r */
  2596. order_by_quantity = OBQ()
  2597. interpret "max_value = " word(order_by_quantity,3)
  2598. select
  2599.   when word(order_by_quantity,3)='Q1' then max_value$ = 'Q1'
  2600.   when word(order_by_quantity,3)='Q2' then max_value$ = 'Q2'
  2601.   when word(order_by_quantity,3)='Q3' then max_value$ = 'Q3'
  2602. end
  2603. ch = char(word(order_by_quantity,3),2)
  2604. select
  2605.   when ch = '1' then largestterm = first
  2606.   when ch = '2' then largestterm = second
  2607.   when ch = '3' then largestterm = third
  2608. end
  2609. ch = char(word(order_by_quantity,2),2)
  2610. select
  2611.   when ch = '1' then middleterm = first
  2612.   when ch = '2' then middleterm = second
  2613.   when ch = '3' then middleterm = third
  2614. end
  2615. ch = char(word(order_by_quantity,1),2)
  2616. select
  2617.   when ch = '1' then smallestterm = first
  2618.   when ch = '2' then smallestterm = second
  2619.   when ch = '3' then smallestterm = third
  2620. end
  2621. return
  2622.  
  2623.  
  2624.  
  2625. second_init_values:
  2626.  
  2627.   select
  2628.     when char(max_value$,2)='1' then
  2629.         total_in_max_term = rr*Q1
  2630.     when char(max_value$,2)='2' then
  2631.         total_in_max_term = rr*Q2
  2632.     when char(max_value$,2)='3' then
  2633.         total_in_max_term = rr*Q3
  2634.   end
  2635.   ch = char(word(order_by_quantity,2),2)
  2636.   select
  2637.     when ch = '1' then middlevalue = Q1
  2638.     when ch = '2' then middlevalue = Q2
  2639.     when ch = '3' then middlevalue = Q3
  2640.   end
  2641. return
  2642.  
  2643.  
  2644. set_terms_for_both_sides:
  2645. interpret "$Num0 = " rr||"11"
  2646. interpret "$Num = " rr||ss||tt
  2647.    if  $Num0=$Num then
  2648.      do
  2649.       $originalR = RHS!
  2650.       $originalL = LHS!
  2651.       $elements_list = R_el_list
  2652.      end
  2653.     else
  2654.       do
  2655.         /*  trace ?r */
  2656.         RHS! = $originalR
  2657.         LHS! = $originalL
  2658.         elements_list = $elements_list
  2659.       end
  2660. return
  2661.  
  2662.  
  2663.  
  2664.  
  2665. $calc:
  2666. /* trace ?r */
  2667. if balances_for_curr_el() then
  2668.   do /**/
  2669.     adjusted_LHS = false
  2670.     adjusted_RHS = false
  2671.     call set_largest_term(largestterm)
  2672.     call set_middle_term(middleterm)
  2673.     call set_smallest_term(smallestterm)
  2674.     /* trace ?r */
  2675.     $unequation = newLHS||" = "||newRHS
  2676.     tt = tt + 1
  2677.     signal begin_nonregular
  2678.   end /**/
  2679. return
  2680.  
  2681.  
  2682.  
  2683.  
  2684. set_largest_term:
  2685. place = arg(1)
  2686. if adjusted_LHS then $LHS = newLHS
  2687.     else $LHS = LHS!
  2688. if adjusted_RHS then $RHS = newRHS
  2689.     else $RHS = RHS!
  2690.  
  2691. ch = char(word(order_by_quantity,3),2)
  2692. select
  2693.   when ch = '2' then
  2694.      if side#2=1 then do
  2695.         newLHS =  replace(rr||word($LHS,place),$LHS,place)
  2696.         adjusted_LHS = true; end
  2697.       else do
  2698.         $place = place-#Lterms
  2699.         newRHS =,
  2700.          replace(rr||word($RHS,$place),$RHS,$place)
  2701.         adjusted_RHS = true; end
  2702.   when ch = '1' then do
  2703.       newLHS = replace(rr||word($LHS,place),$LHS,place)
  2704.       adjusted_LHS = true; end
  2705.   when ch = '3' then do
  2706.       $place = place-#Lterms
  2707.       newRHS =,
  2708.         replace(rr||word($RHS,$place),$RHS,$place)
  2709.         adjusted_RHS = true; end
  2710. end
  2711. return
  2712.  
  2713.  
  2714.  
  2715.  
  2716.  
  2717. set_middle_term:
  2718. place = arg(1)
  2719. if adjusted_LHS then $LHS = newLHS
  2720.     else $LHS = LHS!
  2721. if adjusted_RHS then $RHS = newRHS
  2722.     else $RHS = RHS!
  2723.  
  2724. ch = char(word(order_by_quantity,2),2)
  2725. select
  2726.   when ch = '2' then
  2727.      if side#2=1 then do
  2728.         newLHS =  replace(ss||word($LHS,place),$LHS,place)
  2729.         adjusted_LHS = true; end
  2730.       else do
  2731.         $place = place-#Lterms
  2732.         newRHS =,
  2733.          replace(ss||word($RHS,$place),$RHS,$place)
  2734.         adjusted_RHS = true; end
  2735.   when ch = '1' then do
  2736.       newLHS = replace(ss||word($LHS,place),$LHS,place)
  2737.       adjusted_LHS = true; end
  2738.   when ch = '3' then do
  2739.       $place = place-#Lterms
  2740.       newRHS =,
  2741.         replace(ss||word($RHS,$place),$RHS,$place)
  2742.         adjusted_RHS = true; end
  2743. end
  2744. return
  2745.  
  2746.  
  2747.  
  2748.  
  2749.  
  2750. set_smallest_term:
  2751. place = arg(1)
  2752. if adjusted_LHS then $LHS = newLHS
  2753.     else $LHS = LHS!
  2754. if adjusted_RHS then $RHS = newRHS
  2755.     else $RHS = RHS!
  2756.  
  2757. ch = char(word(order_by_quantity,1),2)
  2758. select
  2759.   when ch = '2' then
  2760.      if side#2=1 then do
  2761.         newLHS =  replace(tt||word($LHS,place),$LHS,place)
  2762.         adjusted_LHS = true; end
  2763.       else do
  2764.         $place = place-#Lterms
  2765.         newRHS =,
  2766.          replace(tt||word($RHS,$place),$RHS,$place)
  2767.         adjusted_RHS = true; end
  2768.   when ch = '1' then do
  2769.       newLHS = replace(tt||word($LHS,place),$LHS,place)
  2770.       adjusted_LHS = true; end
  2771.   when ch = '3' then do
  2772.       $place = place-#Lterms
  2773.       newRHS =,
  2774.         replace(tt||word($RHS,$place),$RHS,$place)
  2775.         adjusted_RHS = true; end
  2776. end
  2777. return
  2778.  
  2779.  
  2780.  
  2781.  
  2782. balances_for_curr_el:
  2783.  
  2784. /* Here we test to see if the coefficients chose will at least balance that particular element before trying to ttest balance the entire equation. */
  2785.  
  2786.  
  2787. do a=1 to 3
  2788.   ch.a = char(word(order_by_quantity,a),2)
  2789. end
  2790. select
  2791.   when ch.3='1' then do /**/
  2792.     if side#2=1 then
  2793.       do /***/
  2794.         if ch.2='2' then
  2795.           do
  2796.             if total_in_max_term+ss*middlevalue=tt*leastvalue
  2797.               then return true
  2798.               else return false
  2799.           end
  2800.         else
  2801.           do
  2802.             if total_in_max_term+tt*leastvalue=ss*middlevalue
  2803.               then return true
  2804.               else return false
  2805.           end
  2806.       end /***/
  2807.     else
  2808.       do
  2809.         if total_in_max_term=tt*leastvalue+ss*middlevalue
  2810.             then return true
  2811.             else return false
  2812.       end
  2813.   end /**/
  2814.  
  2815.   when ch.3='2' then do /**/
  2816.     if side#2=1 then
  2817.       do /***/
  2818.         if ch.1='1' then
  2819.           do
  2820.             if tt*leastvalue+total_in_max_term=ss*middlevalue
  2821.               then return true
  2822.               else return false
  2823.           end
  2824.         else
  2825.           do
  2826.             if ss*middlevalue+total_in_max_term=tt*leastvalue
  2827.               then return true
  2828.               else return false
  2829.           end
  2830.       end /***/
  2831.     else
  2832.       do
  2833.         if ch.1='1' then
  2834.           do
  2835.             if tt*leastvalue=total_in_max_term+ss*middlevalue
  2836.               then return true
  2837.               else return false
  2838.           end
  2839.         else
  2840.             if ss*middlevalue=total_in_max_term+tt*leastvalue
  2841.               then return true
  2842.               else return false
  2843.       end
  2844.   end /**/
  2845.  
  2846.  
  2847.   when ch.3='3' then do /**/
  2848.     if side#2=1 then
  2849.        do 
  2850.         if ss*middlevalue+tt*leastvalue=total_in_max_term
  2851.             then return true
  2852.             else return false
  2853.        end
  2854.      else
  2855.        do
  2856.          if ch.2='1' then
  2857.             do
  2858.              if ss*middlevalue=tt*leastvalue+total_in_max_term
  2859.                then return true
  2860.                else return false
  2861.             end
  2862.           else
  2863.             do
  2864.              if tt*leastvalue=ss*middlevalue+total_in_max_term
  2865.                then return true
  2866.                else return false
  2867.             end
  2868.        end
  2869.     end /**/
  2870. end /* select */