Prime implementation of the HP48/50 MES solver (ish)

+- HP Forums (https://www.hpmuseum.org/forum)
+-- Forum: HP Calculators (and very old HP Computers) (https://www.hpmuseum.org/forum/forum-3.html)
+--- Forum: HP Prime (https://www.hpmuseum.org/forum/forum-5.html)
+--- Thread: Prime implementation of the HP48/50 MES solver (ish) (/thread-25294.html)



Prime implementation of the HP48/50 MES solver (ish) - Paulo MO - 2026-07-29 03:26, edited 2026-07-29 03:54

Hello

1. I have always considered the MES solver of the HP48/50 series a superbly useful tool. For those who never used it, it works like this:

a. It accepts a list of equations as input. The number of equations may be greater or smaller than the number of variables, as long as they are consistent.
b. It lets the user input values for the known variables;
c. It will then attempt to solve for all the remaining variables in the following way: it scans the set of equations until it finds one equation with a single unknown; it solves that equation for the unknown variable. Then, having one more known variable, it scans again for another equation with a single unknown; it solves it. And so on, until all variables have been found, or no equation with a single unknown remains.

It is therefore not a simultaneous equation solver, but instead a sequential multiple equation solver. Due to that, it does not need a system with exactly as many equations as unknowns. It is the perfect tool to be fed with all the equations pertaining to a given topic (let's say "Butterworth digital filter design") and, afterward, with whatever variables are known, to determine all the remaining variables. The only care is to have enough (possibly redundant) formulas in the formula list, so that the solver will easily find formulas with a single unknown during the solving process.

2. Having recently bought a Prime, I wanted to have this type of solver on it. Helas, no such app or program was to be found. Therefore, I made one. The implementation is clumsy, both in the UI and in some aspects of the code. The Prime and its considerably more "different from expected" programming environment, language, and quirks are new to me, and there seems to be a lack of reference documentation (or I was inefficient in finding it). I was therefore content to have it working at all within the time I had allotted to the project. The code is below, in case anyone is interested. Even though many improvements are possible and, as I said, it is a somewhat clumsy implementation, it does the job well enough to scratch my itch. It may still have bugs, and is being released as is, but maybe it can be useful to someone else.

3. Notes on operation:
a. We can create and modify different sets of equations. Each set can have as many equations as needed, for a maximum total number of 21 variables per set.
b. After selecting a particular set, we can solve it by inputting the known variables. The program will then try to determine all the remaining variables using the equations in the set, using the above-described HP48/50 MES approach;
c. The variables are initially shown with a default value of -999999999, which is meant to convey the message "unknown"; (clumsy, as I said, but the final compromise of my conditional surrender to the Prime INPUT function)
d. After solving the set, the values of the user input variables can be altered, and a new solution can be directly found from the variable input screen. All the automatically determined variables will be recomputed. However, if the set of known variables changes, one needs to exit from the variable input screen, and start a new solve session, thus inputting all known variables again.
e. The program is bilingual (English as default);
f. It can operate in either "verbose" or "quiet" mode. In verbose mode, it will show which particular equation is being solved for each one of the variables to be determined. The default is "quiet" mode, in which only the final values of the variables are shown;
g. Often, viewing the equations included in a set cannot be done adequately, due to the size limitations of the msgbox() function. Therefore, whenever a set is viewed, that particular set of equations is also written to the terminal (accessible by simultaneously pressing ON and / (division operator)), where the viewing conditions are much better.
h. After creating your equation sets, you may want to save the contents of the EQSETS variable in one of the global system list variables (say L0) so that you will not lose them after a recompilation or system reset. If you then lose your sets for some reason, just copy L0 to EQSETS back again, and set EQINIT to 1. The program will then run with the previously saved equation sets.

Enjoy. All one has to do is copy the code into a new program (MES), and then run it (e.g., calling MES from the command line). Even though I am not planning to spend any more time on it, any bug reports/improvement suggestions will be welcome.

Paulo

Code:
//=====================================================================
// MES - Sequential Equation Set Manager and Solver HP48/50 style(ish) - Version 0.9
//=====================================================================
// Data model:
//   EQSETS is a list of "sets". Each set is a 2-element list:
//     { SetName(string), Equations(list of strings) }
//   EQSETS itself persists on the calculator as a global variable, but will be
//   reset if the program is recompiled. Therefore, it may be useful to save
//   its contents in a more permanent location (L0, for example).
//
//=====================================================================

EXPORT EQSETS;
EXPORT EQINIT;
EXPORT CVARS;   // variable names for the equation set currently being solved
EXPORT CVALS;   // parallel list of values (defaultVal() means "not yet known")
EXPORT CSTAT;   // parallel list of status codes: 0=unknown,1=user-entered,2=solved


//---------------------------------------------------------------
//Default position of the toggle settings
//---------------------------------------------------------------
verbose:=0;
language:="english";


//---------------------------------------------------------------
// Ensure EQSETS exists exactly once (first run after install/reset)
//---------------------------------------------------------------
initStorage()
BEGIN
  IF EQINIT<>1 THEN
    EQSETS:={};
    EQINIT:=1;
  END;
END;

//---------------------------------------------------------------
// To circumvent the absence of chained indexing EQSETS(idx)(1)/(2)
//---------------------------------------------------------------
setName(idx)
BEGIN
  LOCAL t;
  t:=EQSETS(idx);
  RETURN t(1);
END;

setEqs(idx)
BEGIN
  LOCAL t;
  t:=EQSETS(idx);
  RETURN t(2);
END;

//---------------------------------------------------------------
// Remove element idx from list L, and return the new list
//---------------------------------------------------------------
listRemove(L,idx)
BEGIN
  LOCAL i,n,R;
  R:={};
  n:=SIZE(L);
  FOR i FROM 1 TO n DO
    IF i<>idx THEN
      R(SIZE(R)+1):=L(i);
    END;
  END;
  RETURN R;
END;

//---------------------------------------------------------------
// Get the list of set names (for CHOOSE dialogs)
//---------------------------------------------------------------
getSetNames()
BEGIN
  LOCAL i,n,names;
  names:={};
  n:=SIZE(EQSETS);
  FOR i FROM 1 TO n DO
    names(SIZE(names)+1):=setName(i);
  END;
  RETURN names;
END;

//---------------------------------------------------------------
// Add one equation to set idx, prompting via INPUT
//---------------------------------------------------------------
addEquation(idx)
BEGIN
  LOCAL eq,eqs;
  eq:="";
  IF language=="english" THEN
    IF INPUT({{eq,[2]}},"Add Equation","Equation:","e.g. A+B=C",0,"")==0 THEN RETURN; END;
  ELSE
    IF INPUT({{eq,[2]}},"Adicionar Equação","Equação:","e.g. A+B=C",0,"")==0 THEN RETURN; END;
  END;
  IF eq=="" THEN RETURN; END;
  eqs:=setEqs(idx);
  eqs(SIZE(eqs)+1):=eq;
  EQSETS(idx):={setName(idx),eqs};
END;

//---------------------------------------------------------------
// After creating a set, loop asking to add equations
//---------------------------------------------------------------
editEquationsLoop(idx)
BEGIN
  LOCAL more;
  more:=1;
  WHILE more==1 DO
    addEquation(idx);
    more:=0;
    IF language=="english" THEN
      CHOOSE(more,"Add Another Equation?","Yes","No");
    ELSE
      CHOOSE(more,"Adicionar outra Equação?","Sim","Não");
    END; 
  END;
END;

//---------------------------------------------------------------
// New Set
//---------------------------------------------------------------
newSet()
BEGIN
  LOCAL nm,i,n;
  nm:="";
  IF language=="english" THEN
    IF INPUT({{nm,[2]}},"New Set","Name:","Enter a name for this equation set","","")==0 THEN RETURN; END;
  ELSE
    IF INPUT({{nm,[2]}},"Novo Set","Nome:","Qual o nome do novo set?","","")==0 THEN RETURN; END;
  END;
  IF nm=="" THEN RETURN; END;
  IF SIZE(EQSETS)<>0 THEN
    n:=SIZE(EQSETS);
    FOR i FROM 1 TO n DO
      IF setName(i)==nm THEN
        IF language=="english" THEN
          MSGBOX("A set with that name already exists.");
        ELSE
          MSGBOX("Já existe um set com esse nome.");
        END;
        RETURN;
      END;
    END;
  END;
  EQSETS(SIZE(EQSETS)+1):={nm,{}};
  editEquationsLoop(SIZE(EQSETS));
END;

//---------------------------------------------------------------
// Delete Set
//---------------------------------------------------------------
deleteSet()
BEGIN
  LOCAL names,sel,conf;
  IF SIZE(EQSETS)==0 THEN
    IF language=="english" THEN
      MSGBOX("No equation sets exist.");
    ELSE
      MSGBOX("Não há sets de equações.");
    END;
    RETURN;
  END;
  names:=getSetNames();
  sel:=0;
  IF language=="english" THEN
    CHOOSE(sel,"Delete Which Set?",names);
  ELSE
    CHOOSE(sel,"Qual o set a apagar?",names); 
  END;
  IF sel==0 THEN RETURN; END;
  conf:=0;
  IF language=="english" THEN
    CHOOSE(conf,"Confirm Delete","Yes, delete \""+setName(sel)+"\"","No, cancel");
  ELSE
    CHOOSE(conf,"Confirme","Sim, apaga \""+setName(sel)+"\"","Não, cancela");
  END;
  IF conf==1 THEN
    EQSETS:=listRemove(EQSETS,sel);
    IF language=="english" THEN
      MSGBOX("Set deleted.");
    ELSE
      MSGBOX("Set apagado.");
    END;
  END;
END;

//---------------------------------------------------------------
// View equations in a set
//---------------------------------------------------------------
viewEquations(idx)
BEGIN
  LOCAL eqs,i,s;
  eqs:=setEqs(idx);
  IF SIZE(eqs)==0 THEN
    IF language=="english" THEN
      MSGBOX("This set has no equations yet.");
    ELSE
      MSGBOX("Este set ainda não tem equações.");
    END;
    RETURN;
  END;
  s:="";
  FOR i FROM 1 TO SIZE(eqs) DO
    s:=s+STRING(i)+": "+eqs(i)+CHAR(10);
  END;
  PRINT("****** Set "+setName(idx)+" *********");
  PRINT(s);
  MSGBOX(s);
END;

//---------------------------------------------------------------
// Edit one equation
//---------------------------------------------------------------
editEquation(idx)
BEGIN
  LOCAL eqs,sel,neweq;
  eqs:=setEqs(idx);
  IF SIZE(eqs)==0 THEN
    IF language=="english" THEN
      MSGBOX("No equations to edit.");
    ELSE
      MSGBOX("Não há equações para editar.");
    END;
    RETURN;
  END;
  sel:=0;
  IF language=="english" THEN
    CHOOSE(sel,"Edit Which Equation?",eqs);
  ELSE
    CHOOSE(sel,"Qual a equação a editar?",eqs);
  END;
  IF sel==0 THEN RETURN; END;
  neweq:=eqs(sel);
  IF language=="english" THEN
    IF INPUT({{neweq,[2]}},"Edit Equation","Equation:","Modify and press OK",0,eqs(sel))==0 THEN RETURN; END;
  ELSE
    IF INPUT({{neweq,[2]}},"Editar Equação","Equação:","Modifique e pressione OK",0,eqs(sel))==0 THEN RETURN; END;
  END;
  IF neweq=="" THEN RETURN; END;
  eqs(sel):=neweq;
  EQSETS(idx):={setName(idx),eqs};
END;

//---------------------------------------------------------------
// Delete one equation
//---------------------------------------------------------------
deleteEquation(idx)
BEGIN
  LOCAL eqs,sel,conf;
  eqs:=setEqs(idx);
  IF SIZE(eqs)==0 THEN
    IF language=="english" THEN
      MSGBOX("No equations to delete.");
    ELSE
      MSGBOX("Não há equações para apagar.");
    END;
    RETURN;
  END;
  sel:=0;
  IF language=="english" THEN
    CHOOSE(sel,"Delete Which Equation?",eqs);
  ELSE
    CHOOSE(sel,"Qual a equação a apagar?",eqs);
  END;
  IF sel==0 THEN RETURN; END;
  conf:=0;
  IF language=="english" THEN
    CHOOSE(conf,"Confirm Delete","Yes, delete","No, cancel");
  ELSE
    CHOOSE(conf,"Confirme","Sim, apaga","Não, cancela");
  END;
  IF conf==1 THEN
    eqs:=listRemove(eqs,sel);
    EQSETS(idx):={setName(idx),eqs};
    IF language=="english" THEN
      MSGBOX("Equation deleted.");
    ELSE 
      MSGBOX("Equação apagada.");
    END;
  END;
END;

//---------------------------------------------------------------
// Rename a set
//---------------------------------------------------------------
renameSet(idx)
BEGIN
  LOCAL nm,i,n,cur;
  cur:=setName(idx);
  nm:=cur;
  IF INPUT({{nm,[2]}},"Rename Set","New name:","Enter new name",0,cur)==0 THEN RETURN; END;
  IF nm=="" THEN RETURN; END;
  n:=SIZE(EQSETS);
  FOR i FROM 1 TO n DO
    IF i<>idx AND setName(i)==nm THEN
      IF language=="english" THEN
        MSGBOX("A set with that name already exists.");
      ELSE 
        MSGBOX("Já existe um set com este nome.");
      END;
      RETURN;
    END;
  END;
  EQSETS(idx):={nm,setEqs(idx)};
END;

//---------------------------------------------------------------
// Submenu for a given set
//---------------------------------------------------------------
setSubMenu(idx)
BEGIN
  LOCAL choice,title;
  choice:=0;
  REPEAT
    title:="Set: "+setName(idx);
    IF language=="english" THEN
      CHOOSE(choice,title,
      "View Equations","Add Equation","Edit Equation",
      "Delete Equation","Rename Set","Back");
    ELSE
      CHOOSE(choice,title,
      "Ver Equações","Adicionar Equação","Editar Equação",
      "Apagar Equação","Renomear Set","Back");
    END;
    IF choice==1 THEN viewEquations(idx); END;
    IF choice==2 THEN addEquation(idx); END;
    IF choice==3 THEN editEquation(idx); END;
    IF choice==4 THEN deleteEquation(idx); END;
    IF choice==5 THEN renameSet(idx); END;
  UNTIL choice==6 OR choice==0;
END;

//---------------------------------------------------------------
// Choose a set, then open its submenu
//---------------------------------------------------------------
editSetMenu()
BEGIN
  LOCAL names,sel;
  IF SIZE(EQSETS)==0 THEN
    IF language=="english" THEN
      MSGBOX("No equation sets exist. Create one first.");
    ELSE 
      MSGBOX("Não há sets de equações. Crie um primeiro.");
    END;
    RETURN;
  END;
  names:=getSetNames();
  sel:=0;
  IF language=="english" THEN
    CHOOSE(sel,"Edit Which Set?",names);
  ELSE
    CHOOSE(sel,"Qual o set a editar?",names);
  END;
  IF sel==0 THEN RETURN; END;
  setSubMenu(sel);
END;

//=====================================================================
// SOLVE SECTION - (FINNALY)
//=====================================================================

// Default value meaning "this variable's value is not yet known".
// Do not use this exact number as a genuine variable value.
defaultVal()
BEGIN
  RETURN -999999999;
END;

//---------------------------------------------------------------
// Parsing utilities
//---------------------------------------------------------------
isLetter(c)
BEGIN
  IF (CHAR(c) >= "A" AND CHAR(c) <= "Z") OR (CHAR(c) >= "a" AND CHAR(c) <= "z") THEN
    RETURN 1;
  ELSE
    RETURN 0;
  END;
END;

isDigit(c)
BEGIN
  IF CHAR(c) >= "0" AND CHAR(c) <= "9" THEN
    RETURN 1;
  ELSE
    RETURN 0;
  END;
END;

isAlnum(c)
BEGIN
  IF (isLetter(c) OR isDigit(c)) THEN
    RETURN 1;
  ELSE
    RETURN 0;
  END;
END;

isInList(x,L)
BEGIN
  LOCAL i,n;
  n:=SIZE(L);
  FOR i FROM 1 TO n DO
    IF L(i)==x THEN RETURN 1; END;
  END;
  RETURN 0;
END;

//---------------------------------------------------------------
// Extract unique variable-names from one equation
// (A variable name is a run of letters/digits starting with a letter. If it is
// immediately followed by a "(", it is treated as a function name (e.g. SIN)
// and is not counted as a variable name.
//---------------------------------------------------------------
extractVarsFromEq(eqstr)
BEGIN
  LOCAL n,i,c,tok,vars,isFunc,j;
  n:=SIZE(eqstr);
  vars:={};
  i:=1;
  WHILE i<=n DO
    c:=eqstr(i);
    IF isLetter(c) THEN
      tok:=CHAR(c);
      j:=i+1;
      WHILE j<=n AND isAlnum(eqstr(j)) DO
        tok:=tok+CHAR(eqstr(j));
        j:=j+1;
      END;
      isFunc:=0;
      IF j<=n THEN
        IF CHAR(eqstr(j))=="(" THEN isFunc:=1; END;
      END;
      IF isFunc==0 THEN
        IF isInList(tok,vars)==0 THEN
          vars(SIZE(vars)+1):=tok;
        END;
      END;
      i:=j;
    ELSE
      i:=i+1;
    END;
  END;
  RETURN vars;
END;

//---------------------------------------------------------------
// Union of all variables across every equation in a set
//---------------------------------------------------------------
getSetVariables(idx)
BEGIN
  LOCAL eqs,i,n,v,allVars,j,m;
  eqs:=setEqs(idx);
  allVars:={};
  n:=SIZE(eqs);
  FOR i FROM 1 TO n DO
    v:=extractVarsFromEq(eqs(i));
    m:=SIZE(v);
    FOR j FROM 1 TO m DO
      IF isInList(v(j),allVars)==0 THEN
        allVars(SIZE(allVars)+1):=v(j);
      END;
    END;
  END;
  RETURN allVars;

END;

//---------------------------------------------------------------
// Replace every KNOWN variable name in eqstr with its value
// Uses the global CVARS/CVALS. Function-names (followed by "(")
// and still-unknown variable names are left untouched.
//---------------------------------------------------------------
substituteKnowns(eqstr)
BEGIN
  LOCAL n,i,c,tok,j,isFunc,outS,pp;
  LOCAL kk,foundIdx;
  n:=SIZE(eqstr);
  outS:="";
  i:=1;
  WHILE i<=n DO
    c:=eqstr(i);
    IF isLetter(c) THEN
      tok:=CHAR(c);
      j:=i+1;
      WHILE j<=n AND isAlnum(eqstr(j)) DO
        tok:=tok+CHAR(eqstr(j));
        j:=j+1;
      END;
      isFunc:=0;
      IF j<=n THEN
        IF CHAR(eqstr(j))=="(" THEN isFunc:=1; END;
      END;
      IF isFunc==1 THEN
        outS:=outS+tok;
      ELSE
        //This tok is a variable name. Let us see if it is known
        foundIdx:=0;
        kk:=MIN(SIZE(CVARS),21);
        //For each variable in the set
        FOR pp FROM 1 TO kk DO
          //If the name checks and it is still unknown, them flag having found an unknown
          IF CVARS(pp)==tok AND CVALS(pp)<>defaultVal() THEN
            foundIdx:=pp;
          END;
        END;
        //fill in the value of the unknown variable or keep the variable name
        IF foundIdx>0 THEN
         outS:=outS+STRING(CVALS(foundIdx));
        ELSE
          outS:=outS+tok;
        END;
      END;
      i:=j;
    ELSE
      outS:=outS+CHAR(c);
      i:=i+1;
    END;
  END;
  RETURN outS;
END;

//---------------------------------------------------------------
// Solve one equation (with values of known variables already substituted) for one variable
// Returns {1,value} on success, {0,0} on failure.
//---------------------------------------------------------------
solveOneVar(eqstr,varname)
BEGIN
  LOCAL sol;
  IF verbose==1 THEN
    IF language=="english" THEN
      MSGBOX("Solving "+CHAR(10)+eqstr+CHAR(10)+"for "+varname);
    ELSE
      MSGBOX("A resolver "+CHAR(10)+eqstr+CHAR(10)+"para obter "+varname);
    END;
  END;
  IFERR
    sol:=fsolve(EVAL(eqstr),EVAL(varname),10);
  THEN
    RETURN {0,0};
  END;
  IF SIZE(sol)==0 THEN
    RETURN {0,0};
  ELSE
    RETURN {1,sol};
  END;
END;

//---------------------------------------------------------------
// Find candidate equations for solving (criterion: does it have exactly one unknown?)
// Returns {1,varIndex,value} if solved, {0,0,0} otherwise.
//---------------------------------------------------------------
tryEquation(eqstr)
BEGIN
  LOCAL vlist,m,i,idx2,unknownCount,unknownIdx,subEq;
  LOCAL sol,pp;
  vlist:=extractVarsFromEq(eqstr);
  m:=SIZE(vlist);
  unknownCount:=0;
  unknownIdx:=0;
  FOR i FROM 1 TO m DO
    idx2:=0;
    FOR pp FROM 1 TO MIN(SIZE(CVARS),21) DO
      IF CVARS(pp)==vlist(i) THEN idx2:=pp; END;
    END;
    IF idx2>0 THEN
      IF CVALS(idx2)==defaultVal() THEN
        unknownCount:=unknownCount+1;
        unknownIdx:=idx2;
      END;
    END;
  END;
  IF unknownCount<>1 THEN
    RETURN {0,0,0};
  END;
  IF verbose==1 THEN
    IF language=="english" THEN 
      MSGBOX("Going to solve equation "+CHAR(10)+eqstr+CHAR(10)+"for "+CVARS(unknownIdx));
    ELSE
      MSGBOX("Vou usar a equação "+CHAR(10)+eqstr+CHAR(10)+"para obter "+CVARS(unknownIdx));
    END;
  END;
  subEq:=substituteKnowns(eqstr);
  sol:=solveOneVar(subEq,CVARS(unknownIdx));
  IF sol(1)==0 THEN
    RETURN {0,0,0};
  END;
  IF verbose==1 THEN
    MSGBOX(CVARS(unknownIdx)+" = "+sol(2));
  END;
  RETURN {1,unknownIdx,sol(2)};
END;

//---------------------------------------------------------------
// Iterate the scan of the set's equations until no further progress is made
//---------------------------------------------------------------
solveIterate(idx)
BEGIN
  LOCAL eqs,n,progress,ei,res;
  eqs:=setEqs(idx);
  n:=SIZE(eqs);
  progress:=1;
  WHILE progress==1 DO
    progress:=0;
    FOR ei FROM 1 TO n DO
      res:=tryEquation(eqs(ei));
      IF res(1)==1 THEN
        CVALS(res(2)):=res(3);
        CSTAT(res(2)):=2;
        progress:=1;
      END;
    END;
  END;
END;

//---------------------------------------------------------------
// The solve input/results screen
// Returns 1 to keep showing the screen again, 0 when the user cancels out.
//---------------------------------------------------------------
solveScreen(idx)
BEGIN
  LOCAL labels,help,rsvd,ok,n,pp,prevals;
  LOCAL var1, var2, var3, var4, var5, var6, var7;
  LOCAL var8, var9, var10, var11, var12, var13, var14;
  LOCAL var15, var16, var17, var18, var19, var20, var21;
  n:=SIZE(CVARS);
  // Warn the user that there is an (arbitrary) limit of 21 variables per set (two input pages)
  IF n>21 THEN
    IF language=="english" THEN
      MSGBOX("Each equation set can have only up to 21 variables.");
    ELSE
      MSGBOX("Cada set de equações só pode ter até 21 variáveis.");
    END;
    n:=21;
  END;
  labels:=MAKELIST("",pp,1,21,1);
  IF language=="english" THEN 
    help:=MAKELIST("Enter a numeric value",pp,1,21,1);
  ELSE
    help:=MAKELIST("Entre um valor numérico",pp,1,21,1);
  END;
  rsvd:=MAKELIST(0,pp,1,21,1);
  FOR pp FROM 1 TO 21 DO
    IF pp<=n then
      IF CSTAT(pp)==1 THEN
        labels(pp):=CVARS(pp)+" [User]:";
      END;
      IF CSTAT(pp)==2 THEN
        labels(pp):=CVARS(pp)+" [Auto]:";
      END;
      IF CSTAT(pp)==0 THEN
        labels(pp):=CVARS(pp)+" [?]:";
      END;
    ELSE
        labels(pp):="var"+pp+":";
    END;
  END;
  prevals:=CVALS;
  ok:=INPUT({var1, var2, var3, var4, var5, var6, var7, var8,
  var9, var10, var11, var12, var13, var14, var15, var16,
  var17, var18, var19, var20, var21}, "Solve: "+setName(idx),labels, help,rsvd, prevals);
  IF ok==0 THEN
    RETURN 0;
  END; 
  // For every new computation, force any previously automatically calculated
  // variables to their default value, so that they will be recomputed
  // with the current values of the  user defined variables
  IF n>=1 THEN
    IF CSTAT(1)==2 THEN
      CVALS(1):= defaultVal();
    ELSE
      CVALS(1):= var1;
    END;
  END;
  IF n>=2 THEN
    IF CSTAT(2)==2 THEN
      CVALS(2):= defaultVal();
    ELSE
      CVALS(2):= var2;
    END;
  END;
  IF n>=3 THEN
    IF CSTAT(3)==2 THEN
      CVALS(3):= defaultVal();
    ELSE
      CVALS(3):= var3;
    END;
  END;
  IF n>=4 THEN
    IF CSTAT(4)==2 THEN
      CVALS(4):= defaultVal();
    ELSE
      CVALS(4):= var4;
    END;
  END;
  IF n>=5 THEN
    IF CSTAT(5)==2 THEN
      CVALS(5):= defaultVal();
    ELSE
      CVALS(5):= var5;
    END;
  END;
  IF n>=6 THEN
    IF CSTAT(6)==2 THEN
      CVALS(6):= defaultVal();
    ELSE
      CVALS(6):= var6;
    END;
  END;
  IF n>=7 THEN
    IF CSTAT(7)==2 THEN
      CVALS(7):= defaultVal();
    ELSE
      CVALS(7):= var7;
    END;
  END;
  IF n>=8 THEN
    IF CSTAT(8)==2 THEN
      CVALS(8):= defaultVal();
    ELSE
      CVALS(8):= var8;
    END;
  END;
  IF n>=9 THEN
    IF CSTAT(9)==2 THEN
      CVALS(9):= defaultVal();
    ELSE
      CVALS(9):= var9;
    END;
  END;
  IF n>=10 THEN
    IF CSTAT(10)==2 THEN
      CVALS(10):= defaultVal();
    ELSE
      CVALS(10):= var10;
    END;
  END;
  IF n>=11 THEN
    IF CSTAT(11)==2 THEN
      CVALS(11):= defaultVal();
    ELSE
      CVALS(11):= var11;
    END;
  END;
  IF n>=12 THEN
    IF CSTAT(12)==2 THEN
      CVALS(12):= defaultVal();
    ELSE
      CVALS(12):= var12;
    END;
  END;
  IF n>=13 THEN
    IF CSTAT(13)==2 THEN
      CVALS(13):= defaultVal();
    ELSE
      CVALS(13):= var13;
    END;
  END;
IF n>=14 THEN
    IF CSTAT(14)==2 THEN
      CVALS(14):= defaultVal();
    ELSE
      CVALS(14):= var14;
    END;
  END;
  IF n>=15 THEN
    IF CSTAT(15)==2 THEN
      CVALS(15):= defaultVal();
    ELSE
      CVALS(15):= var15;
    END;
  END;
  IF n>=15 THEN
    IF CSTAT(15)==2 THEN
      CVALS(15):= defaultVal();
    ELSE
      CVALS(15):= var15;
    END;
  END;
  IF n>=16 THEN
    IF CSTAT(16)==2 THEN
      CVALS(16):= defaultVal();
    ELSE
      CVALS(16):= var16;
    END;
  END;
  IF n>=17 THEN
    IF CSTAT(17)==2 THEN
      CVALS(17):= defaultVal();
    ELSE
      CVALS(17):= var17;
    END;
  END;
  IF n>=18 THEN
    IF CSTAT(18)==2 THEN
      CVALS(18):= defaultVal();
    ELSE
      CVALS(18):= var18;
    END;
  END;
  IF n>=19 THEN
    IF CSTAT(19)==2 THEN
      CVALS(19):= defaultVal();
    ELSE
      CVALS(19):= var19;
    END;
  END;
  IF n>=20 THEN
    IF CSTAT(20)==2 THEN
      CVALS(20):= defaultVal();
    ELSE
      CVALS(20):= var20;
    END;
  END;
  IF n=21 THEN
    IF CSTAT(121)==2 THEN
      CVALS(21):= defaultVal();
    ELSE
      CVALS(21):= var21;
    END;
  END;
  FOR pp FROM 1 TO n DO
    IF CSTAT(pp)==0 THEN
      IF CVALS(pp)<>prevals(pp) THEN
        CSTAT(pp):=1;
      END;
    END;
  END;
  solveIterate(idx);
  RETURN 1;
END;

//---------------------------------------------------------------
// Entry point for the solve process (called from the listSets() menu)
//---------------------------------------------------------------
solveSet(idx)
BEGIN
  LOCAL eqs,cont,n,pp;
  eqs:=setEqs(idx);
  IF SIZE(eqs)==0 THEN
    IF language=="english" THEN
      MSGBOX("This set has no equations.");
    ELSE
      MSGBOX("Este set não tem equações.");
    END;
    RETURN;
  END;
  CVARS:=getSetVariables(idx);
  n:=MIN(SIZE(CVARS),21);
  IF n==0 THEN
    IF language=="english" THEN
      MSGBOX("No variables found in this set's equations.");
    ELSE
      MSGBOX("Não há variáveis neste set de equações.");
    END;
    RETURN;
  END;
  CVALS:=MAKELIST(defaultVal(),pp,1,n,1);
  CSTAT:=MAKELIST(0,pp,1,n,1);
  cont:=1;
  WHILE cont==1 DO
    cont:=solveScreen(idx);
  END;
END;

//---------------------------------------------------------------
// List Sets — highlight a set from a scrollable list
//---------------------------------------------------------------
listSets()
BEGIN
  LOCAL names,sel,action,n;
  IF SIZE(EQSETS)==0 THEN
    IF language=="english" THEN
      MSGBOX("No equation sets exist.");
    ELSE
      MSGBOX("Não há nenhum set de equações.");
    END;
    RETURN;
  END;
  sel:=0;
  REPEAT
    names:=getSetNames();
    n:=SIZE(names);
    IF language=="english" THEN
      names(n+1):="Back to Main Menu";
      CHOOSE(sel,"Select a Set",names);
    ELSE
      names(n+1):="Voltar ao menú principal";
      CHOOSE(sel,"Seleccione um set",names);
    END;
    IF sel<>0 AND sel<>n+1 THEN
      action:=0;
      REPEAT
        IF language=="english" THEN
          CHOOSE(action,"Set: "+setName(sel),"Solve","View","Edit","Back to List");
        ELSE
          CHOOSE(action,"Set: "+setName(sel),"Resolver","Ver","Editar","Voltar à lista");
        END;
        IF action==1 THEN solveSet(sel); END;
        IF action==2 THEN viewEquations(sel); END;
        IF action==3 THEN setSubMenu(sel); END;
      UNTIL action==4 OR action==0;
    END;
  UNTIL sel==0 OR sel==n+1;
END;

//---------------------------------------------------------------
// Main MES entry point
//---------------------------------------------------------------
EXPORT MES()
BEGIN
  LOCAL choice;
  initStorage();
  choice:=0;

  // Clear Terminal screen
  PRINT();
 
  REPEAT
    IF language=="english" THEN
      IF verbose==1 THEN
        CHOOSE(choice,"Equation Set Manager","List Sets","New Set","Edit Set","Delete Set","Quiet mode", "Change language","Exit");
      ELSE
        CHOOSE(choice,"Equation Set Manager","List Sets","New Set","Edit Set","Delete Set","Verbose mode", "Change language","Exit");
      END;
    ELSE
      IF verbose==1 THEN
        CHOOSE(choice,"Gestor de equações","Listar Sets","Novo Set","Editar Set","Apagar Set", "Modo silencioso", "Mudar linguagem", "Sair");
      ELSE
        CHOOSE(choice,"Gestor de equações","Listar Sets","Novo Set","Editar Set","Apagar Set", "Modo verboso", "Mudar linguagem", "Sair");
      END;
    END;
    IF choice==1 THEN listSets(); END;
    IF choice==2 THEN newSet(); END;
    IF choice==3 THEN editSetMenu(); END;
    IF choice==4 THEN deleteSet(); END;
    IF choice==5 THEN
      IF verbose==1 then verbose:=0 ELSE verbose:=1 END;
    END;
    IF choice==6 THEN
      IF language=="english" then language:="portuguese" ELSE language:="english" END;
    END;
  UNTIL choice==7 OR choice==0;
END;