Sindbad~EG File Manager
// Original date 10.30.23, author M. Beeson
// modified 11.3-10.23 for the ParseAndDisplay example
// 11.11.23 made all functions static. This file exports zero functions; it's
// 'called' through sockets.
// 11.11.23 changed init_heap to create_heap
// 11.12.23 added DocVarData
// 12.12.23 moved main to main.c
// 12.16.23 added reset_heap() and heapmax() calls
// 12.20.23 added reset_heap() and heapmax() in ProcessAskProblemsSVG
// 1.29.24 modified process_initSymbolDocFromLibrary
// 1.29.24 added process_autoStep
// 2.4.24 added process_autoFinish
// 3.2.24 moved setupdata call below set_svgDevice call
// 3.3.24 modified process_message to fail without crashing on incorrectly-formatted messages.
// 3.3.24 added code to process the graphButton message
// 3.19.24 added process_incrementParameter and process_decrementParameter
//3.23.24 made sendGraphDocument take only one argument
// 4.6.24 wrote process_selectedRectangleSymbol
// 4.7.24 called MakeSelectorMenu
// 4.12.24 wrote code involving NeedsArgPrompt in process_selectMenuChoice
// 4.16.24 added process_showStep
// 4.16.24 MakeSelectorMenu now needs a third arg, so I passed one.
// 4.19.24 changed currentline to activeline in process_selectedRectangleSymbol
// 4.19.24 added process_unSelectRectangle
// 4.19.24 modified process_selectedRectangleSymbol to remove old selected rectangle
// 4.24.24 added if(!err) near the end of execOnSelectedTerm
// 4.25.24 made processExecWithArg also call finish_exec if the op failed,
// and also delete the selected term in that case.
// 5.7.24 wrote and called init_parser in prepareDocument().
// 5.8.24 modified init_parser to only set complex on COMPLEX_NUMBERS
// 5.12.24 removed the call to set_history from prepareDocument, search "an error"
// 5.12.24 modified init_parser to take the topicnumber as arg
// 5.12.24 corrected process_askSubjectStrings, NSUBJECTS-1 instead of NSUBJECTS
// 5.13.24 added set_language call to prepareDocument.
// 6.8.24 process_selectMenuChoice should NOT delete the selected term, so I commented out that code.
// 6.9.24 corrected sscanf in process_checkArg
// 6.10.24 worked on process_execOpWithArg a lot
// added coded near SELECT in process_selectedRectangleSymbol
// 6.11.24 more changes where showStep is called
// 6.13.24 made execOnSelectedTerm deal with pushpending and poppending
// 6.13.24 made processMessage return 1 if verify fails.
// 6.21.24 passed reasonstart in parameter to symbolWindowResized.
// 7.2.24 modified index_of_selected_equation to return -1 when q.path==NULL
// 7.9.24 added process_graphMoved
// 7.13.24 added askPointSlope and doublezoomin, doublezoomout
// 7.15.24 rewrote process_askPointSlope to return a term instead of text.
// 7.15.24 modified process selectMenuChoice to not generate an ERROR if
// there's no selected term.
// 7.23.24 called snap() in process_askPointSlope, and had it set g->crosshairsflag.
// 7.25.24 modified all the zoom messages to use 0 as the zoom center if it's visible.
// 7.26.24 correct the fourth line of process_graphMoved;
// and corrected the loop in process_graphMoved.
// 8.2.24 modified init_parser
// 8.8.24 corrected process_askProblemsSVG and process_askProblemsText
// 8.17.24 added process_setupAndCheckGraph
// 8.22.24 added t = pDocData->function in process_initGraphDocFromLibrary
// 8.23.24 added process_drawAll
// 8.23.24 added setup_data call to process_setupAndCheckGraph
// 8.23.24 added set_language(languagenumber) in process_setupAndCheckGraph
// 7.23.24 added pdocData->mainchoice = graphtype; in process_setupAndCheckGraph
// 8.25.24 modified process_parseAndDisplay to produce an <svg> element, not just
// SVG code to go inside sucn an element.
// 9.2.24 added code to process_setupAndCheckGraph to stash initialConditions on ODE problems
// 9.5.24 process_setupAndCheck should pass pDocData->graphproblemtype
// rather than pDocData->mainchoic to setupdata.
// 9.6.24 added code to process_setupAndCheckGraph to handle initial conditions like sqrt(pi).
// 9.7.24 added code containing pDocData->DocVarData.varinfo[nvariables-2];
// 9.8.24 aded process_circularAspect
// 9.10.24 added code to turn on direction fields by default if they are applicable.
// 9.11.24 added pDocData->language = languagenumber; in prepareDocument.
// 9.14.24 made process_finished set finishedflag to 1 temporarily
// 9.18.24 added process_setupAndCheckSymbol
// 9.19.24 added process_startSolving
// 9.23.24 modified setupAndCheckSymbol to allocate papyrus before setup_and_check.
// 9.24.24 changed "parsererror" to "parserError" in process_parseAndDisplay
// 9.24.24 changed process_setupAndCheckSymbol to pack a parser error as SVG.
// 9.24.24 and in both the last changes, make the color "red"
// 9.24.24 made process_selectMenuChoice try to apply the arg to the equation containing
// the selected term, not just to the whole current line.
// 9.25.24 added call to input_error_message in setup_and_check
// 9.28.24 added set_control_flags(problemtype,topic) in process_setupAndCheckSymbol
// 9.29.24 added code involving pDocData->DocControlData.minmax_interval);
// at the end of process_setupAndCheckSymbol
// 10.3.24 made process_setupAndCheckSymbol check the return value of final_adjustments
// and return an error message
// 10.4.24 corrected process_askProblemsText
// 10.17.24 if setup_and_check returns an err, send an error message to the browser.
// 10.21.24 made process_incrementParameter and process_decrementParameter
// set storedDoc->active_parameter.
// 10.22.24 removed creation of papyrus from process_setupAndCheckSymbol,
// as it's now done by allocate_doc_data.
// 10.24.24 added process_updateParameters
// 10.24.24 added process_parameterIncrementChanged.
// 10.27.24 made process_askPointSlope work on ODE and ODE2.
// 10.29.24 added code to process_initGraphDocFromLibrary to initialize
// parameter values for initial values of ODE2 and ODE
// 11.20.24 incremented totalsteps at the end of process_execOpWithArg and process_selectMenuChoice
// 12.6.24 added g->adjustToPaper = 0; in process_graphMoved
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>
#include <math.h>
#include <time.h>
#include "uthash.h"
#include "heap.h"
#include "heaps.h"
#include "terms.h"
#include "parser.h"
#include "pcontrol.h"
#include "defns.h" // needed by vaux
#include "vaux.h" // vardata
#include "proverdl.h"
#include "model.h"
#include "arith.h"
#include "polyval.h"
#include "graphstr.h"
#include "probtype.h"
#include "coord.h"
#include "termtoSVG.h"
#include "svgGraph.h" // set_svgDevice
#include "mygcvt.h"
#include "mpdoc.h"
#include "getprob.h" // setup_and_check
#include "ProcessMessage.h"
#include "ProblemLibrary.h"
#include "english.h" // english
#include "docdata.h" // init_doc_data
#include "natlang.h" // set_language
#include "display1.h" // TENPOINT
#include "autostep.h" // autostep
#include "bigrect.h" // BIGRECT
#include "lterm.h" // ltermlist
#include "papyrus.h" // init_papyrus
#include "undo.h" // undo
#include "svgSymbolText.h"
#include "natlang.h" // get_language_number
#include "subjectstr.h" // NSUBJECTS
#include "grafinit.h" // setupdata
#include "sendDocument.h" // sendSymbolDocument, sendGraphDocument
#include "grbutton.h" // graph_button
#include "select.h" // select_term
#include "dowith.h" // lcons
#include "rgb.h" // RGBA
#include "selectop.h" // MakeSelectorMenu
#include "fexec.h" // fexec
#include "exec.h" // exec
#include "automode.h" // get_activeline
#include "constant.h" // one
#include "snap.h" // snap
#include "speedbar2.h" // get_hint
#include "prover.h" // interval_as_and
#include "activate.h" // init_polyvalflags
#include "english.h" // ophelp
#include "dispfunc.h" // atom_string
typedef struct { const char *line[10]; /* fixed size to avoid needing malloc */
int nlines;
} message; // needed by execute.h
#include "checkarg.h" // needed by execute.h
#include "execute.h" // finished
#include "getarg.h" // needs_arg
#include "optable.h" // access_optable
#include "tdefn.h" // init_parser needs topic constants
#include "verify.h" // verify
#include "activedoc.h" // GetActiveDoc
#include "pvalaux.h" // topflatten
#include "operator.h" // linear_equations_by_selection
#include "integral.h" // tryparts
#include "verify.h" // destroyDocument
#include "deval.h" // deval
#include "grpaper.h" // GetGraphPaper
#include "pstring.h" // pstring
// pre-document messages
static int process_askTopicStrings(PDOCDATA storedDoc, char *language, char *outbuffer,int outbuffersize);
static int process_askSubjectStrings(PDOCDATA storedDoc, char *language, char *outbuffer,int outbuffersize);
static int process_askProblemsText(PDOCDATA pDoc, char *param, char *outbuffer,int outbuffersize);
static int process_askProblemsSVG(PDOCDATA pDoc, char *param, char *outbuffer, int outbuffersize);
static int process_asknProblems(PDOCDATA storedDoc,char *param, char *outbuffer,int outbuffersize);
static int process_initSymbolDocFromLibrary(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_initGraphDocFromLibrary(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_setupAndCheckGraph(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_setupAndCheckSymbol(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_startSolving(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
// Ajax messages, response is NOT the whole document but a message for local use in javascript.
static int process_checkArg(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
//static int process_unSelectRectangle(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_askPointSlope(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize);
static int process_parameterIncrementChanged(PDOCDATA storedDoc,char *param,char *outbuffer, int outbuffersize);
// messages from and to a symbol document, response is sendSymbolDoc
static int process_autoStep(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_showStep(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_autoFinish(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_undo(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_hint(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_finished(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_selectedRectangleSymbol(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_selectMenuChoice(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_execOpWithArg(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize);
static int process_symbolWindowResized(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
// one message from a symbol document to a graph document
static int process_graphButton(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
// messages from and to a graph document, response is SendGraphDoc
static int process_selectedRectangleGraph(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_incrementActiveParameter(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_decrementActiveParameter(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_decrementParameter(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_incrementParameter(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_horizontalzoomout(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_horizontalzoomin(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_verticalzoomout(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_verticalzoomin(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_doublezoomout(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_doublezoomin(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_graphWindowResized(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_toggleDirectionField(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_toggleErase(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_circularAspect(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_setGraphPaper(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_updateParameters(PDOCDATA storedDoc, char *param, char *outbuffer, int outbuffersize);
static int process_drawAll(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int process_zoomAtPoint(PDOCDATA storedDoc, char *param, char *outbuffer, int outbuffersize);
static int process_graphMoved(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize);
static int use_dfield(term t);
/*________________________________________________________________________*/
static int process_parseAndDisplay (PDOCDATA storedDoc, char *param, char *outbuffer, int outbuffersize)
// handle the parseAndDisplay message
{
char *rest; // unparsed part
int err;
term t;
memset(outbuffer,0,outbuffersize);
int language_number = get_selected_language(); // returns ENGLISH if none has been set
set_language(language_number);
err = bparse(get_parser_flags(),&t,param,&rest);
if(err != 0) // failure to parse
{ printf("parse failed\n");
char *message = (char *) pem(err); // parser error message in the current language
char id[24];
svgSymbolTextElement(message, outbuffer, id, "parserError", outbuffersize, RGB(255,0,0),0,0);
int byteswritten = (int) strlen(outbuffer);
outbuffer += byteswritten;
outbuffersize -= byteswritten;
return 0; // message successfully processed
}
printf("Successful parse\n");
// err = termtoSVG(t,outbuffer, outbuffersize, 16,32);
err = termtoSVGElement(t, outbuffer, "parsedTerm","termSVG", outbuffersize);
if(err)
{ send_error("term_to_SVG failed.", outbuffer);
return 0; // message successfully processed
}
// printf("%s",outbuffer); // for debugging: SVG graphics code will be printed on the console.
return 0;
}
/*____________________________________________________________________________*/
static int process_askProblemsText(PDOCDATA storedDoc, char *param, char *outbuffer, int outbuffersize)
/* handle the askProblemsText message; return 0 for success or nonzero for error.
Outbuffer will get a newline-separated list of entries like "x^2+1" (without quotes)
containing the problems in the MathXpert Problem Library for the topic specified in param.
Also a newline follows the last entry.
Param can contain either a number or a string. If a string it's a quote of one of the
identifiers in tdefn.h.
*/
{
char temp[300]; // enough to hold the longest problem
char **problems;
char *next = outbuffer;
memset(outbuffer,0,outbuffersize);
int topicnumber;
if(param == NULL)
{
strcpy( outbuffer, "You didn't pass anything in the parameter to askStoredProblems.");
return 1;
}
// param is either a topic number or a topic string, a quoted topic name as in tdefn.h
int nfields = sscanf(param,"%d",&topicnumber);
if (nfields == 0) // param contains a string
topicnumber = stringToTopic(param);
if (topicnumber < 0)
{
printf("Param to askStoredProblems did not contain a legal topic.\n");
return 1; // error
}
// Now we have topicnumber.
problems = getStoredRawProblems(topicnumber);
for(int k = 0;; k++)
{ char *x = problems[k];
int problemnumber;
int nfields = sscanf(x, "%d.%[^@\n]", &problemnumber, temp);
// read up to the first newline or @ character
// @ characters introduce author's commentaries, which we don't want here.
if (nfields < 2)
{
// we have passed the last problem, so don't write anything to next
break;
}
strcpy(next,temp);
strcat(next,"\n");
next += strlen(next);
}
*next = '\0';
++next;
return 0;
}
/*___________________________________________________________________________*/
static int process_asknProblems(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize)
/* Handle the asknProblems message; return 0 for success or nonzero for error.
*param is not used (but it must be non-NULL, e.g. "dummy")
Outbuffer will get a newline-separated list of numbers, representing an
array nProblems[topic], telling how many problems there are for that topic.
(This message can be used by the interface to set up parts of the interface that
need those numbers before downloading all the problems.)
Return 0 for success, nonzero for error.
*/
{ char **problems;
int maxtopic = getmaxTopic();
char *next = outbuffer;
int k;
memset(outbuffer,0,outbuffersize);
for(int t = 1; t <= maxtopic; t++)
{ problems = getStoredRawProblems(t);
for(k = 0; *(problems[k]) != '\0' && k < 100; k++)
continue;
if(k == 100)
assert(0); // problems in C code must have been malformed to get here.
// now k is the number of problems for topic t, because the last problem is an empty string. T
if(t < maxtopic)
sprintf(next,"%d\n",k);
else
sprintf(next,"%d",k); // no newline on the last entry.
next = outbuffer + strlen(outbuffer);
}
return 0; // this message never fails
}
/*___________________________________________________________________________*/
static int process_askTopicStrings(PDOCDATA storedDoc, char *language, char *outbuffer,int outbuffersize)
/* Handle the askTopicStrings message; return 0 for success or nonzero for error.
The parameter, language, is the name of a language, such as "english" or "french",
or its number, such as ENGLISH or FRENCH.
Outbuffer will get a newline-separated list of strings, each naming a topic.
(This message can be used by the interface to set up parts of the interface that
enable a user to select a topic.)
Note: storedDoc must exist, but it need not be the active document.
Return 0 for success, nonzero for error.
*/
{
int maxtopic = getmaxTopic();
char *next = outbuffer;
const char *temp;
int language_number;
int nfields = sscanf(language,"%d",&language_number);
if(nfields == 0)
language_number = get_language_number(language);
// returns ENGLISH if the string language is not the name of a supported language
set_language(language_number);
memset(outbuffer,0,outbuffersize);
for(int t = 1; t <= maxtopic; t++)
{ temp = topicstr(t);
if(t < maxtopic)
sprintf(next,"%s\n",temp);
else
sprintf(next,"%s",temp); // no newline on the last entry.
next = outbuffer + strlen(outbuffer);
}
return 0; // this message never fails
}
/*___________________________________________________________________________*/
static int process_askSubjectStrings(PDOCDATA storedDoc, char *language, char *outbuffer,int outbuffersize)
/* Handle the askSubjectStrings message; return 0 for success or nonzero for error.
*param is the name of a language, such as "english" or "french", or its number.
Outbuffer will get a newline-separated list strings, each naming a subject.
(This message can be used by the interface to set up parts of the interface that
enable a user to select a subject.)
Note: storedDoc must exist, but it need not be the active document.
Return 0 for success, nonzero for error.
*/
{
char *next = outbuffer;
const char *temp;
int language_number;
int nfields = sscanf(language,"%d",&language_number);
if(nfields == 0)
language_number = get_language_number(language);
// returns ENGLISH if the string language is not the name of a supported language
set_language(language_number);
memset(outbuffer,0,outbuffersize);
for(int t = 0; t < NSUBJECTS; t++)
{ temp = subjectstr(t);
if(t < NSUBJECTS-1)
sprintf(next,"%s\n",temp);
else
sprintf(next,"%s",temp); // no newline on the last entry.
next = outbuffer + strlen(outbuffer);
}
return 0; // this message never fails
}
/*____________________________________________________________________________*/
static int process_askProblemsSVG(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize)
/* Handle the askProblemsSVG message; return 0 for success or nonzero for error.
Param is a topic. It can be either a number or a string.
If a string it's a quote of one of the identifiers in tdefn.h.
Outbuffer will get a newline-separated list of entries.
Each entry is a newline-separated list of svg graphics
(one line for each problem) that also ends with a newline.
Thus if you explode outbuffer on double-newline you'll get
an array of newline-separated string, which is SVG code
for a problem, but without any <svg> tags, so it can be inserted
into an existing <svg> element.
These strings represent the problems in the MathXpert Problem Library for t
topic specified in param.
Return 0 for success, nonzero for error.
*/
{
char **problems;
char *next = outbuffer;
memset(outbuffer,0,outbuffersize);
int topicnumber;
int remaining = outbuffersize;
if(param == NULL)
{
strcpy( outbuffer, "You didn't pass anything in the parameter to askStoredProblems.");
return 1;
}
// param is either a topic number or a topic string, a quoted topic name as in tdefn.h
int nfields = sscanf(param,"%d",&topicnumber);
if (nfields == 0) // param contains a string
topicnumber = stringToTopic(param);
if (topicnumber < 0)
{
printf("Param to askProblemsSVG did not contain a legal topic.\n");
return 1; // error
}
// Now we have topicnumber.
problems = getStoredRawProblems(topicnumber);
void *savenode = heapmax(); // used below for reset_heap
for(int k = 0;; k++)
{ char *x;
char temp[300]; /* Usually does not have to hold the whole problem, just the first string after the period, but in certain cases
it needs to be longer, see a few lines below. */
int problemnumber;
int nfields = sscanf(problems[k], "%d.%s", &problemnumber, temp);
if (nfields < 2)
break;
// printf("%s\n",problems[k]); //was used for debugging
x = problems[k];
while (*x && *x != '.')
++x;
++x;
while(*x == ' ')
++x;
// now x contains the problem
assert(*x); // there has to be something after the period
char *marker;
if( (marker = strchr(x,'|'))|| (marker = strchr(x,'@')))
{ // @ introduces an author's comment and |
// introduces range restrictions, both not
// supported in WebMathXpert, so delete that part from x,
// but without altering the original problem string.
assert(marker-x < sizeof(temp));
strncpy(temp,x,(int) (marker-x));
temp[marker-x] = '\0';
x = temp;
}
term t;
char *rest;
int err = bparse(get_parser_flags(),&t,x,&rest);
if(err)
{ printf("%s did not parse\n",x);
printf("%s\n",pem(err));
printf("%s\n",rest);
printf("%c\n",get_parser_flags()->decimalchar);
assert(0); // stored problems do indeed parse!
}
int old = (int) strlen(next);
termtoSVG(t,next,remaining,10,10); // always ends in a single newline
// printf("%d %s\n",k,next); // was used for debugging
strcat(next,"\n"); // creates a double newline to separate problems
int used = (int)strlen(next)-old;
next += strlen(next);
remaining = remaining - used;
if (used <= 0)
assert(0);
assert(remaining > 0);
}
printf("Finished topic %s\n", param); // debug
reset_heap(savenode); // otherwise we might run out of heapspace when
// fetching the problems for all the topics.
return 0;
}
/* _________________________________________________________________________*/
static void init_parser(parser_control *p, int problemtype, int topicnumber)
{ if(problemtype == COMPLEX_NUMBERS
|| topicnumber == _complex_arithmetic
|| topicnumber == _complex_trig
|| topicnumber == _complex_quadratics
|| topicnumber == _complex_cubics
|| topicnumber ==_complex_roots
)
p->complex = 1; // allow complex numbers
else
p->complex = 0;
p->separator= 1; // no grouping of bignum digits
p->decimalchar = get_decimalpoint_char(get_selected_language());
p->unwritten_mult = 1;
p->long_identifiers = 2; // one-character identifiers only, except subscripts OK
// it must be 0,2, or 3. Not 1.
strncpy(p->functions,"fFgG",8);
p->letflag = 0;
if(problemtype == LIMITS ||
problemtype == DIFFERENTIATE_FROM_DEFN ||
problemtype == LHOPITAL
)
strcpy(p->functions,"fFgG");
else
strcpy(p->functions,"fFgGhH");
set_complex_frozen(0); /* 'complex' can still be changed */
if(problemtype == COMPLEX_CONTOUR_PLOT ||
problemtype == POLYROOTS ||
problemtype == COMPLEX_NUMBERS ||
COMPLEXTOPIC(topicnumber)
)
{ set_complex(1);
set_complex_frozen(1);
/* complex variables must be on so that setup_varlist makes
the variables be of type DCOMPLEX; this is needed so ceval
will work properly in drawing complex contour graphs */
p->complex = 1;
}
bparse(NULL,NULL,NULL,NULL);
/* causes parser_initialized to be set to 0, so next time
the file-scope statics in parser.c will be set to coincide with
the parser_flags that have been changed above. */
}
/*____________________________________________________________________________*/
static int prepareDocument(PDOCDATA pDocData, char *param)
{ int topicnumber, problemnumber,reasonstart,windowwidth,papyrus_height;
char languagename[64];
int nfields = sscanf(param,"%d+%d+%d+%d +%s",&topicnumber,&problemnumber,&reasonstart,&windowwidth,languagename);
if(nfields < 5)
{ printf("Incorrect parameter to message\n");
printf("Need topicnumber+problemnumber+reasonstart+width+language\n");
printf("This should have been caught by verify\n");
return 1;
}
if(pDocData->heap) // then we're reusing a document
{ destroy_document_data(pDocData);
}
int languagenumber = get_language_number(languagename);
// returns ENGLISH if languagename is not recognized or supported
set_language(languagenumber);
pDocData->language = languagenumber;
int nProblems = getnProblems(topicnumber);
if(problemnumber > nProblems)
{ printf("problemnumber %d too large for topic %d\n", problemnumber,topicnumber);
return 1;
}
char problemtext[1000];
char ranges[1000];
char commentary[COMMENTARYBUFLENGTH];
int err = getStoredProblem(topicnumber,problemnumber,problemtext,ranges, commentary);
if(err)
return 1;
term t;
char *rest;
pDocData->currenttopic = topicnumber;
pDocData->problemsource = SOURCE_MATHPERT;
err = init_doc_data(SYMBOLDOC, pDocData);
assert(!err);
pDocData->language = languagenumber;
Papyrus *pPapyrus = (Papyrus *) calloc(1,sizeof(Papyrus));
assert(pPapyrus != NULL);
pDocData->papyrus = pPapyrus;
papyrus_height = 1L << 20; // some very large number; height is irrelevant
init_papyrus(pDocData, windowwidth, papyrus_height, reasonstart);
activate(pDocData);
int problemtype = pDocData->problemtype; // set by init_doc_data
set_control_flags(problemtype,topicnumber);
const char *reason = english(1188); /* the problem */
store_reason(pDocData,0,reason);
printf("reason is %s\n", reason);
pDocData->problemnumbers[SOURCE_MATHPERT] = problemnumber;
parser_control *parser_flags = get_parser_flags();
init_parser(parser_flags,problemtype,topicnumber);
err = bparse(parser_flags,&t,problemtext, &rest);
if(err)
assert(0);
pDocData->function = t; // setup_and_check looks for it there.
err = setup_and_check(pDocData);
if(err)
assert(0);
// currentline is still -1, which allows final_adjustments
// to make assumptions for the domain. When currentline >= 0,
// while solving equations, you can't make assumptions involving
// the eigenvariable.
t = pDocData->function; // it may have been "enhanced", e.g. derivatives or integrals added
final_adjustments(pDocData->problemtype, t); // calls set_history
increment_currentline(); // to zero
// so calling set_history here would be an error, as it would overwrite
// what final_adjustments put in history, and that may have some info bits different.
return 0;
}
/*____________________________________________________________________________*/
static int process_initSymbolDocFromLibrary(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
// parameter has the form topicnumber+problemnumber+reasonstart+windowwidth+language
// fill those into pDocData and complete initializing that document.
// return 0 for success, nonzero for error
// respond to the message with the first line and reason "the problem"
// (in the correct language)
{ int err = prepareDocument(pDocData, param);
if(err)
return err; // incorrect parameter for example
err = sendSymbolDocument(pDocData, outbuffer, outbuffersize);
printf("%s",outbuffer); // debug
return err;
}
/*____________________________________________________________________________*/
static int process_initGraphDocFromLibrary(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
/* parameter has the form topicnumber+problemnumber+language+width+height
language is given by language number (e.g. ENGLISH or FRENCH) or by name
width and height are width and height of rectangle R which is where the
graphs will be written, i.e. browser window excluding the Graph Toolbar.
The Engine knows nothing about the Graph Toolbar and doesn't need to know.
Fill those data items into pDocData and complete initializing that document.
return 0 for success, nonzero for error
Respond to the message with sendGraphDocument.
*/
{ char languagename[33];
if(pDocData->heap) // then we're reusing a document
{ destroy_document_data(pDocData);
}
int windowwidth, windowheight, topic,languagenumber,problemnumber;
int nfields = sscanf(param,"%d+%d+%d+%d+%d",
&topic,
&problemnumber,
&languagenumber,
&windowwidth,
&windowheight
);
if(nfields < 5)
{ nfields = sscanf(param,"%d+%d+%32[^+]+%d+%d",
&topic,
&problemnumber,
languagename,
&windowwidth,
&windowheight
);
if(nfields < 5)
{ printf("Incorrect parameter to initGraphDocFromLibrary\n");
printf("Need topicnumber+problemnumber+language+windowwidth+windowheight");
return 1;
}
languagenumber = get_language_number(languagename);
}
char problemtext[1000];
char ranges[1000];
char commentary[2000];
ranges[0] = commentary[0] = '\0';
int err = getStoredProblem(topic,problemnumber,problemtext,ranges,commentary);
if(err)
return 1;
term t;
char *rest;
pDocData->currenttopic = topic;
pDocData->problemsource = SOURCE_MATHPERT;
err = init_doc_data(GRAPHDOC, pDocData);
assert(!err);
pDocData->language = languagenumber;
pDocData->problemnumbers[SOURCE_MATHPERT] = problemnumber;
bparse(get_parser_flags(),&t,problemtext, &rest);
// it must parse because it is from the MathXpert Problem Library
pDocData->function = t;
err = setup_and_check(pDocData);
t = pDocData->function; // enhance_problem may have changed it, e.g. changing x^3 to y = x^3
if(err)
assert(0); // because the problem is from the Problem Library
activate(pDocData); // needed a few lines below by initialize_parameter
rect r;
r.left = 0;
r.top = 0;
r.right = windowwidth;
r.bottom = windowheight;
set_svgDevice(outbuffer, outbuffersize,&r);
if( ranges[0] != '\0')
{ term q;
char *rest;
int err = bparse(get_parser_flags(),&q,ranges,&rest);
if(err)
assert(0); // it's a stored problem, ranges have to parse
t = or(t,q);
}
setupdata(&pDocData->ngraphs, &pDocData->mainchoice, t, pDocData->graphs);
if(pDocData->graphproblemtype == ODE2 ||
pDocData->graphproblemtype == ODE
)
{
// We have to initialize parameters for the initial conditions
term *varlist = pDocData->DocVarData.varlist;
term t = varlist[0];
term x = varlist[1];
term y = varlist[2];
// use the default initial values (0,1,1)
SETVALUE(t,0.0);
SETVALUE(x,1.0);
SETVALUE(y,1.0); // so they will deval to the initial values
// now they still have to be entered as parameter values.
int nvariables = pDocData->DocVarData.nvariables;
initialize_parameter(nvariables-2,0,1);
initialize_parameter(nvariables-1,0, 1);
pDocData->graphs[0]->initial_values[0] = &(pDocData->DocVarData.varinfo[nvariables-2].realpart);
pDocData->graphs[0]->initial_values[1] = &(pDocData->DocVarData.varinfo[nvariables-1].realpart);
*(pDocData->graphs[0]->initial_values[0]) = 1;
*(pDocData->graphs[0]->initial_values[1]) = 1;
}
// OK, now the document is good to go. Time to respond to the message.
// The response is SVG graphics to draw the graph.
return sendGraphDocument(pDocData);
}
/*____________________________________________________________________________*/
static int process_setupAndCheckGraph(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
/* parameter has the form "problemtext"+graphtype+language+graphwidth+height.
graphtype is a number, one of the values in mainchoi.h (but not all values
there are supported).
language is given by language number (e.g. ENGLISH or FRENCH) or by name
width and height are width and height of rectangle R which is where the
graphs will be written, i.e. browser window excluding the Graph Toolbar.
The Engine knows nothing about the Graph Toolbar and doesn't need to know.
Fill those data items into pDocData and try to initialize that document.
In particular:
1. Parse the problemtext. It may contain graph ranges already, or not
2. set up the varlist
3. pick the independent and dependent variable(s),
4. initialize the parameters
5. initialize the graph ranges.
6. Specify whether aspect-ratio 1 is required.
return 0 for success, nonzero for error
Respond to the message with
-- if it parses, The <svg> element containing the (typeset) problem.
-- if it doesn't parse, ??
Notes: -- We do not ask about dependent and independent variables, and
whether some variables depend on the independent variable or are
parameters. We just make reasonable choices.
-- We do not ask about graph ranges. User can change them after
drawing the graph.
*/
{ char languagename[33];
char problemtext[MAXPROBLEMTEXT];
char ranges[1000];
term t; // for the parsed problem
char *rest; // pointer to the unparsed part of problemtext
int graphwidth, windowheight,graphtype,languagenumber;
if(pDocData->heap) // then we're reusing a document
{ destroy_document_data(pDocData);
}
char *format_string = "\"%[^\"]\"+%d+%d+%d+%d";
int nfields = sscanf(param, format_string,
problemtext,
&graphtype,
&languagenumber,
&graphwidth,
&windowheight
);
if(nfields < 5)
{
format_string = "\"%[^\"]\"+%d+%32[^+]+%d+%d";
nfields = sscanf(param, format_string,
problemtext,
&graphtype,
languagename,
&graphwidth,
&windowheight
);
if(nfields < 5)
{ printf("Incorrect parameter to setupAndCheckGraph\n");
printf("Need \"problemtext\"+graphtype+language+windowwidth+windowheight");
return 1;
}
languagenumber = get_language_number(languagename);
}
pDocData->language = languagenumber;
pDocData->mainchoice = graphtype;
char *parameterInterval;
char *initialConditions; // for use with ODEs
int topic;
char *marker;
term tt; // to stash the parameter interval
SETFUNCTOR(tt,0,0); // until something is put there
// we have to set topic from graphtype.
switch(graphtype)
{
case ORDINARY: /* y=f(x) */
topic = _ordinary_graph;
break;
case COMPARE_SAME: /* two graphs on the same axes */
topic = _compare_same;
break;
case COMPARE_DIFFERENT: /* two graphs on different axes */
topic = _compare_different;
break;
case MC_INEQ: /* y < f(x) or f(x) < y < g(x) */
topic = _graph_ineq;
break;
case MC_SET: /* arbitrary relation with shaded interior */
topic = _graph_set;
break;
case PARAMETRIC:
topic = _parametric_graph;
break;
case POLAR_CIRCULAR: /* axes drawn as circles and radial lines */
topic = _polar_graph;
break;
case POLAR: /* user specifies xmax and ymax */
topic = _polar_graph;
break;
case POLYROOT: /* graph complex roots of a polynomial */
topic = _polyroots;
break;
case ODE: /* graph y' = f(x,y) */
/* we draw in both directions, and direction
fields work. */
topic = _solve_ode;
// problemtext matches "equation; x0,y0" or maybe "equation; (x0,y0)"
marker = strchr(problemtext,';');
if(marker)
{ initialConditions = marker+1;
*marker = '\0';
}
break;
case ODE2: /* Two odes y' = f(x,y) and x'= g(x,y) */
/* Drawing is over a parameter interval, one
direction only, but direction fields work. */
// problemtext looks like "two equations; (0<= t <= 2pi), x0,y0"
marker = strchr(problemtext,';');
if(marker)
{ parameterInterval = marker+1;
*marker = '\0'; // marking the end of the two equations
marker = strchr(parameterInterval,',');
assert(marker);
initialConditions = marker+1;
// we cannot parse parameterInterval yet because the
// document heap does not exist until allocate_data is called below,
// inside init_docdata
}
topic = _solve_two_odes;
break;
case ODESYSTEM: /* graphically solve an ode x'=f(t,x,y), y'= g(t,x,y) */
/* if t does not occur, mainchoice should be ODE2 instead */
/* direction fields don't work */
// problemtext matches "equation; x0,y0" or maybe "equation; (x0,y0)"
marker = strchr(problemtext,';');
if(marker)
{ initialConditions = marker+1;
*marker = '\0';
}
topic = _solve_two_odes;
break;
case HDE: /* poly(derivs of y) = f(x,y), linear in highest y-deriv */
/* draws in both directions, direction fields don't work */
// problemtext matches "equation; x0,y0,y0',0''..." or maybe "equation; (x0,y0)"
marker = strchr(problemtext,';');
if(marker)
{ initialConditions = marker+1;
*marker = '\0';
}
topic = _high_order_ode;
break;
case RELATION: /* graph a relation */
topic = _graph_relation;
break;
case CONTOUR: /* contour_plot */
topic = _contour_plot;
break;
case COMPLEX_CONTOUR:
topic = _complex_contour_plot;
break;
case COMPAREDERIV:
topic = _comparefandfprime;
break;
case COMPAREDERIVS:
topic = _comparetwoderivs;
break;
case RIEMANNSUMS:
topic = _riemann_sums;
break;
case TRAPEZOIDRULE:
topic = _trapezoid_rule;
break;
case SIMPSONSRULE:
topic = _simpsons_rule;
break;
default:
assert(0);
}
pDocData->currenttopic = topic;
pDocData->problemsource = SOURCE_USER;
int err = init_doc_data(GRAPHDOC,pDocData);
assert(!err);
// the only way it can fail is if we're out of memory for allocate_doc_data,
// and there's nothing we can do about that.
if(graphtype == ODE2)
{ // Now we can continue processing parameterInterval, now that we have a heap
*marker = '\0'; // marking the end of parameterInterval
// now, the parameterInterval is validated by the interface somewhat
int err = bparse(get_parser_flags(),&tt,parameterInterval,&rest);
if(err || !interval_as_and(tt))
{ // FINISH THIS--report an error
assert(0); // for now
}
}
ranges[0] = '\0';
pDocData->language = languagenumber;
set_language(languagenumber); // also needed! to get tooltip language right
pDocData->problemnumbers[SOURCE_USER] = 1;
// WebMathXpert does not keep track of previously entered problems;
// the user interface can offer them as suggestions in an edit box.
// problemtext starts with an escaped quote, so skip that.
err = bparse(get_parser_flags(),&t,problemtext, &rest);
if(err)
{ // handle parser errors by sending the error to the user
const char *errmsg = pem(err);
rect r2;
r2.left = 0;
r2.top = 0;
r2.right = graphwidth;
r2.bottom = windowheight;
set_svgDevice(outbuffer, outbuffersize, &r2);
strcpy(outbuffer,errmsg);
return 0; // this message is successfully processed, despite the parse error
}
pDocData->function = t; // for example x^3-ax
pDocData->graphproblemtype = graphtype;
err = setup_and_check(pDocData); // sets up varlist etc.
if(err)
{
const char *errmsg = input_error_message(err);
rect r2;
r2.left = 0;
r2.top = 0;
r2.right = graphwidth;
r2.bottom = windowheight;
set_svgDevice(outbuffer, outbuffersize, &r2);
strcpy(outbuffer,errmsg);
return 0; // this message is successfully processed, despite the parse error
}
t = pDocData->function; // for example y = x^3-ax, after enhance_problem
// setupdata requires an svgDevice, so we have to set one
rect r;
r.left = 0;
r.right = graphwidth;
r.top = 0;
r.bottom = windowheight;
set_svgDevice(outbuffer, outbuffersize, &r);
if(FUNCTOR(tt) != 0)
{ // then tt is the parameter interval, a <= t <= b.
term left = ARG(0,tt);
if(FUNCTOR(left) == 0)
assert(0);
//term right = ARG(1,tt); // unused, but I wanted to inspect it while debugging
term indvar = ARG(1,left);
// The independent variable must come first in varlist
// this has to happen before setupdata, so we don't have the graph structures yet.
// Leave the other variables in their original order.
term *varlist = pDocData->DocVarData.varlist;
int nvars= pDocData->DocVarData.nvariables;
int k = 0;
for(k = 0; k<nvars; k++)
{ if(equals(varlist[k],indvar) && k != 0)
{
term temp = varlist[k];
for(int i = k-1;i>=0;i--)
varlist[i+1] = varlist[i];
varlist[0] = temp;
break;
}
}
if( k == nvars)
{ // indvar did not occur, as in an ODE2 graph where the independent variable isn't explicit
vaux(indvar); // adds indvar to the varlist and points its args into varinfo[nvars]
swapvars(0,nvars); // stick it in at the beginning
}
}
activate(pDocData); // added 11.23.24
setupdata(&pDocData->ngraphs, &pDocData->graphproblemtype, t, pDocData->graphs);
if(pDocData->graphproblemtype == ODE)
{ // initialConditions contains "t0, y0", which have to evaluate to numbers, but might
// contain e.g. sqrt(pi). They must not contain commas though.
// Extract t0 and y0
double t0, y0;
int nfields = sscanf(initialConditions,"%lf,%lf",&t0,&y0);
if(nfields != 2)
{ nfields = sscanf(initialConditions, "(%lf,%lf)",&t0,&y0);
}
if(nfields == 2)
{ term *varlist = get_varlist();
term indvar = varlist[0];
term y = varlist[1];
SETVALUE(indvar,t0);
SETVALUE(y,y0); // this is where we keep the values of the parameters
*(pDocData->graphs[0]->initial_values[0]) = t0;
*(pDocData->graphs[0]->initial_values[1]) = y0;
}
else
{ // in case one of them contains something like sqrt(pi), parse and evaluate with deval
term conditions;
char *rest;
int err = bparse(get_parser_flags(),&conditions, initialConditions,&rest);
if(!err && FUNCTOR(conditions) == AND && ARITY(conditions)== 2)
{ deval(ARG(0,conditions),&t0);
deval(ARG(1,conditions),&y0);
if(t0 != BADVAL && y0 != BADVAL)
{ term *varlist = get_varlist();
term var0 = varlist[0];
term y = varlist[1];
SETVALUE(var0,t0);
SETVALUE(y,y0); // this is where we keep the values of the parameters
*(pDocData->graphs[0]->initial_values[0]) = t0;
*(pDocData->graphs[0]->initial_values[1]) = y0;
}
else
err = 1;
}
if(err)
{ char *errmsg = "initial conditions must evaluate to a pair of numbers.";
rect r2;
r2.left = 0;
r2.top = 0;
r2.right = graphwidth;
r2.bottom = windowheight;
set_svgDevice(outbuffer, outbuffersize, &r2);
strcpy(outbuffer,errmsg);
return 0; // this message is successfully processed, despite the parse error
}
}
}
if(pDocData->graphproblemtype == ODE2 || pDocData->graphproblemtype == ODESYSTEM)
{
// check if the independent variable occurs on the right side of
// the equations. If so it should be ODESYSTEM rather than ODE2
pDocData->graphs[0]->parameter_interval = tt;
deval(ARG(0,ARG(0,tt)),&pDocData->graphs[0]->tmin);
deval(ARG(1,ARG(1,tt)),&pDocData->graphs[0]->tmax);
term indvar = pDocData->graphs[0]->independent_variable = ARG(1,ARG(0,tt));
assert(FUNCTOR(t) == AND && ARITY(t) == 2);
term first = ARG(0,t);
term second = ARG(1,t);
assert(FUNCTOR(first) == '=' && FUNCTOR(second)== '=');
if(contains(ARG(1,first),FUNCTOR(indvar)) || contains(ARG(1,second),FUNCTOR(indvar)))
{ graphtype = pDocData->graphproblemtype = ODESYSTEM;
}
// initialConditions contains "x0, y0", which have to evaluate to numbers, but might
// contain e.g. sqrt(pi). They must not contain commas though.
// Extract x0 and y0
double x0,y0;
int nfields = sscanf(initialConditions,"%lf,%lf",&x0,&y0);
if(nfields != 2)
{ nfields = sscanf(initialConditions, "(%lf,%lf)",&x0,&y0);
}
if(nfields == 2)
{ term *varlist = get_varlist();
term x = varlist[1];
term y = varlist[2];
SETVALUE(x,x0);
SETVALUE(y,y0); // so they will deval to the initial values
// now they still have to be entered as parameter values.
int nvariables = get_nvariables();
initialize_parameter(nvariables-2,0,x0);
initialize_parameter(nvariables-1,0, y0);
pDocData->graphs[0]->initial_values[0] = &(pDocData->DocVarData.varinfo[nvariables-2].realpart);
pDocData->graphs[0]->initial_values[1] = &(pDocData->DocVarData.varinfo[nvariables-1].realpart);
*(pDocData->graphs[0]->initial_values[0]) = x0;
*(pDocData->graphs[0]->initial_values[1]) = y0;
}
else
{ // in case one of them contains something like sqrt(pi), parse and evaluate with deval
term conditions;
char *rest;
int err = bparse(get_parser_flags(),&conditions, initialConditions,&rest);
if(!err && FUNCTOR(conditions) == AND && ARITY(conditions)== 2)
{
deval(ARG(0,conditions),&x0);
deval(ARG(1,conditions),&x0);
if(y0 != BADVAL && x0 != BADVAL)
{ term *varlist = get_varlist();
term x = varlist[1];
term y = varlist[2];
SETVALUE(x,x0);
SETVALUE(y,y0); // this is where we keep the values of the parameters
*(pDocData->graphs[0]->initial_values[1]) = x0;
*(pDocData->graphs[0]->initial_values[2]) = y0;
}
else
err = 1;
}
if(err)
{ char *errmsg = "initial conditions must evaluate to a pair of numbers.";
rect r2;
r2.left = 0;
r2.top = 0;
r2.right = graphwidth;
r2.bottom = windowheight;
set_svgDevice(outbuffer, outbuffersize, &r2);
strcpy(outbuffer,errmsg);
return 0; // this message is successfully processed, despite the parse error
}
}
/* Determine whether direction fields make sense or
not; if not change mainchoice to ODESYSTEM */
unsigned f = FUNCTOR( pDocData->graphs[0]->independent_variable);
if (f == 0 || f == ILLEGAL)
{ if(! use_dfield(t))
pDocData->mainchoice = ODESYSTEM; /* direction fields won't work */
}
else if(contains(ARG(1,ARG(0,t)),f) || contains(ARG(1,ARG(1,t)),f))
pDocData->mainchoice = ODESYSTEM; /* direction fields won't work */
else
pDocData->graphs[0]->dfield = 1; /* turn on direction fields by default. */
}
relinquish_device();
// OK, now the document is good to go. Time to respond to the message.
int m = (int)(2*strlen(param));
char *buffer = malloc(m);
if(!buffer)
assert(0);
if(graphtype == RIEMANNSUMS || graphtype == TRAPEZOIDRULE || graphtype == SIMPSONSRULE)
{ int eigenvariable = pDocData->DocVarData.eigenvariable;
term x = pDocData->DocVarData.varlist[eigenvariable];
t = definite_integral(ARG(0,t),x,ARG(1,t),ARG(2,t));
// dropping the number of intervals and style arguments
}
pstring(t,buffer,2048);
err = process_parseAndDisplay(pDocData, buffer , outbuffer, outbuffersize);
free(buffer);
return err;
}
/*____________________________________________________________________________*/
static int default_topic(int problemtype)
/* return the default topic for the given problemtype (an integer from probtype.h
that is one of those referenced in EnterSymbolProblem.php).
*/
{
switch(problemtype)
{ // all the problemtypes in EnterProblem.php must be covered here
case SIMPLIFY:
return _simplify;
break;
/*
#define _simplify 72
#define _simple_commondenom 73
#define _advanced_commondenom 74
#define _compound_fractions 75
#define _simplify_polynomials 76
#define _simple_exponents 77
#define _simplify_rational_functions 78
#define _negative_exponents 79
#define _eliminate_negative_exponents 80
#define _radicals 81
#define _absolute_value 82
#define _fractional_exponents 83
#define _eliminate_fractional_exponents 84
*/
case LINEAR_EQUATIONS:
return _eqns_by_adding_eqns;
break;
/*
#define _eqns_by_substitution 66
#define _eqns_by_adding_eqns 67
#define _eqns_in_matrix_form 68
#define _gauss_jordan 69
#define _eqns_by_matrix_inverse 70
#define _cramers_rule 71
*/
case SOLVE_EQUATION:
case INEQUALITIES:
case DIFFERENTIATE:
case INTEGRATION:
case TRIG_IDENTITY:
case MINMAX:
case IMPLICIT_DIFF:
case RELATED_RATES:
default:
assert(0); // all the problemtypes in EnterProblem.php must be covered here
}
}
/*_______________________________________________________________________*/
static int process_setupAndCheckSymbol(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
/* parameter has the form "problemtext"+problemtype+language+width+height+topic + toolbarWidth.
problemtype is a number, one of the values in probtype.h (but not all values
there are supported).
language is given by language number (e.g. ENGLISH or FRENCH) or by name.
width and height are width and height of rectangle R which is where the
graphs will be written, i.e. browser window excluding the Graph Toolbar.
The Engine knows nothing about the Graph Toolbar and doesn't need to know.
'topic' is a number as in tdefn.h. If it is 0, that means "no information",
and this function must determine an appropriate topic. If nonzero, user has
chosen it from a select element on the Enter page, so use that topic.
Fill those data items into pDocData and try to initialize that document.
In particular:
1. Parse the problemtext.
2. Pick the topic if not passed.
3. set up the varlist
4. pick the independent and dependent variable(s),
5. initialize the parameters
return 0 for success, nonzero for error
Respond to the message with
-- if it parses, The <svg> element containing the (typeset) problem.
-- if it doesn't parse, the parser error message.
Notes: -- We do not ask about dependent and independent variables, and
whether some variables depend on the independent variable or are
parameters. We just make reasonable choices.
*/
{ char languagename[33];
char problemtext[MAXPROBLEMTEXT];
term t; // for the parsed problem
char *rest; // pointer to the unparsed part of problemtext
int width, windowheight,problemtype,languagenumber,topic,toolbarWidth;
if(pDocData->heap) // then we're reusing a document
{ destroy_document_data(pDocData);
}
char *format_string = "\"%[^\"]\"+%d+%d+%d+%d+%d+%d";
// verify already checked it is not too long, so the format string is simpler here.
int nfields = sscanf(param, format_string,
problemtext,
&problemtype,
&languagenumber,
&width,
&windowheight,
&topic,
&toolbarWidth
);
if(nfields < 7)
{
format_string = "\"%[^\"]\"+%d+%32[^+]+%d+%d+%d+%d";
nfields = sscanf(param, format_string,
problemtext,
&problemtype,
languagename,
&width,
&windowheight,
&topic,
&toolbarWidth
);
if(nfields < 7)
{ printf("Incorrect parameter to setupAndCheckGraph\n");
printf("Need \"problemtext\"+graphtype+language+windowwidth+windowheight+topic+toolbarWidth");
return 1;
}
languagenumber = get_language_number(languagename);
}
pDocData->language = languagenumber;
pDocData->problemtype = problemtype;
term tt; // to stash the parameter interval
SETFUNCTOR(tt,0,0); // until something is put there
if(topic == 0) // user did not select a topic
// but verify will reject the message without a topic! so we won't get here.
topic = default_topic(problemtype);
pDocData->currenttopic = topic;
pDocData->problemsource = SOURCE_USER;
int err = init_doc_data(SYMBOLDOC,pDocData);
assert(!err);
// the only way it can fail is if we're out of memory for allocate_doc_data,
// and there's nothing we can do about that.
pDocData->language = languagenumber;
set_language(languagenumber); // also needed! to get tooltip language right
pDocData->problemnumbers[SOURCE_USER] = 1;
pDocData->problemsource = SOURCE_USER;
// WebMathXpert does not keep track of previously entered problems;
// the user interface can offer them as suggestions in an edit box.
// problemtext starts with an escaped quote, so skip that.
err = bparse(get_parser_flags(),&t,problemtext, &rest);
if(err)
{ // handle parser errors by sending the error to the user
rect r2;
r2.left = 0;
r2.top = 0;
r2.right = width;
r2.bottom = windowheight;
set_svgDevice(outbuffer, outbuffersize, &r2);
printf("parse failed\n");
char *message = (char *) pem(err); // parser error message in the current language
char id[24];
svgSymbolTextElement(message, outbuffer, id, "parserError", outbuffersize, RGB(255,0,0), 0, 0);
int byteswritten = (int) strlen(outbuffer);
outbuffer += byteswritten;
outbuffersize -= byteswritten;
return 0; // this message is successfully processed, despite the parse error
}
pDocData->function = t; // for example x^3-ax
pDocData->problemtype = problemtype;
err = setup_and_check(pDocData); // sets up varlist etc., calls input_check but not check_problem
if(err)
{ // input not accepted. There should be an error message.
char *errmsg = (char *) input_error_message(err);
errbuf(0,errmsg);
sendErrors(pDocData,outbuffer,outbuffersize,0);
return 0; // the message was successfully processed, even though the input wasn't usable.
}
t = pDocData->function; // for example y = x^3-ax, after enhance_problem
err = final_adjustments(pDocData->problemtype, t); // calls set_history and checkproblem
if(err)
{ // input not accepted. There should be an error message.
char *errmsg = (char *) input_error_message(err);
errbuf(0,errmsg);
sendErrors(pDocData,outbuffer,outbuffersize,0);
return 0; // the message was successfully processed, even though the input wasn't usable.
}
const char *reason = english(1188); /* the problem */
store_reason(pDocData,0,reason);
activate(pDocData);
// setupdata requires an svgDevice, so we have to set one
rect r;
r.left = 0;
r.right = width;
r.top = 0;
r.bottom = windowheight;
set_svgDevice(outbuffer, outbuffersize, &r);
int papyrus_height = 1L << 20; // some very large number; height is irrelevant
int reasonstart = 0.7 * width;
init_papyrus(pDocData, width, papyrus_height, reasonstart);
set_control_flags(problemtype,topic); // must come after activate
increment_currentline(); // to zero; must come after activate
// and must come after set_control_flags since that sets currentline to -1.
if(FUNCTOR(tt) != 0)
{ // then tt is the parameter interval, a <= t <= b.
term left = ARG(0,tt);
if(FUNCTOR(left) == 0)
assert(0);
//term right = ARG(1,tt); // unused, but I wanted to inspect it while debugging
term indvar = ARG(1,left);
// The independent variable must come first in varlist
// this has to happen before setupdata, so we don't have the graph structures yet.
// Leave the other variables in their original order.
term *varlist = pDocData->DocVarData.varlist;
int nvars= pDocData->DocVarData.nvariables;
int k = 0;
for(k = 0; k<nvars; k++)
{ if(equals(varlist[k],indvar) && k != 0)
{
term temp = varlist[k];
for(int i = k-1;i>=0;i--)
varlist[i+1] = varlist[i];
varlist[0] = temp;
break;
}
}
if( k == nvars)
{ // indvar did not occur, as in an ODE2 graph where the independent variable isn't explicit
vaux(indvar); // adds indvar to the varlist and points its args into varinfo[nvars]
swapvars(0,nvars); // stick it in at the beginning
}
}
if(needsGraphButton(pDocData)==0)
pDocData->showGraphButton = 0;
else
pDocData->showGraphButton = 1; // it's already 1, set by init_doc_data,
// so this line is only for clarity
// OK, now the document is good to go. Time to respond to the message.
int m = (int)(2*strlen(param));
char *buffer = malloc(m);
if(!buffer)
assert(0);
if(pDocData->problemtype == MINMAX)
{ t = and(t,pDocData->DocControlData.minmax_interval);
}
pstring(t,buffer,2048);
deactivate(pDocData);
// int save_anddisplay = get_anddisplay();
// set_anddisplay(1); // no brackets, typeset as matrix with no bars
err = process_parseAndDisplay(pDocData, buffer , outbuffer, outbuffersize);
// set_anddisplay(save_anddisplay);
if(err)
{ free(buffer);
return err;
}
if(pDocData->problemtype == SOLVE_EQUATION)
{ int byteswritten = (int) strlen(outbuffer);
outbuffersize -= byteswritten;
outbuffer += byteswritten;
term x = pDocData->DocVarData.varlist[0];
strcpy(buffer, english(1453)); // "solve for "
buffer[0] = toupper(buffer[0]); // "Solve for "
strcat(buffer, atom_string(x));
svgSymbolTextElement(buffer, outbuffer, "SolveButtonText", "buttontext", outbuffersize, RGB(255,255,255), 16, 8);
}
free(buffer);
return err;
}
/*____________________________________________________________________________*/
static int process_drawAll(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
/* param is width+height (of the browser window)
Just call sendGraphDocument, since the document has been prepared already by
process_setupAndCheckGraph. And, we do not need to recompute singularities,
since we're just drawing a different portion of the graph.
*/
{ int graphwidth, height;
sscanf(param, "%d+%d", &graphwidth, &height);
// verify has already checked this conversion works
rect r;
r.left = 0;
r.top = 0;
r.right = graphwidth;
r.bottom = height;
set_svgDevice(outbuffer, outbuffersize, &r);
return sendGraphDocument(pDocData);
}
/*____________________________________________________________________________*/
static int process_startSolving(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
/* param is width+height (of the browser window)
or param is width+height+topic.
If topic is sent, overwrite the current value of PDocData->currenttopic. Then
Just call sendSymbolDocument, since the document has been prepared already by
process_setupAndCheckSymbol.
*/
{ int width, height,topic;
int nfields = sscanf(param, "%d+%d+%d", &width, &height,&topic);
if(nfields == 3)
{ pDocData->currenttopic = topic;
pDocData->DocControlData.currenttopic = topic;
// there is a duplication in the data structures!
activate(pDocData); // copy document variables to local variables
init_model(pDocData->currenttopic,pDocData->DocControlData.model);
init_polyvalflags(&pDocData->DocPolyData);
initialize_complex(topic,&pDocData->function);
// that also sets parser_flags->complex
if(get_complex())
{ set_complex_types(&pDocData->function);
set_complex_types(&pDocData->DocProverData.history[0]);
}
else
{ set_real_types(&pDocData->function);
set_real_types(&pDocData->DocProverData.history[0]);
}
deactivate(pDocData); // local variables (e.g. "complex") to document variables
}
else
sscanf(param, "%d+%d", &width, &height);
// verify has already checked one of these conversions works
rect r;
r.left = 0;
r.top = 0;
r.right = width;
r.bottom = height;
set_svgDevice(outbuffer, outbuffersize, &r);
return sendSymbolDocument(pDocData,outbuffer,outbuffersize);
}
/*___________________________________________________________________________*/
static int process_zoomAtPoint(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
/* The parameter has the form
[scroll,x,y, graphwidth,windowheight].
Zoom all the graphs that contain (x,y).
Here x and are pixel viewport coordinates of the desired zoom center.
(The viewport is the rectangle containing the graphs but not the Toolbar.)
We have to change the xmin, xmax, ymin, ymax entries in pDocData->graphs[k]
but NOT the dxmin, dxmax,...or pxmin,pymin entries as the location of the graph has not changed.
then call sendGraphDocument.
*/
{
int k;
double scroll,x,y;
int graphwidth, windowheight;
graph *g;
sscanf(param,"[%lf,%lf,%lf,%d,%d]",&scroll,&x,&y,&graphwidth,&windowheight);
// verify already checked that this works.
for(k=0; k < pDocData->ngraphs && k < MAXGRAPHS; k++)
{ g = pDocData->graphs[k];
g->adjustToPaper = 0; // disable AdjustToGraphPaper
/* zoom graph g by the factor scroll about pixel (x,y) */
double pixelwidth = g->pxmax-g->pxmin;
double pixelheight = g->pymax-g->pymin;
double worldwidth = g->xmax-g->xmin;
double worldheight = g->ymax-g->ymin;
// check if (x,y) is in this graph
if(x < g->pxmin || x > g->pxmax)
continue;
if(y < g->pymin || y > g->pymax)
continue;
// OK, (x,y) is in g, so proceed with the zoom calculations
// The graph has not changed location on the screen, so we do NOT
// change g->pxmin or g->dxmin etc. We will only change g->xmin etc.
// For that we need to convert x to world coordinates
double world_x = g->xmin + (x - g->pxmin) * (worldwidth/pixelwidth);
double world_y = g->ymin + (g->pymax-y) * (worldheight/pixelheight);
// Now the changes
g->xmax += (scroll-1)*(g->xmax - world_x);
g->xmin -= (scroll-1)*(world_x - g->xmin);
g->ymax += (scroll-1)*(g->ymax - world_y);
g->ymin -= (scroll-1)*(world_y - g->ymin);
// That's it!
}
rect r2;
r2.left = 0;
r2.top = 0;
r2.right = graphwidth;
r2.bottom = windowheight;
set_svgDevice(outbuffer, outbuffersize, &r2);
int rval = sendGraphDocument(pDocData);
for(k=0; k < pDocData->ngraphs && k < MAXGRAPHS; k++)
{ g = pDocData->graphs[k];
g->adjustToPaper = 1; // restore AdjustToGraphPaper
}
return rval;
}
/*___________________________________________________________________________*/
static int process_graphMoved(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
/* This message is sent on mouseup after scrolling a graph. The parameter has the form
[k, deltax, deltay, graphwidth, windowheight]
where k is the index of the scrolled graph. (In case there are more graphs
on the same axes, they also have scrolled.) Here deltax and deltay are in pixel coordinates.
We have to change the xmin, xmax, ymin, ymax entries in pDocData->graphs[k]
but NOT the dxmin, dxmax,...or pxmin,pymin entries as the location of the graph has not changed.
then call sendGraphDocument.
*/
{
int k, k0;
double deltax, deltay;
int graphwidth, windowheight;
graph *g;
sscanf(param,"[%d,%lf,%lf,%d,%d]",&k0,&deltax,&deltay,&graphwidth,&windowheight);
// verify already checked that this works.
for(k=k0; k < pDocData->ngraphs && (k==k0 || pDocData->graphs[k]->newaxes==0) && k < MAXGRAPHS; k++)
{ g = pDocData->graphs[k];
g->adjustToPaper = 0; // disable AdjustToGraphPaper
double pixelwidth = g->pxmax-g->pxmin;
double pixelheight = g->pymax-g->pymin;
double worldwidth = g->xmax-g->xmin;
double worldheight = g->ymax-g->ymin;
double world_deltax = deltax * (worldwidth/pixelwidth);
double world_deltay = -deltay * (worldheight/pixelheight);
g->xmin -= world_deltax;
g->xmax -= world_deltax;
g->ymin -= world_deltay;
g->ymax -= world_deltay;
if(g->ymin == g->ymax)
assert(0);
}
rect r2;
r2.left = 0;
r2.top = 0;
r2.right = graphwidth;
r2.bottom = windowheight;
set_svgDevice(outbuffer, outbuffersize, &r2);
return sendGraphDocument(pDocData);
}
/*____________________________________________________________________________*/
static int process_graphButton(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
/* pDocData is an existing symbolic document. message is "graphButton", param is "width+height",
giving the window width and height in CSS pixels.
Modify pDocData so that pDocData is a valid graph document (but leave its kind SYMBOLICDOC),
without overwriting any fields that would invalidate it as a symbolic document.
*/
{
assert(pDocData -> heap); // it already must have a heap
// do not call init_doc_data, as it was already done
int windowwidth,windowheight;
int nfields = sscanf(param,"%u+%u",&windowwidth,&windowheight);
if(nfields != 2)
{ printf("Wrong parameter in graphButton message\n");
return 1;
}
rect r;
int err;
r.left = 0;
r.top = 0;
r.right = windowwidth; // in CSS pixels
r.bottom = windowheight;
set_svgDevice(outbuffer, outbuffersize,&r);
err = graph_button(pDocData);
if(err)
{ return err; // error message will be sent by process_message.
}
activate(pDocData);
// OK, now the document is good to go. Time to respond to the message.
// The response is SVG graphics to draw the graph.
return sendGraphDocument(pDocData);
}
/*__________________________________________________________________________*/
static int process_autoStep(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize)
{
activate(storedDoc);
int rval = autostep(storedDoc);
if(rval == 1 || rval == 2)
{ const char *maybe = english(1219);
// "MathXpert has no suggestion. Maybe you're finished.";
commentbuf(0,maybe);
}
return sendSymbolDocument(storedDoc, outbuffer, outbuffersize);
}
/*__________________________________________________________________________*/
static int process_showStep(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize)
{
activate(storedDoc);
char *menuitems[64];
int indexToHighlight = -1; // so it will be negative unless set by ShowStep
char finalremark[MAXFINALREMARK]; // some of the final remarks can be long!
finalremark[0] = '\0';
int byteswritten;
int nitems;
int rval = showstep(storedDoc, menuitems, &nitems , &indexToHighlight, finalremark,MAXFINALREMARK);
if(rval != 0)
{ // automode can't find something to do, so finalmessage and finalremark have
// been filled in.
strncpy(storedDoc->papyrus->finalremark,finalremark,MAXFINALREMARK);
}
// Now the menu commands and texts are in menuitems as invisible <svg> elements
for(int k = 0;k< nitems;k++)
{ strcpy(outbuffer, menuitems[k]);
byteswritten = (int) strlen(outbuffer);
outbuffer += byteswritten;
outbuffersize -= byteswritten;
free(menuitems[k]); // calloc'd by MakeSelectorMenu
}
sprintf(outbuffer, "<script> highlightIndex = %d; </script>", indexToHighlight);
byteswritten = (int) strlen(outbuffer);
outbuffer += byteswritten;
outbuffersize -= byteswritten;
return sendSymbolDocument(storedDoc, outbuffer, outbuffersize);
}
/*__________________________________________________________________________*/
static int process_autoFinish(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize)
{
activate(storedDoc);
autofinish(storedDoc);
int err = sendSymbolDocument(storedDoc, outbuffer, outbuffersize);
return err;
}
/*__________________________________________________________________________*/
static int process_initAndAutoFinish(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize)
{ int err = prepareDocument(storedDoc,param);
if(err)
{ // incorrect parameter, for example
return err;
}
activate(storedDoc); // should already be done in prepareDocument
int problemnumber =storedDoc->problemnumbers[SOURCE_MATHPERT];
int topic =storedDoc->currenttopic;
if(problemnumber == 1)
printf("Starting topic %d\n",topic);
int rval = autofinish(storedDoc);
err = sendSymbolDocument(storedDoc, outbuffer, outbuffersize);
if(topic == 129 && problemnumber == 37)
{ // alternating harmonic sum, not intended to be "solved"
return 0;
}
if(rval != 0)
{ printf("topic %d problem %d was not solved.\n",topic, problemnumber);
printf("Topic %s\n", topicstr(topic));
assert(0); // die when autotest first fails
}
return err;
}
/*__________________________________________________________________________*/
static int process_undo(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize)
{
// param is not used
activate(storedDoc);
undo(storedDoc);
return sendSymbolDocument(storedDoc, outbuffer, outbuffersize);
}
/*___________________________________________________________________________*/
static int process_finished(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize)
{
// param is not used
activate(storedDoc);
int percent;
finished(storedDoc, storedDoc->papyrus->finalremark, &percent);
int saveflag = storedDoc->DocControlData.finishedflag;
storedDoc->DocControlData.finishedflag = 1;
// this gets the final remark sent visibly instead of invisibly
int err = sendSymbolDocument(storedDoc, outbuffer, outbuffersize);
storedDoc->DocControlData.finishedflag = saveflag;
return err;
}
/*___________________________________________________________________________*/
static int process_hint(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize)
{
// param is not used
activate(storedDoc);
get_hint(storedDoc,storedDoc->papyrus->hint, MAXHINTS);
int err = sendSymbolDocument(storedDoc, outbuffer, outbuffersize);
storedDoc->papyrus->hint[0] = '\0'; // effectively deleting the record of this hint
return err;
}
/*___________________________________________________________________________*/
static int process_setLanguage(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize)
{
// param is "english", "french", etc.
// Or it can also be the number of the language, such as 5 for Dutch (since DUTCH is defined as 5), etc.
// document does not need to have been init-ed
int language_number;
int nfields = sscanf(param,"%d", &language_number);
printf("In process_setLanguage, param is %s\n",param);
if(nfields == 0)
{ language_number = get_language_number(param);
// returns ENGLISH if param is not the name of a supported language
}
printf("In process_setLanguage, language number is %d\n",language_number);
storedDoc->language = language_number;
set_language(language_number);
int err = sendSymbolDocument(storedDoc, outbuffer, outbuffersize);
storedDoc->papyrus->hint[0] = '\0'; // effectively deleting the record of this hint
return err;
}
/*__________________________________________________________________________*/
static int process_incrementActiveParameter(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize)
{ int k = storedDoc->active_parameter; // index in parameters array
activate(storedDoc);
parameter p = storedDoc->DocVarData.parameters[k];
int i = p.index;
term alpha = storedDoc->DocVarData.varlist[i]; // the term for this parameter;
double pval = VALUE(alpha);
SETVALUE(alpha, pval + p.increment);
int windowwidth, windowheight;
sscanf(param,"%u+%u",&windowwidth,&windowheight);
rect r;
r.left = 0;
r.top = 0;
r.right = windowwidth;
r.bottom = windowheight;
set_svgDevice(outbuffer, outbuffersize,&r);
// singularities (may) need to be recalculated.
calculate_singularities(storedDoc->ngraphs,storedDoc->mainchoice,
storedDoc->function,storedDoc->graphs);
return sendGraphDocument(storedDoc);
}
/*__________________________________________________________________________*/
static int process_horizontalzoomout(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
{
int windowwidth, windowheight;
sscanf(param,"%u+%u",&windowwidth,&windowheight);
rect r;
r.left = 0;
r.top = 0;
r.right = windowwidth;
r.bottom = windowheight;
set_svgDevice(outbuffer, outbuffersize,&r);
int ngraphs = pDocData->ngraphs;
for(int i=0;i< ngraphs;i++)
{ graph *g = pDocData->graphs[i];
g->circular = 0;
double width = g->xmax-g->xmin;
if(g->xmin <= 0 && g->xmax >= 0)
{ g->xmin *= 2;
g->xmax *= 2;
}
else
{ g->xmin -= width/2;
g->xmax += width/2;
}
}
int err = sendGraphDocument(pDocData);
return err;
}
/*__________________________________________________________________________*/
static int process_horizontalzoomin(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
{
int windowwidth, windowheight;
sscanf(param,"%u+%u",&windowwidth,&windowheight);
rect r;
r.left = 0;
r.top = 0;
r.right = windowwidth;
r.bottom = windowheight;
set_svgDevice(outbuffer, outbuffersize,&r);
int ngraphs = pDocData->ngraphs;
for(int i=0;i< ngraphs;i++)
{ graph *g = pDocData->graphs[i];
g->circular = 0;
double width = g->xmax-g->xmin;
if(g->xmin <= 0 && g->xmax >= 0)
{ g->xmin *= 0.5;
g->xmax *= 0.5;
}
else
{ g->xmin += width/4;
g->xmax -= width/4;
}
}
int err = sendGraphDocument(pDocData);
return err;
}
/*__________________________________________________________________________*/
static int process_verticalzoomout(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
/* If the x-axis is visible, it should be the zoom center;
else the midpoint.
*/
{
int windowwidth, windowheight;
sscanf(param,"%u+%u",&windowwidth,&windowheight);
rect r;
r.left = 0;
r.top = 0;
r.right = windowwidth;
r.bottom = windowheight;
set_svgDevice(outbuffer, outbuffersize,&r);
int ngraphs = pDocData->ngraphs;
for(int i=0;i< ngraphs;i++)
{ graph *g = pDocData->graphs[i];
g->circular = 0;
double height = g->ymax-g->ymin;
if(g->ymin <= 0 && g->ymax >= 0)
{ g->ymin *= 2;
g->ymax *= 2;
}
else
{
g->ymin -= height/2;
g->ymax += height/2;
}
}
int err = sendGraphDocument(pDocData);
return err;
}
/*__________________________________________________________________________*/
static int process_verticalzoomin(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
{
int windowwidth, windowheight;
sscanf(param,"%u+%u",&windowwidth,&windowheight);
rect r;
r.left = 0;
r.top = 0;
r.right = windowwidth;
r.bottom = windowheight;
set_svgDevice(outbuffer, outbuffersize,&r);
int ngraphs = pDocData->ngraphs;
for(int i=0;i< ngraphs;i++)
{ graph *g = pDocData->graphs[i];
g->circular = 0;
double height = g->ymax-g->ymin;
if(g->ymin <= 0 && g->ymax >= 0)
{ g->ymin *= 0.5;
g->ymax *= 0.5;
}
else
{ g->ymin += height/4;
g->ymax -= height/4;
}
}
int err = sendGraphDocument(pDocData);
return err;
}
/*__________________________________________________________________________*/
static int process_doublezoomout(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
{
int windowwidth, windowheight;
sscanf(param,"%u+%u",&windowwidth,&windowheight);
rect r;
r.left = 0;
r.top = 0;
r.right = windowwidth;
r.bottom = windowheight;
set_svgDevice(outbuffer, outbuffersize,&r);
int ngraphs = pDocData->ngraphs;
for(int i=0;i< ngraphs;i++)
{ graph *g = pDocData->graphs[i];
double height = g->ymax-g->ymin;
if(g->ymin <= 0.0 && g->ymax >= 0.0)
{ g->ymin *= 2;
g->ymax *= 2;
}
else
{ g->ymin -= height/2;
g->ymax += height/2;
}
if(g->xmin <= 0 && g->xmax >= 0)
{ g->xmin *= 2;
g->xmax *= 2;
}
else
{ double width = g->xmax-g->xmin;
g->xmin -= width/4;
g->xmax += width/4;
}
}
int err = sendGraphDocument(pDocData);
return err;
}
/*__________________________________________________________________________*/
static int process_doublezoomin(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
{
int windowwidth, windowheight;
sscanf(param,"%u+%u",&windowwidth,&windowheight);
rect r;
r.left = 0;
r.top = 0;
r.right = windowwidth;
r.bottom = windowheight;
set_svgDevice(outbuffer, outbuffersize,&r);
int ngraphs = pDocData->ngraphs;
for(int i=0;i<ngraphs;i++)
{ graph *g = pDocData->graphs[i];
double height = g->ymax-g->ymin;
if(g->ymin <= 0 && g->ymax >= 0)
{ g->ymin *= 0.5;
g->ymax *= 0.5;
}
else
{ g->ymin += height/4;
g->ymax -= height/4;
}
double width = g->xmax-g->xmin;
if (g->xmin <= 0 && g->xmax >= 0)
{ g->xmin *= 0.5;
g->xmax *= 0.5;
}
else
{ g->xmin += width/4;
g->xmax -= width/4;
}
}
int err = sendGraphDocument(pDocData);
return err;
}
/*__________________________________________________________________________*/
static int process_toggleDirectionField(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
// change g->dfield;
{ graph *g = pDocData->graphs[0];
g->dfield = g->dfield ? 0 : 1;
int windowwidth, windowheight;
sscanf(param,"%u+%u",&windowwidth,&windowheight);
rect r;
r.left = 0;
r.top = 0;
r.right = windowwidth;
r.bottom = windowheight;
set_svgDevice(outbuffer, outbuffersize,&r);
return sendGraphDocument(pDocData);
}
/*__________________________________________________________________________*/
static int process_toggleErase(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
// change g->erase in all the graphs
{ graph *g;
for(int i = 0;i<pDocData->ngraphs;i++)
{ g = pDocData->graphs[i];
g->erase = g->erase ? 0 : 1;
}
if(pDocData->graphs[0]->erase) // after the change
{ // destroy all the old stored parameter values
clear_parameter_history();
}
int windowwidth, windowheight;
sscanf(param,"%u+%u",&windowwidth,&windowheight);
rect r;
r.left = 0;
r.top = 0;
r.right = windowwidth;
r.bottom = windowheight;
set_svgDevice(outbuffer, outbuffersize,&r);
return sendGraphDocument(pDocData);
}
/*__________________________________________________________________________*/
static int process_circularAspect(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
// ensure that aspect ratio is 1
{ graph *g;
int windowwidth, windowheight;
sscanf(param,"%u+%u",&windowwidth,&windowheight);
rect r;
r.left = 0;
r.top = 0;
r.right = windowwidth;
r.bottom = windowheight;
int k;
for(k=0;k<pDocData->ngraphs;k++)
{ g = pDocData->graphs[k];
g->circular = 1;
AdjustGraphRanges(g, _graph_circle);
}
set_svgDevice(outbuffer, outbuffersize,&r);
return sendGraphDocument(pDocData);
}
/*__________________________________________________________________________*/
static int process_decrementActiveParameter(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize)
{ int k = storedDoc->active_parameter; // index in parameters array
activate(storedDoc);
parameter p = storedDoc->DocVarData.parameters[k];
int i = p.index;
term alpha = storedDoc->DocVarData.varlist[i]; // the term for this parameter;
double pval = VALUE(alpha);
SETVALUE(alpha, pval - p.increment);
int windowwidth, windowheight;
sscanf(param,"%u+%u",&windowwidth,&windowheight);
rect r;
r.left = 0;
r.top = 0;
r.right = windowwidth;
r.bottom = windowheight;
set_svgDevice(outbuffer, outbuffersize,&r);
// singularities (may) need to be recalculated.
calculate_singularities(storedDoc->ngraphs,storedDoc->mainchoice,
storedDoc->function,storedDoc->graphs);
return sendGraphDocument(storedDoc);
}
/*__________________________________________________________________________*/
static int process_decrementParameter(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize)
/* param is width + height + index (index of the parameter) */
{ int k;
int windowwidth, windowheight;
sscanf(param,"%u+%u+%d",&windowwidth,&windowheight,&k);
activate(storedDoc);
storedDoc->active_parameter = k; // now this one will be active
parameter p = storedDoc->DocVarData.parameters[k];
int i = p.index;
term alpha = storedDoc->DocVarData.varlist[i]; // the term for this parameter;
double pval = VALUE(alpha);
SETVALUE(alpha, pval - p.increment);
rect r;
r.left = 0;
r.top = 0;
r.right = windowwidth;
r.bottom = windowheight;
set_svgDevice(outbuffer, outbuffersize,&r);
// singularities (may) need to be recalculated.
calculate_singularities(storedDoc->ngraphs,storedDoc->mainchoice,
storedDoc->function,storedDoc->graphs);
return sendGraphDocument(storedDoc);
}
/*__________________________________________________________________________*/
static int process_incrementParameter(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize)
/* param is width + height + index (index of the parameter in parameters array, not in varlist) */
{ int k;
int windowwidth, windowheight;
sscanf(param,"%u+%u+%d",&windowwidth,&windowheight,&k);
activate(storedDoc);
storedDoc->active_parameter = k; // now this one will be active
parameter p = storedDoc->DocVarData.parameters[k];
int i = p.index;
term alpha = storedDoc->DocVarData.varlist[i]; // the term for this parameter;
double pval = VALUE(alpha);
SETVALUE(alpha, pval + p.increment);
rect r;
r.left = 0;
r.top = 0;
r.right = windowwidth;
r.bottom = windowheight;
set_svgDevice(outbuffer, outbuffersize,&r);
// singularities (may) need to be recalculated.
calculate_singularities(storedDoc->ngraphs,storedDoc->mainchoice,
storedDoc->function,storedDoc->graphs);
return sendGraphDocument(storedDoc);
}
/*__________________________________________________________________________*/
static int process_selectedRectangleSymbol(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
/* User has selected a rectangle in a symbol view of the document, given by left, top, width, height.
The rectangle of the current line is lineleft,linetop,linewidth, lineheight.
The parameter has the form
[left,top,width,height,lineleft,linetop,linewidth,lineheight,windowwidth,windowheight].
These are all CSS pixel coordinates. They must either all be numbers or all have px units.
The selected rectangle should already be in the active line (since the interface
should only allow selections there). [The active line is usually the current line, but
e.g. if the last operation was "evaluate numerically", it might be a preceding line.]
But in case the selected rectangle is not in that line, it is first replaced
by their intersections with the active line's rectangle. If it does not
meet the current line, return 2.
The Engine will determine the selected term (if any)
and fill in storedDoc->papyrus->selectedterm accordingly, and then call
sendSymbolDocument. The COLOR bit of the blocked term that is sent to termtoSVG to
compute the SVG for the line will be correctly set, so the display of the
selected term will be automatic, unless the interface will color the
selected rectangle (after cutting it down to match the block of the selected term).
In addition to the document, the response will also contain a number of <svg>
elements, of class "menuitem", which the interface should assemble into a popup
menu of operations the use can do "to or with" the selected formula.
Return 0 for success, 1 for "no term was in that rectangle".
*/
{ int left,top,width,height,lineleft,linetop,linewidth,lineheight;
int windowwidth, windowheight;
int err,byteswritten;
int nfields = sscanf(param, "[%d,%d,%d,%d,%d,%d,%d,%d,%d,%d]",
&left,&top,&width,&height,&lineleft,&linetop,&linewidth,&lineheight,&windowwidth,&windowheight);
if (nfields != 10)
nfields = sscanf(param, "[%dpx,%dpx,%dpx,%dpx,%dpx,%dpx,%dpx,%dpx,%dpx,%dpx]", &left,&top,&width,&height,&lineleft,&linetop,&linewidth,&lineheight,&windowwidth,&windowheight);
if(nfields != 10)
{
send_error("selectedRectangleSymbol parameter was incorrectly formatted.\n",outbuffer);
return 1;
}
if(width <= 0 || height <= 0)
{ send_error("parameter to selectedRectangleSymbol failed to specify positive width and height.\n",outbuffer);
return 1;
}
activate(pDocData);
// Next remove any existing selected term. This is needed in
// case the user selects a term, then dismisses the Term Selection Menu,
// leaving a selected term, and then selects a new term.
if(pDocData && pDocData->papyrus->selected)
{
destroy_ltermlist(pDocData->papyrus->selected);
pDocData->papyrus->selected = NULL;
}
int activeline = get_activeline(); // pDocData->DocVarData.currentline;
term t = pDocData->DocProverData.history[activeline];
// but, in case there is a "selected equation", only that equation is displayed, so:
if(FUNCTOR(t) == AND || FUNCTOR(t) == OR)
{ // check for a selected arg
for(int k = 0; k< ARITY(t);k++)
{ if (SELECTED(ARG(k,t)))
{ t = ARG(k,t);
break;
}
}
}
term bt;
bblock(t,&bt);
lterm located;
BIGRECT r;
// make r the rectangle of the active line
// the two 8's are the margin within the <svg> element of the line
r.left = lineleft+8;
r.top = linetop + 8;
r.right = r.left + linewidth;
r.bottom = r.top + lineheight;
locate_term(bt,&r,&located);
// Now make r the selected rectangle
r.left = left;
r.top = top;
r.right = r.left + width;
r.bottom = r.top + height;
lterm selected;
err = select_term(&located,&r,NULL, &selected); // NULL says located is not a broken term
if(err)
{ // there was no term in rectangle r
printf("There was no term inside the selected rectangle.\n");
// so the document is unchanged...
sendSymbolDocument(pDocData,outbuffer,outbuffersize);
return 0;
}
// Now selected is the selected subterm.
HIGHLIGHT(selected);
pDocData->papyrus->selectedline = activeline;
pDocData->papyrus->selected = lcons(&selected,(ltermlist *) pDocData->papyrus->selected);
char *menuitems[64];
operation menuops[64];
int menuIDs[64];
// Now create the "select menu" of operations to be done with or to the selected term:
int nitems = MakeSelectorMenu(pDocData,menuitems,menuops);
// Remove any error messages stored while trying operations while making the menu
memset(pDocData->papyrus->error_buffer,0,DIM_ERROR_BUFFER*ERRBUFLENGTH);
memset(pDocData->papyrus->comment_buffer,0,DIM_COMMENT_BUFFER * COMMENTBUFLENGTH);
// Now the menu commands and texts are in menuitems as invisible <svg> elements
for(int k = 0;k<nitems;k++)
{ strcpy(outbuffer, menuitems[k]);
byteswritten = (int) strlen(outbuffer);
outbuffer += byteswritten;
outbuffersize -= byteswritten;
char *marker = strstr(menuitems[k],"id=\"");
sscanf(marker+4,"%d",&menuIDs[k]);
free(menuitems[k]); // calloc'd by MakeSelectorMenu
}
// also send tooltips for the menu items
for(int k=0;k<nitems;k++)
{ int op = menuops[k].choice;
int menu = menuops[k].men;
char *tooltipText = (char *) ophelp(menu)[op];
if(tooltipText == NULL)
{ printf("Oops, no ophelp[%d][%d]",menu,op);
continue;
}
char id[32];
sprintf(id,"tooltip-%d",menuIDs[k]);
svgSymbolTextInvisibleElement(tooltipText, outbuffer,
id, "tooltip",
outbuffersize, RGB(0,0,0), // black text
0, 0); // x and y should be zero, relative to the tooltip container
byteswritten = (int) strlen(outbuffer);
outbuffer += byteswritten;
outbuffersize -= byteswritten;
}
sendSymbolDocument(pDocData,outbuffer,outbuffersize);
return 0;
}
/*__________________________________________________________________________*/
static int process_selectedRectangleGraph(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
/* User has selected a rectangle in a graph view of the document. */
/* param has the form [whichgraph,left,top,width,height,toolbarwidth,windowwidth,windowheight],
where the numbers are integers,
and are pixel coordinates of the selected rectangle in the drawing rectangle
(which excludes the toolbar). The selected rectangle will (must) lie in a
single graph!
It also accepts the coordinates with "px" after the number.
Whichgraph is in [0,5) and tells which graph contains the selected rectangle
*/
{ int whichgraph,left,top,width,height; // of the selected rectangle
int toolbarwidth, windowwidth, graphwidth, windowheight;
int nfields = sscanf(param, "[%d,%d,%d,%d,%d,%d,%d,%d]", &whichgraph,&left,&top,&width,&height,&toolbarwidth,&windowwidth,&windowheight);
if (nfields != 8)
nfields = sscanf(param, "[%d,%dpx,%dpx,%dpx,%dpx,%dpx,%dpx,%dpx]", &whichgraph,&left,&top,&width,&height,&toolbarwidth,&windowwidth,&windowheight);
if(nfields != 8)
{
send_error("selectedRectangleGraph parameter was incorrectly formatted.\n",outbuffer);
return 1;
}
graphwidth = windowwidth - toolbarwidth;
// now never mention windowwidth or toolbarwidth again!
if(width <= 0 || height <= 0)
{ send_error("parameter to selectedRectangleGraph failed to specify positive width and height.\n",outbuffer);
return 1;
}
graph *g = pDocData->graphs[whichgraph];
// g->pxmin etc. are the pixel coordinates of the viewport that contains this graph
rect r;
svgDevice *theDevice = get_device();
r.left = 0;
r.top = 0;
r.right = graphwidth;
r.bottom = windowheight;
set_svgDevice(outbuffer, outbuffersize,&r);
set_viewport(g->dxmin, g->dxmax,g->dymin,g->dymax);
// sets theDevice->pxmin etc.
set_world(g->xmin, g->xmax, g->ymin, g->ymax);
// this sets theDevice->xpixel and ->ypixel
theDevice = get_device();
if(fabs(theDevice->xpixel *(r.right-r.left) - (g->xmax-g->xmin)) > 0.000000001)
assert(0);
// so if we get here, xpixel must be correct!
r.bottom = theDevice->pymax; // pixel coordinates of the viewport
r.top = theDevice->pymin;
if(fabs(theDevice->ypixel * (r.bottom-r.top) -(g->ymax-g->ymin)) > 0.000000001)
{
assert(0);
}
// so if we get here, ypixel must be correct!
double deltax = theDevice->xpixel * (left-r.left);
printf("delta x = %lf\n",deltax);
g->xmin += deltax;
deltax = theDevice->xpixel * (r.right-(left+width));
g->xmax -= deltax;
g->ymax -= theDevice->ypixel * (top-r.top);
g->ymin = g->ymax - theDevice->ypixel * height;
return sendGraphDocument(pDocData);
}
/*__________________________________________________________________________*/
static int process_graphWindowResized(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
/* The browser window has been resized. */
/* param has the form [windowwidth,windowheight]
where the numbers are all integers (not pixels), or are all in pixels.
*/
{ int graphwidth,windowheight;
rect r;
int nfields = sscanf(param, "[%d,%d]", &graphwidth,&windowheight);
if (nfields != 2)
nfields = sscanf(param, "[%dpx,%dpx]", &graphwidth,&windowheight);
if(nfields != 2)
{
send_error("selectedRectangleGraph parameter was incorrectly formatted.\n",outbuffer);
return 1;
}
if(graphwidth <= 0 || windowheight <= 0)
{ send_error("parameter to selectedRectangleGraph failed to specify positive width and height.\n",outbuffer);
return 1;
}
for(int k = 0; k<pDocData->ngraphs;k++)
{
graph *g = pDocData->graphs[k];
svgDevice *theDevice = get_device();
r.left = 0;
r.top = 0;
r.right = graphwidth;
r.bottom = windowheight;
set_svgDevice(outbuffer, outbuffersize,&r);
double devicewidth = theDevice->dxmax-theDevice->dxmin;
double deviceheight = theDevice->dymax-theDevice->dymin;
double pixelwidth = graphwidth;
double pixelheight = windowheight;
// g->xmin etc. do not change, but we need
// to set g->pxmin and g->pxmax
// You can't use ndcToPixel to do this, as it assumes g->pxmin
// etc. are already set correctly.
g->pxmin = round(g->dxmin * pixelwidth/devicewidth);
g->pxmax = round(g->dxmax * pixelwidth/devicewidth);
// the minimum device coordinate is the max pixel coordinate
// because pixels increase downwards while ndc's increase upwards.
// ndc = normalized device coordinates.
g->pymin = round((1-g->dymax) * pixelheight/deviceheight);
g->pymax = round((1-g->dymin) * pixelheight/deviceheight);
set_viewport(g->dxmin, g->dxmax,g->dymin,g->dymax);
set_world(g->xmin, g->xmax, g->ymin, g->ymax);
// this sets theDevice->xpixel and ->ypixel
theDevice = get_device();
if(fabs(theDevice->xpixel *(r.right-r.left) - (g->xmax-g->xmin)) > 0.000000001)
{ double err = fabs(theDevice->xpixel *(r.right-r.left) - (g->xmax-g->xmin));
printf("%lf",err);
assert(0);
}
// so if we get here, xpixel must be correct!
r.bottom = theDevice->pymax; // pixel coordinates of the viewport
r.top = theDevice->pymin;
if(fabs(theDevice->ypixel * (r.bottom-r.top) -(g->ymax-g->ymin)) > 0.000000001)
{
assert(0);
}
// so if we get here, ypixel must be correct!
}
return sendGraphDocument(pDocData);
}
/*__________________________________________________________________________*/
void send_error(char *errmsg, char *outbuffer)
// send "DEVELOPER ERROR:" + errmsg
// and print it to the console.
// This is called just before sending the message response, so
// it doesn't attempt to check if there's space or to update the
// pointer to outbuffer, which here is only local.
{
assert(*outbuffer == '\0');
strcpy(outbuffer, "DEVELOPER ERROR: ");
strcat(outbuffer, errmsg);
printf("\nDEVELOPER ERROR: %s\n",errmsg); // print it on the console
}
/*________________________________________________________________________*/
static int execOnSelectedTerm(PDOCDATA pDocData, operation op, term arg, term *next, char *reason)
/* execute the operation op on the selected term, replacing the selected term in
history[activeline] by the result [except as noted in this comment below] and return
that in *next, with the justification in *reason.
Only works on the first selected term if more than one is selected, and cannot deal
with selected subranges.
Presumes 0 < op.men <= 16, i.e. op.men is 1-based, not 0-based.
Pushpending and poppending change the current line to something that isn't equivalent,
e.g. when doing integration by substitution. So there is special code for that
in this function.
*/
{
term result;
int activeline = get_activeline();
copy(pDocData->DocProverData.history[activeline],next);
// find the located term as a subterm of next
term *marker2 = next;
ltermlist *q = pDocData->papyrus->selected;
if(q == NULL)
return 1; // there has to be a selected term to execute an operator on it.
if(q->data.arity < 0)
return 1; // negative arity indicates a selected subrange;
// You can't do something TO a selected subrange; you do something WITH it.
for(pathlist *z =q->data.path; z; z=z->next)
{
marker2 = ARGPTR(*marker2)+(z->data);
}
actualop code = access_optable(op.men)[op.choice-1];
lterm test = ((ltermlist*)(pDocData->papyrus->selected))->data;
if(!equals(abstract(test),*marker2))
assert(0);
assumption *pending1 = get_pending();
int err = (*code)(*marker2,arg,&result,reason);
if(err)
return err;
assumption *pending2 = get_pending();
if(pending2 && pending2->line == get_activeline())
{ // then 'code' has called pushpending, and thus pushed the current line
// onto the pending stack, so result should become the next current line
*next = result;
}
else if(pending1 != pending2 && pending1!= NULL )
{ // then 'code' has called poppending, so again result should be the next line.
*next = result;
}
else // replace the focus by the result
*marker2 = result;
++pDocData->DocControlData.totalsteps;
pDocData->papyrus->finalremark[0] = '\0'; // don't repeat a previous 'finished'
return 0;
}
/*__________________________________________________________________________*/
static int index_of_selected_equation(void)
/* Return the 1-based index of the selected equation or inequality, or -1
if there is no such selected equation.
*/
{ PDOCDATA pDocData = GetActiveDoc();
ltermlist *q = pDocData->papyrus->selected;
if(q == NULL || q->data.path == NULL)
return -1;
term eq = abstract(q->data);
if(! INEQUALITY(FUNCTOR(eq))) // includes '='
return -1;
return 1+ q->data.path->data; // make it 1-based as specified
}
/*__________________________________________________________________________*/
static int process_execOpWithArg(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
/* param has the form commandID+inputArgText.
check_arg has already verified that inputArgText parses and is correct for the operation
specified by commandID. So parse inputArgText and call exec. But be careful about
the term to apply the op to, and the arg!
Example, op is add_to_selected_equation, inputArgText is 1, we want to add
the selected equation to equation 1. The op add_to_selected_equation has to be
applied to history[activeline], not to the selected equation, and the arg has to
be passed as and(make_int(index_of_selected_term(),arg).
*/
{
term arg;
int commandID;
operation op;
char reason[512];
char inputArgText[512];
char *marker;
term next;
int nfields = sscanf(param,"%d",&commandID);
int activeline = get_activeline();
assert(nfields == 1);
char *rest = strchr(param, '+');
assert(rest != NULL && rest[1] != '\0');
strncpy(inputArgText,rest+1,512);
op.choice = commandID & 15; // lowest 4 bits
op.choice += 1; // so op.choice is between 1 and 16 now
// set bits 13,14,15 of commandID to zero as well as all bits to the left of that
// there will be more bits to the left as ints are larger than 16 bits
commandID = commandID >> 5; //commandID | 0x1fff;
op.men = (commandID & 0xff) -1; // see opcommand, which this inverts
int err = bparse(get_parser_flags(), &arg, inputArgText, &marker);
assert(!err); // because it was already verified before this message was sent
int promptnumber;
condition c = needs_two_args(op,&promptnumber);
ltermlist *q = pDocData->papyrus->selected;
pathlist *p = q->data.path;
int index = index_of_selected_equation(); // positive if there's a selected equation
// but c is also positive on matrix operations, when you select a row of a matrix for example
if(index <= 0 && p) // there is a selected term, but not a toplevel equation.
{ // then the toplevel term must be a matrix equation, and the selected term
// is a row of the matrix. Which row?
while(p->next)
p = p->next;
// now p points to the last element in the path
index = p->data +1; // index is 1-based
}
if((int) c != 0) // 0 as a condition is called dummy, but if(c) passes dummy through.
{ // then we'll apply op to the toplevel term
actualop code = access_optable(op.men)[op.choice-1];
if(index <= 0)
assert(0);
arg = topflatten(and(make_int(index),arg));
term top = history(activeline);
err = (*code)(top,arg,&next, reason);
}
else
{ /* Now it wasn't an op that needs two args, so apply it to the selected term. */
err = execOnSelectedTerm(pDocData,op,arg,&next,reason);
}
finish_exec(pDocData,err, op,next,reason);
destroy_ltermlist((ltermlist*)pDocData->papyrus->selected);
pDocData->papyrus->selected = NULL; // remove the selected rectangle
if(err == 0) // op was successful
{
sendSymbolDocument(pDocData,outbuffer,outbuffersize);
return 0;
}
// now err was not zero, so fetch the error message and send it as svg
char *errmsg = operator_errmsg(op);
strncpy(pDocData->papyrus->error_buffer[0],errmsg,DIM_ERROR_BUFFER);
if(strlen(errmsg) > DIM_ERROR_BUFFER)
printf("Hey! error message too long, ProcessMessage.c\n");
// then sendSymbolDocument will call sendErrors to get this message to the browser.
sendSymbolDocument(pDocData,outbuffer,outbuffersize);
return 0;
}
/*__________________________________________________________________________*/
static int process_selectMenuChoice(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
/* param is the commandID of an item on the selection menu. There must be a selected term
in order to send this message, so if not, send an ERROR to the developer. Find the operation
corresponding to this commanID. (There must be one.) See it this op needs_two_args or
needs_arg.
--if neither, then attempt to execute that operation
either on the selected term, or using the selected term as argument. If that works, update
the document and call sendSymbolDocument(). If it fails outright, send an invisible <svg> element of
class "mathError" with the reason why it failed and then call sendSymbolDocument.
-- If the operation needs two arguments, then the first one will be the selected term,
and the second will be supplied by the user, and they will be combined with AND.
-- if the operation needs one arg, and the selected term is the whole active line or
a top-level equation or inequality, ask the user to supply the arg.
-- otherwise, try to use the selected term for the arg.
To ask the user for an arg, we send an invisible <svg> of class "needsArg" with the prompt for
the needs_arg dialog, and an id that encodes the condition c that the arg should satisfy.
The client must present a dialog accordingly, e.g. to get a variable, etc.
We also call sendSymbolDocument, so the calculation appears, with the dialog to collect the argument.
(The dialog will then send an Ajax message "checkArg", and if it checks, will send an
execOpWithArg message.)
Return 0, indicating success in processing the message, whether or
not the operation succeeded.
*/
{
ltermlist *selected = (ltermlist *) (pDocData->papyrus->selected);
if (! selected)
{ // ignore the error. This happens e.g. if the user hits the
// browser's back button after execOpOnSelectedTerm.
sendSymbolDocument(pDocData,outbuffer,outbuffersize);
return 0; // success in processing the message, even though nothing is done.
// So we don't do this:
// send_error("selectMenuChoice message should be sent only when there is a selected term",outbuffer);
// return 1;
}
term arg = abstract(selected->data);
operation op;
int commandID;
assert(strlen(param)< 10);
sscanf(param,"%d",&commandID);
op.choice = commandID & 15; // lowest 4 bits
op.choice += 1; // so op.choice is between 1 and 16 now
// set bits 13,14,15 of commandID to zero as well as all bits to the left of that
// there will be more bits to the left as ints are larger than 16 bits
commandID = commandID >> 5; //commandID | 0x1fff;
op.men = (commandID & 0xff) -1; // see opcommand, which this inverts
// verify has already checked that it is a legal command.
activate(pDocData);
char needsArgPrompt[256];
int c=0;
int c2 = 0;
char reason[256];
if(selected->next == NULL && // only one selected term
op.men == linear_equations_by_selection
)
{ int promptnumber;
c = needs_two_args(op,&promptnumber);
/* Some operators, notably those on the linear_equations_by_selection
menu, require a selected-term argument AND an argument entered by
the user, e.g. "add the selected equation to equation ?".
The selected term must be an inequality or
equation and must be at toplevel or one member of the toplevel AND
(but I think that's the only place an inequality or equation can occur).
*/
if(c != 0)
strcpy(needsArgPrompt, english(promptnumber));
}
if(!c) // it's not an operator that needs two args
{ if(op.men == 31 && op.choice == 7)
{ // swap_equations, with only 2 equations, doesn't need a user-supplied
// argument, but it does need an argument, which we supply here.
int index = index_of_selected_equation();
if (index <= 0)
assert(0);
int index2 = index == 1 ? 2 : 1;
term next;
term arg3 = and(make_int(index),make_int(index2));
actualop code = access_optable(op.men)[op.choice-1];
term t = pDocData->DocProverData.history[get_activeline()];
int err = (*code)(t,arg3,&next,reason);
if(err == 0) // op was successful
{ finish_exec(pDocData,0, op,next,reason);
destroy_ltermlist((ltermlist*)pDocData->papyrus->selected);
pDocData->papyrus->selected = NULL;
++pDocData->DocControlData.totalsteps;
// printf("Incrementing totalsteps in selectMenuChoice\n");
sendSymbolDocument(pDocData,outbuffer,outbuffersize);
return 0;
}
}
c2 = (int) needs_arg(op,needsArgPrompt);
}
// Now c is non-zero if op will need two arguments, and if c is zero, then
// and c2 is non-zero if op will need one; c2 and c c tell what kind of argument
if(c2 == condition_index)
{ int index = index_of_selected_equation();
if(index <= 0)
assert(0); // there must be a selected equation for an operator
// that requires an index of an equation to show up on the menu.
arg = make_int(index);
goto finish;
}
if(c == 0)
c = c2;
if(c)
{ // the op needs an arg. So maybe the
// selected term is the needed arg. Try to apply op
// to the whole line, using the selected term as argument.
// But NOT if the selected term IS the whole line.
term next;
int err = check_arg(arg,c);
if(!err)
{ // OK to use selected term for the arg
actualop code;
finish:
code = access_optable(op.men)[op.choice-1];
term t = pDocData->DocProverData.history[get_activeline()];
if(equals(t,arg))
err = 1; // don't try it if the arg is equal to the selected term.
else
err = (*code)(t,arg,&next,reason);
// int err = exec(op,arg,&next,reason);
// exec is too greedy; on 4x+2=11 with 2 selected and writeassum as op, it applies to 4x+2,
// when we want it to fail, and hence ask the user for args.
if(err == 0) // op was successful
{ finish_exec(pDocData,0, op,next,reason);
destroy_ltermlist((ltermlist*)pDocData->papyrus->selected);
pDocData->papyrus->selected = NULL;
++pDocData->DocControlData.totalsteps;
// printf("Incrementing totalsteps in selectMenuChoice\n");
sendSymbolDocument(pDocData,outbuffer,outbuffersize);
return 0;
}
if(FUNCTOR(t) == AND)
{ // find out which arg of the AND contains the selected term,
// and apply the op to THAT term (e.g. one of a system of linear equations)
term temp;
int index = selected->data.path->data;
err = (*code)(ARG(index,t),arg,&temp,reason);
if(!err)
{ copy(t,&next);
ARGREP(next,index,temp);
finish_exec(pDocData,0, op,next,reason);
destroy_ltermlist((ltermlist*)pDocData->papyrus->selected);
pDocData->papyrus->selected = NULL;
++pDocData->DocControlData.totalsteps;
// printf("Incrementing totalsteps in selectMenuChoice\n");
sendSymbolDocument(pDocData,outbuffer,outbuffersize);
return 0;
}
}
}
// Either the selected term didn't pass check_arg, or the op failed to apply
// to the whole line with selected term as arg, or to the equation containing selected.
// So after the c == 0 case, we'll come back to this and get the arg from the user.
}
if( c == 0 )
// It doesn't need an arg. So attempt to apply the operator to the selected term(s), using fexec:
{ int err = fexec(pDocData, op, pDocData->papyrus->selected);
// remove the selected rectangle (term) whether or not the operation worked.
destroy_ltermlist((ltermlist*)pDocData->papyrus->selected);
pDocData->papyrus->selected = NULL;
if(err == 0)
{ // fexec succeeded or user cancelled out of get_arg dialog
// remove the selected term or terms
++pDocData->DocControlData.totalsteps;
// printf("Incrementing totalsteps in selectMenuChoice\n");
sendSymbolDocument(pDocData,outbuffer,outbuffersize);
return 0;
}
// op failed to apply to the selected term
// so this is failure for this op on the selection menu, since it
// doesn't need an arg because c == 0.
// Possibly the operation left a message in pDocData->papyrus->error_buffer.
// If not we'll put one there now.
if(pDocData->papyrus->error_buffer[0][0] == '\0')
{ // no message left by the operation
char *errmsg = operator_errmsg(op);
strncpy(pDocData->papyrus->error_buffer[0],errmsg,ERRBUFLENGTH);
if(strlen(errmsg) > ERRBUFLENGTH)
{ printf("Hey! error message too long, ProcessMessage.c\n");
printf("%s\n",errmsg);
}
// then sendSymbolDocument will call sendErrors to get this message to the browser.
}
// Now it's up to sendSymbolDocument to convert the error message(s) to SVG.
sendSymbolDocument(pDocData,outbuffer,outbuffersize);
return 0; // success in processing the message
//
}
// Now we know the op needs an argument, and must be applied if possible
// to the selected term. So create and send a "needsargprompt" <svg> element
// containing NeedsArgPrompt. But we also need to send the condition c;
// to do that we code c into the id of this <svg> element.
char id[32];
sprintf(id, "prompt%d", c);
svgSymbolTextInvisibleElement(
needsArgPrompt, // menu string with TeX-like abbreviations
outbuffer, // where to write the SVG code
id, // id for the resulting SVG element, e.g. reason3
"needsargprompt", // the class of the SVG element
outbuffersize,
0, // the color to use
0, // x-coordinate to use
0 // y-coordinate to use
// zero because the prompt will be put in a wrapper div
// with absolute positioning, so they will be positioned relative to the wrapper.
);
int byteswritten = (int) strlen(outbuffer);
outbuffer += byteswritten;
outbuffersize -= byteswritten;
// pDocData->papyrus->selected = NULL; // Not yet!
sendSymbolDocument(pDocData,outbuffer,outbuffersize);
return 0;
}
/*__________________________________________________________________________*/
static int process_symbolWindowResized(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
/* param is windowWidth+windowHeight */
/* This message is used to update the width, height, and reasonstart
in the document. It does not call sendSymbolDocument, because it is an Ajax
message and the resize event is handled by the client. If line breaks
are ever implemented, however, we probably would need to use this as a
normal message and call sendSymbolDoc
*/
{ int windowWidth, windowHeight,reasonstart;
sscanf(param,"%d+%d+%d",&windowWidth,&windowHeight,&reasonstart);
pDocData->papyrus->width = windowWidth;
// papyrus->height has nothing to do with the window height.
pDocData->papyrus->reasonstart = reasonstart;
printf("In process_symbolWindowResized, reasonstart is %d\n", pDocData->papyrus->reasonstart);
// reasonstart is controlled by SymbolDoc.php or another interface file;
// here we just preserve the previous distance from the right edge of the window.
// sendSymbolDocument(pDocData, outbuffer, outbuffersize); // No, see comments above
strcpy(outbuffer,"OK"); // there needs to be some response.
return 0;
}
/*__________________________________________________________________________*/
static int process_graphTitleMoved(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
/* param is [index,x1,y1,x2,y2] specifying which graph,
and the pixel coordinates of the title window, relative to the rectangle that contains
the graphs but not the graph toolbar.
The meaning of index is that it's the title of pDocData->graphs[index] that has moved.
The function has to convert to normalized device coordinates (doubles between 0 and 1,
increasing downwards), in the viewport that exactly contains the graph;
and store the result in pDocData->graphs[index].
If index is out of range, just ignore the message.
*/
{ int index;
double x1,y1,x2,y2;
sscanf(param,"[%d,%lf,%lf,%lf,%lf",&index,&x1,&y1,&x2,&y2);
// verify has already checked that this scanf works and 0 <= index < MAXGRAPHS
int ngraphs = pDocData->ngraphs;
if(index < 0 || index >= ngraphs)
{ send_error("parameter to graphTitleMoved contained illegal index" , outbuffer);
return 0; // message was successfully processed, from Engine's point of view.
}
graph *g = pDocData->graphs[index];
if((x2-x1)*(y2-y1) <= 0)
{ send_error("parameter to graphTitleMoved contained illegal rectangle",outbuffer);
return 0;
}
// (x1,y1) and (x2,y2) are diagonally opposite corners of the title rectangle.
if(x1 > x2)
{ // then swap them
int temp = x2; x2 = x1; x1 = temp;
temp = y2; y2 = y1; y1 = temp;
}
// now x1 < x2 and y1 < y2
int left = g->pxmin;
int bottom = g->pymax;
double width = g->pxmax - g->pxmin; // width of the graph, not the browser window
double height = g->pymax - g->pymin; // height of the graph
// now convert pixel coordinates x1, y1, etc to normalized device coordinates
// normalized device coordinates have 0 at the bottom and increase upwards.
g->txmin = (x1-left)/width;
g->txmax = (x2-left)/width;
g->tymin = (bottom - y2)/height;
g->tymax = (bottom - y1)/height;
strcpy(outbuffer,"OK"); // there needs to be some response.
return 0;
}
/*__________________________________________________________________________*/
static void WorldToPixel(graph *g,double x, double y, double *px, double *py)
// convert world coordinates (x,y) to pixel coordinates in the
// window containing the graphs but not the Toolbar.
{ double xmin = g->xmin;
double xmax = g->xmax;
double ymin = g->ymin;
double ymax = g->ymax;
double worldwidth = xmax-xmin;
double worldheight = ymax-ymin;
double pixelwidth = g->pxmax - g->pxmin;
double pixelheight = g->pymax - g->pymin;
*px = g->pxmin + (x-xmin)* pixelwidth/worldwidth;
*py = g->pymax - (y-ymin)* pixelheight/worldheight;
// pixel coordinates increase downwards
// world coordinates increase upwards
}
/*__________________________________________________________________________*/
static void pixelToWorld(graph *g,double px, double py, double *x, double *y)
// convert pixel coordinates (px,py) to world coordinates (x,y)
{ double pxmin = g->pxmin;
double pxmax = g->pxmax;
double pymin = g->pymin;
double pymax = g->pymax;
double worldwidth = g->xmax-g->xmin;
double worldheight = g->ymax-g->ymin;
double pixelwidth = pxmax - pxmin;
double pixelheight = pymax - pymin;
*x = g->xmin + (px-pxmin)* worldwidth/ pixelwidth;
*y = g->ymax - (py-pymin)* worldheight/pixelheight;
// pixel coordinates increase downwards
// world coordinates increase upwards
}
/*__________________________________________________________________________*/
static void my_gcvt2(double x,int precision,char *ans)
/* like my_gcvt, but language-sensitive. Uses the decimalchar for the
selected language
*/
{ char decimalchar;
char *marker;
my_gcvt(x,precision,ans);
marker = strchr(ans,'.');
if(!marker)
return;
decimalchar = get_decimalpoint_char(get_selected_language());
*marker = decimalchar;
}
/*__________________________________________________________________________*/
static int process_askPointSlope(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
/* param is [px,py], specifying the pixel coordinates
relative to the rectangle that contains
the graphs but not the graph toolbar.
Compute which graph with g->newaxes = 1 the point (px,py) lies in.
If the graphtype is one that makes it possible, convert (px,py) to
world coordinates (x,y), compute y = f(x) where f is g->function, and y' = f'(x1),
where f' is g-> , convert y to pixel coordinates py, and send
back a term (as svg) that is an AND of the three equations
(for example) x=1, y = 2.3, y' = 8.54" as the response.
The function also calls 'snap(PDocData)', which adjust g->selectedx
if it's close to an x-intercept, a y-intercept, a max or min, or crossing
point with another graph on the same axes. So, the calling Javascript
should adjust the position of the crosshairs accordingly when it receives
the response.
*/
{
double px,py,px2;
sscanf(param,"[%lf,%lf]",&px,&py);
// verify has already checked that this scanf works and 0 <= index < MAXGRAPHS
int ngraphs = pDocData->ngraphs;
int k;
int index;
int firstindex = -1;
graph *g;
for(k=0;k < ngraphs;k++)
{ g = pDocData->graphs[k];
if (g->newaxes &&
g->pxmin <= px && px <= g->pxmax &&
g->pymin <= py && py <= g->pymax
)
{ if (firstindex == -1)
firstindex = k;
g->crosshairsflag = 1; // which means crosshairs are visible.
// Let's hope the interface actually makes them visible
// after this message.
}
if (! g->newaxes)
break;
}
int lastindex = k-1; // the last graph on these axes has this index
index = firstindex;
g = pDocData->graphs[index];
// (px,py) is the mouse location, relative to the rectangle that
// contains all the graphs but not the Graph Toolbar.
int mainchoice = g->graphtype;
double x,y,yprime,yIn;
int byteswritten = 0;
void *savenode;
switch(mainchoice)
{ case ORDINARY:
savenode = heapmax();
pixelToWorld(g,px,py,&x,&yIn);
g->selectedx = x;
term t = g->independent_variable;
SETVALUE(t,x);
int rval = snap(pDocData);
if(rval == 0) // snap found something
x = g->selectedx;
deval(t,&x);
deval(g->function,&y);
int closestIndex = index;
double y3, delta = fabs(yIn-y);
for(k=index;k<= lastindex;k++)
{ g = pDocData->graphs[k];
deval(g->function,&y3);
if(fabs(y3-yIn) < delta)
{ closestIndex = k;
y = y3;
}
}
g = pDocData->graphs[closestIndex];
WorldToPixel(g,x,y,&px2,&py);
// if snap worked, px2 != px.
// printf("%lf\n",py);
deval(g->fprime,&yprime);
char tbuffer[32];
char ybuffer[32];
char yprimebuffer[32];
int precision = rval == 0 ? 6 : 3;
my_gcvt2(x,precision, tbuffer);
my_gcvt2(y, precision, ybuffer);
my_gcvt2(yprime, precision, yprimebuffer);
double tval,yval,yprimeval;
sscanf(tbuffer,"%lf",&tval);
// so now tval only has two decimal places thanks to my_gcvt2,
// unless snap worked, and then it has many more.
sscanf(ybuffer,"%lf",&yval);
sscanf(yprimebuffer,"%lf",&yprimeval);
term Tval, Yval, Yprimeval;
Tval = make_double(tval);
Yval = make_double(yval);
Yprimeval = make_double(yprimeval);
term first, second, third,yterm, yprimeterm;
yterm = g->dependent_variable;
yprimeterm = pr(yterm,one);
yprimeterm = pr(yterm,one);
first = equation(t,Tval);
second = equation(yterm,Yval);
third = equation(yprimeterm,Yprimeval);
term ans = and3(first,second,third);
int color = 0; // black
coord level,width,height;
// the response will look like [px2,py] followed by svg code
sprintf(outbuffer,"[%lf,%lf]",px2,py);
byteswritten = (int) strlen(outbuffer);
outbuffer += byteswritten;
outbuffersize -= byteswritten;
int save_anddisplay = get_anddisplay();
set_anddisplay(1); // no brackets, typeset as matrix with no bars
termtoAbsoluteSVGElement(ans, outbuffer, "PointSlope", "PointSlope", outbuffersize, 0, 0, color, &level, &width, &height);
set_anddisplay(save_anddisplay);
reset_heap(savenode);
return 0;
case ODE: // fall through
case ODE2:
pixelToWorld(g,px,py,&x,&yIn);
// get the indices of the parameters corresponding
// to the initial values of x and y
//
// the response will look like [x,yIn]
sprintf(outbuffer,"[%lf,%lf]",x,yIn);
return 0;
default:
strcpy(outbuffer, "OK");
return 0;
}
}
/*_________________________________________________________________________*/
static int process_parameterIncrementChanged(PDOCDATA pDocData,char *param,char *outbuffer, int outbuffersize)
/* parameter is index+newIncrement */
{
int index;
double newIncrement;
sscanf(param,"%d+%lf",&index,&newIncrement);
// verify has checked this conversion, and that index >= 0
int n = pDocData->DocVarData.nparameters;
if(index >= n)
return 1; // interface prevents index >= n anyway
pDocData->DocVarData.parameters[index].increment = newIncrement;
return 0;
}
/*__________________________________________________________________________*/
static int process_checkArg(PDOCDATA pDocData, char *param, char *outbuffer, int outbuffersize)
// This message is sent via Ajax.
// param is c+userText, where userText is what the user entered in the needsArg dialog box,
// and c is the integer coding the condition returned by needs_arg.
// If there's no error, the response is "0+0".
// What is the response supposed to be if there's an error?
{ int c2,err;
char userText[512];
assert(strlen(param)<= 512);
int nfields = sscanf(param,"%d",&c2);
assert(nfields == 1);
char *rest = strchr(param, '+');
if(rest == NULL)
assert(0); // verify already checked these conditions
if(rest[1] == '\0')
assert(0);
strcpy(userText,rest+1);
condition c = (condition) c2;
term t;
char *marker;
char response[256];
int parseerr= bparse(get_parser_flags(),&t,userText,&marker);
if(!parseerr)
{ err = check_arg(t,c);
if(!err && c == intbyparts1)
{ // we need to compute v so the integrand is u dv
// where the integral in question is the selected term
ltermlist *selected = (ltermlist*) pDocData->papyrus->selected;
term integral = abstract(selected->data);
assert(FUNCTOR(integral) = INTEGRAL);
term udv = ARG(0,integral);
term x = ARG(1,integral);
term u,v,dv;
u = t;
int err2 = tryparts(udv,x,u,&v,&dv);
char errbuf[512];
char errbuf2[512];
int level,width,height;
if(err2 == 1)
{ strncpy(errbuf, english(786),512);
/* You must enter a term that divides the integrand */
svgSymbolTextElement(errbuf, outbuffer, "error0","mathError",
outbuffersize,
255, // red
30, // x-coordinate to use
30); // y-coordinate
return 0;
}
if(err2 == 2) /* can't integrate dv */
{ strncpy(errbuf,english(787),512);
/* MathXpert can't integrate 'dv' for this choice of 'u' */
svgSymbolTextElement(errbuf, outbuffer, "error0","mathError",
outbuffersize,
255, // red
30, // x-coordinate to use
30); // y-coordinate
int byteswritten = (int) strlen(outbuffer);
outbuffer += byteswritten;
outbuffersize -= byteswritten ;
strncpy(errbuf2,english(788),512);
/* so that choice of u won't work. */
svgSymbolTextElement(errbuf, outbuffer, "error0","mathError",
outbuffersize,
255, // red
30, // x-coordinate to use
50); // y-coordinate
return 0;
}
if(err2 == 0)
{ term output = and3(
equation(MAKE_ATOM('u'),t),
equation(product(MAKE_ATOM('d'),MAKE_ATOM('v')),
product3(dv,MAKE_ATOM('d'),x)),
equation(MAKE_ATOM('v'),v)
);
termtoAbsoluteSVGElement(output, outbuffer,"feedback1" , "feedback", outbuffersize, 40,15, 0, &level, &width, &height);
return 0;
}
}
if(!err && c == intbyparts2)
{ // The second time OK is pressed in the getArg dialog
char *message = "0+0"; // it parsed, and it passed check_arg
strcpy(outbuffer,message);
return 0;
}
if(!err)
{ /* userText parsed and passed its check */
char *message = "0+0"; // it parsed, and it passed check_arg
strcpy(outbuffer,message);
return 0;
}
}
// Now there was an error, either parseerr or err
if(!parseerr)
{ int rval = checkarg_msg(err, response, sizeof(response));
assert(rval == 0);
}
else
{ strcpy(response, pem(parseerr)); // parser error message, in selected natural language
}
// now convert response to SVG
svgSymbolTextInvisibleElement(response, outbuffer, "error1","checkargError", outbuffersize,0,0,0);
return 0;
}
/*__________________________________________________________________________*/
#if 0
static int process_unSelectRectangle(PDOCDATA storedDoc, char *param, char *outbuffer,int outbuffersize)
// remove the selected rectangle(s) from the document.
// The interface will have to remove the display of the selected rectangle on its own.
{ ltermlist *selected = storedDoc->papyrus->selected;
destroy_ltermlist(selected);
storedDoc->papyrus->selected = NULL;
strcpy(outbuffer,""); // empty response
return 0;
}
#endif
/*__________________________________________________________________________*/
int process_message(PDOCDATA storedDoc, char *message, char *param, char *outbuffer, int outbuffersize)
// returns 0 for success or nonzero for developer error.
// 0 means success processing the message and message was legal; mathematical error
// is not an error in processing a message.
// writes the response to the message to outbuffer (if there is enough space)
// If there is an error, the response begins with "ERROR:" followed by the error message.
// These error messages are for the client-side developers, not the MathXpert user.
{ memset(outbuffer, 0, outbuffersize);
printf("process_message received %s %s %d\n", message, param,outbuffersize);
if(message == NULL || param == NULL)
{
char *errmsg;
if(message == NULL)
{ errmsg = "process_message received illegal NULL message.\n The message was ignored.";
send_error(errmsg,outbuffer);
}
else
{ errmsg = malloc(strlen(message) + 200);
sprintf(errmsg, "process_message received illegal NULL parameter to the %s message. The message was ignored.", message);
send_error(errmsg,outbuffer);
free(errmsg);
}
return 1;
}
char errbuf[512];
int err = verify(message, param, errbuf, 512);
if(err)
{ send_error(errbuf,outbuffer);
return 1;
}
// OK, it's a legal message.
storedDoc->lastMessageTime = time(NULL);
// First consider messages from GetProblem phase, before a document is initialized.
if (!strcmp(message, "parseAndDisplay"))
return process_parseAndDisplay(storedDoc, param, outbuffer, outbuffersize);
if (!strcmp(message, "asknProblems"))
return process_asknProblems(storedDoc, param, outbuffer, outbuffersize);
if (!strcmp(message, "askProblemsText"))
return process_askProblemsText(storedDoc, param, outbuffer, outbuffersize);
if (!strcmp(message, "askSubjectStrings"))
return process_askSubjectStrings(storedDoc, param, outbuffer, outbuffersize);
if (!strcmp(message, "askTopicStrings"))
return process_askTopicStrings(storedDoc, param, outbuffer, outbuffersize);
if (!strcmp(message, "askProblemsSVG"))
return process_askProblemsSVG(storedDoc, param, outbuffer, outbuffersize);
if (!strcmp(message, "initSymbolDocFromLibrary"))
return process_initSymbolDocFromLibrary(storedDoc, param,outbuffer,outbuffersize);
if (!strcmp(message, "initGraphDocFromLibrary"))
return process_initGraphDocFromLibrary(storedDoc,param,outbuffer,outbuffersize);
if (!strcmp(message, "zoomAtPoint"))
return process_zoomAtPoint(storedDoc, param,outbuffer,outbuffersize);
if (!strcmp(message, "graphMoved"))
return process_graphMoved(storedDoc, param,outbuffer,outbuffersize);
if (!strcmp(message, "setLanguage"))
return process_setLanguage(storedDoc,param, outbuffer, outbuffersize);
if (!strcmp(message, "setupAndCheckGraph"))
return process_setupAndCheckGraph(storedDoc, param, outbuffer, outbuffersize);
if (!strcmp(message, "setupAndCheckSymbol"))
return process_setupAndCheckSymbol(storedDoc, param, outbuffer, outbuffersize);
if (!strcmp(message, "drawAll"))
return process_drawAll(storedDoc, param, outbuffer, outbuffersize);
if (!strcmp(message, "startSolving"))
return process_startSolving(storedDoc, param, outbuffer, outbuffersize);
// Next consider Ajax messages, which do not require sending the whole document in response
if (!strcmp(message, "checkArg"))
return process_checkArg(storedDoc,param, outbuffer, outbuffersize);
if(!strcmp(message, "symbolWindowResized"))
return process_symbolWindowResized(storedDoc, param, outbuffer, outbuffersize);
if (!strcmp(message, "destroyDocument"))
{ destroyDocument(storedDoc);
return 0;
}
if (!strcmp(message, "graphTitleMoved"))
return process_graphTitleMoved(storedDoc, param, outbuffer, outbuffersize);
if (!strcmp(message, "askPointSlope"))
return process_askPointSlope(storedDoc, param, outbuffer, outbuffersize);
if (!strcmp(message, "parameterIncrementChanged"))
return process_parameterIncrementChanged(storedDoc,param,outbuffer, outbuffersize);
// Next consider messages that require sendSymbolicDoc in response:
// These are all sent from a Symbol view.
if (!strcmp(message, "autoStep"))
return process_autoStep(storedDoc,"dummy", outbuffer, outbuffersize);
if (!strcmp(message, "showStep"))
return process_showStep(storedDoc,"dummy", outbuffer, outbuffersize);
if (!strcmp(message, "autoFinish"))
return process_autoFinish(storedDoc,"dummy", outbuffer, outbuffersize);
if (!strcmp(message, "initAndAutoFinish"))
return process_initAndAutoFinish(storedDoc,param, outbuffer, outbuffersize);
if (!strcmp(message, "finished"))
return process_finished(storedDoc,"dummy", outbuffer, outbuffersize);
if (!strcmp(message, "undo"))
return process_undo(storedDoc, "dummy", outbuffer, outbuffersize);
if (!strcmp(message, "hint"))
return process_hint(storedDoc,"dummy", outbuffer, outbuffersize);
if(!strcmp(message, "selectMenuChoice"))
return process_selectMenuChoice(storedDoc,param, outbuffer,outbuffersize);
if(!strcmp(message, "selectedRectangleSymbol"))
return process_selectedRectangleSymbol(storedDoc,param,outbuffer,outbuffersize);
if(!strcmp(message, "graphWindowResized"))
return process_graphWindowResized(storedDoc, param, outbuffer, outbuffersize);
if(!strcmp(message, "execOpWithArg"))
return process_execOpWithArg(storedDoc, param, outbuffer, outbuffersize);
// Next consider messages that require sendGraphDoc in response:
// Except "graphButton" these are all sent from a Graph view
if (!strcmp(message, "graphButton"))
{ int err = process_graphButton(storedDoc,param,outbuffer,outbuffersize);
if(err)
{ char *errmsg = (char *) input_error_message(err);
send_error(errmsg,outbuffer);
return err;
}
return 0;
}
if(!strcmp(message, "selectedRectangleGraph"))
return process_selectedRectangleGraph(storedDoc,param,outbuffer,outbuffersize);
if (!strcmp(message, "incrementActiveParameter"))
return process_incrementActiveParameter(storedDoc,param, outbuffer, outbuffersize);
if (!strcmp(message, "decrementActiveParameter"))
return process_decrementActiveParameter(storedDoc,param, outbuffer, outbuffersize);
if (!strcmp(message, "incrementParameter"))
return process_incrementParameter(storedDoc,param, outbuffer, outbuffersize);
if (!strcmp(message, "decrementParameter"))
return process_decrementParameter(storedDoc,param, outbuffer, outbuffersize);
if (!strcmp(message, "horizontalzoomout"))
return process_horizontalzoomout(storedDoc,param, outbuffer, outbuffersize);
if (!strcmp(message, "horizontalzoomin"))
return process_horizontalzoomin(storedDoc,param, outbuffer, outbuffersize);
if (!strcmp(message, "verticalzoomout"))
return process_verticalzoomout(storedDoc,param, outbuffer, outbuffersize);
if (!strcmp(message, "verticalzoomin"))
return process_verticalzoomin(storedDoc,param, outbuffer, outbuffersize);
if (!strcmp(message, "doublezoomout"))
return process_doublezoomout(storedDoc,param, outbuffer, outbuffersize);
if (!strcmp(message, "doublezoomin"))
return process_doublezoomin(storedDoc,param, outbuffer, outbuffersize);
if (!strcmp(message, "toggleDirectionField"))
return process_toggleDirectionField(storedDoc,param,outbuffer, outbuffersize);
if (!strcmp(message, "toggleErase"))
return process_toggleErase(storedDoc,param,outbuffer, outbuffersize);
if (!strcmp(message, "circularAspect"))
return process_circularAspect(storedDoc,param,outbuffer,outbuffersize);
if (!strcmp(message, "setGraphPaper"))
return process_setGraphPaper(storedDoc,param,outbuffer,outbuffersize);
if (!strcmp(message,"updateParameters"))
return process_updateParameters(storedDoc, param, outbuffer, outbuffersize);
char errmsg[512];
strcpy(errmsg,"\nprocess_message received an unrecognized message, which was ignored. Namely,\n ");
strcat(errmsg,message);
send_error(errmsg,outbuffer);
printf("%s",errmsg);
return 1;
}
/*_____________________________________________________________________*/
static int use_dfield(term t)
/* t is a pair of differential equations x' = u, y'= v. Return 1
if direction fields make sense, 0 if not. That is, return 1
if u and v contain only x and y, and not the independent variable
*/
{ term eq1, eq2, u,v,x,y;
unsigned f = 0; // initialize to avoid a warning below
term *varlist = get_varlist();
int nvars = get_nvariables();
if(nvars == 2)
return 1; // no independent variable was in the problem, e.g. y' = x, x' = y
eq1 = ARG(0,t);
eq2 = ARG(1,t);
u = ARG(1,eq1);
v = ARG(1,eq2);
x = ARG(0,ARG(0,eq1));
y = ARG(0,ARG(0,eq2));
assert(ISATOM(x) && ISATOM(y));
int k;
for(k=0;k<nvars;k++)
{ if(!equals(varlist[k],x) && !equals(varlist[k],y))
{ f = FUNCTOR(varlist[k]); // assume this is the independent variable
break;
}
}
if(contains(u,f) || contains(v,f))
return 0;
return 1;
}
/*__________________________________________________________________________*/
static int process_setGraphPaper(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
/* param should be width+height+index to change graphpaper to 'index' in all graphs, or
width+height+index+whichgraph to affect just one graph
*/
{
int windowwidth, windowheight,index,whichgraph;
// this is width of the graph rectangle excluding toolbar
int nfields = sscanf(param,"%u+%u+%d+%d",&windowwidth,&windowheight,&index,&whichgraph);
if(nfields < 3)
{ send_error("Incorrect parameter to message setGraphPaper",outbuffer);
return 1;
}
rect r;
r.left = 0;
r.top = 0;
r.right = windowwidth;
r.bottom = windowheight;
set_svgDevice(outbuffer, outbuffersize,&r);
int ngraphs = pDocData->ngraphs;
GraphPaper G = GetGraphPaper(index);
if(nfields == 3)
{ for(int i=0;i< ngraphs;i++)
{ graph *g = pDocData->graphs[i];
g->grpaper = G;
g->axescolor = G.axescolor;
g->axeslabelcolor = G.axeslabelcolor;
g->background = G.background;
}
}
else
{ if (whichgraph >= ngraphs)
return 1;
graph *g = pDocData->graphs[whichgraph];
g->grpaper = G;
g->axescolor = G.axescolor;
g->axeslabelcolor = G.axeslabelcolor;
g->background = G.background;
}
return sendGraphDocument(pDocData);
}
/*__________________________________________________________________________*/
static int process_updateParameters(PDOCDATA pDocData, char *param, char *outbuffer,int outbuffersize)
/* param might be for example
"a|1.23|0.1|0;b|4.56|0.2|1"
See AdjustParam.js for how it is created
*/
{ int index=0;
double increment,value;
char *data;
char name[50];
parameter *params = pDocData->DocVarData.parameters;
// Tokenize the string by ';' to get each parameter
int width, height;
// param has the form width+height+codedParameterArray
sscanf(param,"%u+%u",&width,&height);
// verify has already checked that this conversion works
data = strchr(strchr(param,'+')+1,'+')+ 1;// the part following second +
char *token;
while ((token = strsep(&data, ";")) != NULL)
{ sscanf(token, "%49[^#]#%lf#%lf#%d",name,&value,&increment,&index);
if( index > pDocData->DocVarData.nparameters)
{ // assert(0);
return 1;
}
if(strcmp(params[index].name, name))
{ // assert(0);
return 1;
}
*params[index].addr = value;
params[index].increment = increment;
}
rect r;
r.left = 0;
r.top = 0;
r.right = width;
r.bottom = height;
set_svgDevice(outbuffer, outbuffersize,&r);
return sendGraphDocument(pDocData);
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists