Sindbad~EG File Manager
/* M. Beeson, for Mathpert
6.10.94 file created from older code
3.18.99 last modified
1.15.00 added call to polyform
4.20.00 modified init_params at and after "if(k==3)"
5.3.00 modified initialize_two_odes
7.13.00 modified for grpaper
7.16.00 made pixel_fields exported
8.1.00 added ODESYSTEM at line 193
8.1.00 added if(g->tmin == g->tmax) in create_graph where g->tmin is set
8.1.00 added ODESYSTEM and ODE2 in adjust_graph
10.7.04 added code about piflag around line 697
10.8.04 modified just before call to make_title for MC_INEQ
10.9.04 removed that modification.
Set graphs[0]->function correctly for MC_INEQ in fill_graph_structures
Added code to suppress the title of the second graph in MC_INEQ (search for showtitles)
Added code to set g->tselected, g->selectedx, g->selectedy on parametric and polar graphs
10.10.04 added code to fill_graph_structures to set g->fprime and g->gprime on
parametric and polar graphs, and to set rvariable to the text
form of eigenvariable on parametric graphs for use in the Point,
Slope, and Parameter dialog.
5.5.05 fixed graphs[i]->yvariable around lines 380-390 to eliminate '$' coming from mstring.
9.11.07 modified fill_graph_structures under SIMPSONSRULE
added set_parameters
8.28.11 modified init_two_odes to create a parameter for t0.
9.5.11 modifed init_two_odes on two_odes to change the order of the parameters
9.8.11 cast result of strlen to int for 64-bit compilation without warnings
5.17.13 wrote get_default_value and code that calls it.
5.20.13 removed include getprob.h
5.22.13 corrected initialize_high_order_ode
6.10.13 used xfunction and yfunction to store the symbolic forms of left and right in RIEMANNSUMS graphs
6.11.13 modified default_range for RIEMANNSUMS when using left or centered rectangles
added RIEMANNSUMS and TRAPEZOIDRULE at line 856
6.12.13 at line 767, set graphs[1]->left = graphs[0]->left etc.
10.1.13 changed i to i+1 at line 1338
12.1.14 modified line 1008, see comment there.
12.9.14 modified near "denormalized"
12.11.23 changed oem_atom_string to atom_string
2.24.24 changed include cgraph. to svgGraph.h and dstate to svgDevice
2.25.24 made setupdata also allocate graphs[0]
2.25.24 removed calls to get_aspect();
2.26.24 added thin_lines_only and made fill_graph_structures intialize linewidth
3.13.24 adjusted txmin and tymax in create_graph.
3.21.24 introduced aspect in create_graph
3.23.24 changed the code in create_graph to set g->tymin
6.25.24 removed call to init_params in fill_graph_structures under RIEMANN_SUMS
6.29-30.24 changes to initializing RIEMANN_SUMS according as it's an integral_test
problem (arity 5) or not.
6.30.24 set g->titlecolor under RIEMANN_SUMS
7.2.24 added code in fill_graph_structures to set graphs[i]->txmax and graphs[i]->tymax
7.5.24 made create_graph set g->titlebackgroundcolor.
7.6.24 changed setting of titlebackgroundcolor on COMPARE_SAME
7.6.24 deleted two lines setting graphs[i]->whichgraph in fill_graph_structures
7.7.24 added two lines to change graphs[i]->titlebackgroundcolor etc on COMPARE_SAME in fill_graph_structures
7.7.24 Modified fill_graph_structures just below // Here is where to set txmax and tymin
set titlebackgroundcolor on RIEMANNSUMS in fill_graph_structures.
9.4.24 made fill_graph_structures set g->parameter_interval on PARAMETRIC
9.5.24 also on POLAR_CIRCULAR
9.6.24 removed assertions that prevented input f(x) = sin x in comparederiv
and in setupdata
9.6.24 modified initialize_two_odes to always allocate space for three initial values
9.7.24 modified use_dfield and arranged it is called only if g->independent_variable is not set,
which it always will be on user-entered problems [it got moved to ProcessMessage.c later]
12.3.24 added adjustToPaper to create_graph
12.10.24 modified fill_graph_structures to store lo and hi for SIMPSONSRULE graphs, so the
title will be made correctly.
12.13.24 removed call to trigsimp3 in fill_in_singularities.
1.7.25 added || entire(t) to fill_in_singularities.
1.20.25 modified default_range on HDE and ODE.
1.21.25 set graphs[i]->yvariable at line 475
1.22.25 strncpy line 390
2.24.25 adjusted init_params to account for the situation when there are already
some parameters initialized, as in differentiate_from_defn.
2.26.25 added in fill_graph_structures: graphs[0]->right += 1; // so last rectangle begins at 'hi'
3.9.25 set g->linewidth = 2 on ODE and ODE2 in create_graph
*/
#include <string.h>
#include <math.h> // ceil
#include <assert.h>
#include <stdio.h> // printf, used sometimes for debugging
#include "globals.h"
#include "graphstr.h"
#include "mpdoc.h"
#include "tdefn.h"
#include "deval.h"
#include "prover.h" // for singularities and critical_points
#include "deriv.h" // derivative
#include "probtype.h" // problemtype and its values
#include "mplimits.h" // LIMITAND
#include "svgGraph.h" // device, ASPECT, etc.
#include "algaux.h"
#include "mstring.h"
#include "grafinit.h"
#include "sderiv.h"
#include "preferences.h" // get_initgraphcolors
#include "rgb.h"
#include "wcolors.h" // RED24, YELLOW24, etc.
#include "cflags.h"
#include "polynoms.h"
#include "wtitle.h" /* make_title */
#include "display.h" /* bblock */
#include "pvalaux.h" /* isinteger */
#include "chkinput.h" /* graph_interval */
#include "exec.h" /* erase_colors */
#include "ratsimp.h"
#include "trigpoly.h" /* algpoly */
#include "trigdom.h" /* trigsing */
#include "reset.h" /* save_and_reset */
#include "trigsimp.h"
#include "checkarg.h" /* execute.h needs it */
#include "execute.h" /* autoans */
#include "dispfunc.h" /* atom_string */
#include "grpaper.h" /* GetSelectedGraphPaper */
#include "activedoc.h" /* GetActiveDoc */
#include "domain.h" /* defined_on_interval */
#include "pstring.h" /* log_term, for debugging */
#include "display1.h" /* block, so HEIGHT and WEIGHT work */
static void create_graph(int mainchoice, graph *g, int whichgraph, int ngraphs);
static void initialize_ode(term, graph *);
static void initialize_two_odes(term,int, graph *);
static void initialize_high_order_ode(term, graph *);
static void fill_in_singularities(term, graph *);
static void init_params(int mainchoice, term t, int k);
static void set_parameters(term t);
static double get_default_value(term x, term t);
static int thin_lines_only(int mainchoice);
/*____________________________________________________________________*/
void set_graph_range(graph *g, term ineq)
/* ineq must be an interval_as_and, i.e. you must specify both
min and max, or neither.
*/
/* use ineq to set g->xmin, etc. */
{ if(!interval_as_and(ineq))
return; // don't do anything; also we don't put assert(0);
term x = g->independent_variable;
term y = g->dependent_variable;
/* ineq will have just one variable */
term *atomlist;
variablesin(ineq,&atomlist);
if(equals(atomlist[0],x))
{ deval(ARG(0,ARG(0,ineq)),&g->xmin);
deval(ARG(1,ARG(1,ineq)),&g->xmax);
}
if(equals(atomlist[0],y))
{ deval(ARG(0,ARG(0,ineq)),&g->ymin);
deval(ARG(1,ARG(1,ineq)),&g->ymax);
}
}
/*____________________________________________________________________*/
void setupdata(int *ngraphs, int *mainchoice, term t, graph **graphs)
/* Using mainchoice, currenttopic, and t (the function(s)
to be graphed) decide what kind of graph(s) we must make, and
allocate and set up the required data structures graphs[i].
(The array of pointers 'graphs' has a fixed dimension and is
statically allocated; but the structures it points to are
dynamically allocated here on the document's heap.)
This function sets the values of *ngraphs
and can change the value of mainchoice in some cases.
Initialize the 'parameters' array.
( All variables in t except the independent variable varlist[0] and
the dependent variables are parameters--there may be MORE variables
in varlist that were introduced by the theorem-prover. Do NOT presume
that the parameters all come LAST in the varlist.)
Possible values of mainchoice are
ORDINARY, PARAMETRIC, POLAR, POLAR_CIRCULAR, POLYROOT,
COMPARE_SAME, COMPARE_DIFFERENT, INEQUALITY,
COMPAREDERIV, COMPAREDERIVS,
CONTOUR, COMPLEX_CONTOUR, RELATION,
ODE, ODE2, ODESYSTEM, HDE (explained in graphstr.h)
All of these except POLAR_CIRCULAR and ODESYSTEM correspond directly
to menu choices. These two, however, are different: POLAR_CIRCULAR
arises from the POLAR choice when the user wants circles on her graph,
and ODESYSTEM arises from 'two odes' when direction fields won't be
appropriate, i.e. y'=f(x,y,t), x'=g(x,y,t), and t actually appears on
the right.
When this function is called, mainchoice has been set already to
correspond to the topic, so it only needs to be adjusted in case it
is POLAR and should be POLAR_CIRCULAR, or in case it is ODE2 and should
be ODESYSTEM (direction fields don't work, see below). Also, in case
the singularities cannot be calculated, we shift mainchoice to
RELATION so that the graphrelation will be used instead of carefulgraph
to draw the graph.
This function presumes that set_device has already been called.
It also presumes that 'varlist' has been set up by 'get_problem',
so it contains a list (dynamic array actually) of all variables in the problem,
and the 'eigenvariable' has been correctly chosen.
The term t is the problem, in "standard form". Thus for 'ordinary graph' or
'polar graph', t will be an equation, with the dependent variable on the left.
For 'parametric graph', it will be two equations (with functor AND); for
polyroot it will be a polynomial. If this wasn't true on input,
enhance_problem has arranged it. Also, enhance_problem has added the
derivatives in, in case of topics _comparefandfprime and _comparetwoderivs.
For _solve_ode, it will be y'=f(x,y) or diff(y,x) = f(x,y).
For _solve_two_odes, it will be two equations, (x'= f AND y' = g),
or possibly with dx/dt instead of x'.
For _high_order_ode it will be one equation y'''' = f(x,y,y'...),
or diff(y,x,n) = f(x,y,y'...).
For these three topics,
there are four values of mainchoice, ODE, ODE2, ODESYSTEM, and HDE.
The value of mainchoice is used to control whether
direction fields work--they do work for ODE (y'=f(x,y)) and ODE2
(y'=f(x,y), x'=f(x,y), t doesn't apppear on right), but not for
ODESYSTEM or HDE.
*/
{ int i;
term q;
if(FUNCTOR(t) == OR && ARITY(t) == 2 && *mainchoice != POLAR_CIRCULAR)
/* strip_intervals has already been called by process_ok, so
ARG(1,t) is a graph_interval or an AND of graph_intervals.
Note: Riemann sum graphs etc. come in with an OR functor,
but the arity is 5 or 6.
Let them pass! */
{ q = ARG(1,t);
t = ARG(0,t);
}
else
SETFUNCTOR(q,ILLEGAL,0);
/* first set ngraphs, xvariable, and yvariable */
switch(*mainchoice)
{ /* cases from algebra menu */
case ORDINARY:
*ngraphs = 1;
break;
case COMPARE_DIFFERENT:
case COMPARE_SAME:
*ngraphs = ARITY(t); /* enhance_problem has padded it */
assert(ARITY(t) >= 2 && (FUNCTOR(t)==AND || FUNCTOR(t)==OR));
break;
case MC_SET:
*ngraphs = 1;
break;
case MC_INEQ:
*ngraphs = FUNCTOR(t)==AND ? 2 : 1;
break;
case COMPAREDERIVS:
case COMPAREDERIV:
*ngraphs = ARITY(t); /* enhance_problem has padded it */
assert(ARITY(t) >= 2 && (FUNCTOR(t)==AND || FUNCTOR(t)==OR));
// assert(ISATOM(ARG(0,ARG(0,t))));
// no, because f(x) = sin x is allowed
break;
case RELATION:
*ngraphs = 1;
break;
case POLYROOT:
*ngraphs = 1;
break;
/* cases from trig menu */
case POLAR_CIRCULAR:
case POLAR:
*ngraphs = 1;
break;
case PARAMETRIC: /* fall through */
case SPACECURVE:
*ngraphs = 1;
break;
case COMPLEX_CONTOUR:
*ngraphs = 1; /* ONE real function of a complex variable */
break;
case CONTOUR:
*ngraphs = 1;
break;
case NONPARAMETRICSURFACE: /* fall through */
case POLARNONPARAMETRICSURFACE:
case PARAMETRICSURFACE:
*ngraphs = 1;
break;
case ODE:
*ngraphs = 1;
break;
case ODE2:
*ngraphs = 1;
break;
case ODESYSTEM:
*ngraphs = 1;
break;
case RIEMANNSUMS:
case SIMPSONSRULE:
case TRAPEZOIDRULE:
*ngraphs = 2;
break;
case HDE:
*ngraphs = 1;
break;
default: assert(0);
}
/* Now mainchoice and ngraphs are set */
/* Allocate the graph structures */
for(i=0;i<*ngraphs; i++)
{ graphs[i] = (graph *) callocate(1,sizeof(graph));
/* They will last as long as the document, and get destroyed
only when the document heap is destroyed under WM_DESTROY.
Using callocate gets the data initialized to zero.
*/
if(graphs[i] == NULL)
nospace();
}
fill_graph_structures(*ngraphs, *mainchoice, t, graphs);
if(FUNCTOR(q) != ILLEGAL)
/* Graph ranges have been typed in with the problem */
adjust_graph_structures(*ngraphs,q,graphs);
}
/*_______________________________________________________________*/
static int thin_lines_only(int mainchoice)
/* If graphs of the specified kind cannot have
user-choosable thin or thick lines, return 1. */
{ switch(mainchoice)
{ case RELATION:
case MC_SET:
case POLYROOT:
case CONTOUR:
case COMPLEX_CONTOUR:
return 1;
}
return 0;
}
/*____________________________________________________________________*/
void fill_graph_structures(int ngraphs, int mainchoice, term t, graph **graphs)
/* the array graphs contains ngraphs allocated graph structures;
t is a term giving the function or functions to be graphed;
mainchoice tells what kind of graphs.
Fill out the graph structures, setting
xvariable, yvariable, function, xfunction, yfunction as appropriate,
depending on mainchoice. Even if not used, some valid values must
be entered, because otherwise there will be a crash when saving the
document, as these values will be read in order to save the document
data structure.
*/
{ int i,err;
short saveit;
int flag = 0;
char localbuf[83];
char *temp;
double savet;
graph *g;
term *varlist = get_varlist();
term eigenvariable = get_eigenvariable();
for(i=0;i<ngraphs;i++)
{ graphs[i]->xfunction = graphs[i]->yfunction = zero;
graphs[i]->fprime = graphs[i]->gprime = zero;
if (thin_lines_only(mainchoice))
graphs[i]->linewidth = 1;
else if (mainchoice == ODE2 || mainchoice == ODE)
graphs[i]->linewidth = 2; // use medium line thickness with direction fields.
else
graphs[i]->linewidth = 3;
}
/* ensure that valid values are present in the data structure.
For types where these elements are actually used, they will
be filled in with more meaningful values below. This just
prevents these fields from containing garbage.
*/
switch(mainchoice)
{ case ORDINARY:
assert(FUNCTOR(t) == '='); /* ensured by enhance_problem */
graphs[0]->xvariable = atom_string(eigenvariable);
graphs[0]->independent_variable = eigenvariable;
graphs[0]->dependent_variable = ARG(0,t);
/* Possibly it isn't an atom, e.g. it could be f(x) */
if(ISATOM(ARG(0,t)))
graphs[0]->yvariable = atom_string(ARG(0,t));
else /* e.g., graphing f(x) = x + 3; there is no
dependent_variable. */
{ int k,err;
localbuf[0] = '\0';
err = mstring(ARG(0,t),localbuf);
k = (int) strlen(localbuf);
if(localbuf[0] == '$')
{ int j=k-1;
while(j >= 0 && localbuf[j] != '$')
--j;
if(j == 0)
assert(0);
localbuf[j] = '\0'; /* remove final $ */
k = (int) strlen(localbuf);
}
if(!err && k < 20)
{ graphs[0]->yvariable = (char *) callocate(k+2,sizeof(char));
strncpy(graphs[0]->yvariable,
localbuf[0] == '$' ? localbuf + 1 : localbuf,
k+1);
}
else
graphs[0]->yvariable = NULL;
}
printf("setting graphs[0]->function in grafinit line 395\n");
graphs[0]->function = ARG(1,t);
saveit = get_nextassumption();
set_nextassumption(-1); /* do not make any assumptions while
calculating the derivative */
graphs[0]->fprime = derivative(graphs[0]->function,graphs[0]->independent_variable);
set_nextassumption(saveit);
init_params(mainchoice,t,2);
break;
case COMPAREDERIV:
case COMPAREDERIVS:
/* similar to COMPARE_DIFFERENT and COMPARE_SAME, but we
don't need to compute the derivatives for graphs[0]->fprime
or graphs[1]->fprime, as they're already in t. */
graphs[0]->xvariable = atom_string(eigenvariable);
for(i=0;i<ngraphs;i++)
{ if(i>0)
graphs[i]->xvariable = graphs[0]->xvariable;
if(ISATOM(ARG(0,ARG(i,t))))
graphs[i]->yvariable = atom_string(ARG(0,ARG(i,t)));
else
{ err = mstring(ARG(0,ARG(i,t)),localbuf);
if(!err && strlen(localbuf)<10)
{ graphs[i]->yvariable = mallocate((int) strlen(localbuf)+1);
if(graphs[i]->yvariable == NULL)
nospace();
strcpy(graphs[i]->yvariable,localbuf);
}
}
graphs[i]->function = ARG(1,ARG(i,t));
graphs[i]->independent_variable = eigenvariable;
graphs[i]->dependent_variable = ARG(0,ARG(i,t));
saveit = get_nextassumption();
set_nextassumption(-1); /* do not make any assumptions while
calculating the derivative */
if(i == 0 && FUNCTOR(t) == AND && FUNCTOR(ARG(1,t)) == '=')
graphs[0]->fprime = ARG(1,ARG(1,t));
else if(i==1 && FUNCTOR(t) == AND && ARITY(t) > 2 && FUNCTOR(ARG(2,t)) == '=')
graphs[1]->fprime = ARG(1,ARG(2,t));
else
graphs[i]->fprime = derivative(graphs[i]->function,graphs[i]->independent_variable);
set_nextassumption(saveit);
}
init_params(mainchoice,t,2);
break;
case COMPARE_DIFFERENT: /* fall-through */
case COMPARE_SAME :
graphs[0]->xvariable = atom_string(eigenvariable);
int count_intervals = 0;
for(i=0;i<ngraphs;i++)
{ int k;
if(graph_interval(ARG(i,t)))
{
set_graph_range(graphs[0],ARG(i,t));
++count_intervals;
continue; // to next i
}
if(i>0)
graphs[i]->xvariable = graphs[0]->xvariable;
localbuf[0] = 0;
err = mstring(ARG(0,ARG(i,t)),localbuf);
k = (int) strlen(localbuf);
if(localbuf[0] == '$')
{ int j=k-1;
while(j >= 0 && localbuf[j] != '$')
--j;
if(j == 0)
assert(0);
localbuf[j] = '\0'; /* remove final $ */
k = (int) strlen(localbuf);
}
if(!err && k<11)
{ graphs[i]->yvariable = mallocate(k+1);
if(graphs[i]->yvariable == NULL)
nospace();
strcpy(graphs[i]->yvariable,localbuf + ((localbuf[0] == '$') ? 1 : 0));
}
graphs[i]->function = ARG(1,ARG(i,t));
graphs[i]->independent_variable = eigenvariable;
graphs[i]->dependent_variable = ARG(0,ARG(i,t));
saveit = get_nextassumption();
set_nextassumption(-1); /* do not make any assumptions while
calculating the derivative */
graphs[i]->fprime = derivative(graphs[i]->function,graphs[i]->independent_variable);
set_nextassumption(saveit);
}
if(count_intervals)
{ SETFUNCTOR(t,AND,ARITY(t)-count_intervals);
// that would be all if the intervals were the last
// one or two args. For now we assume that.
// But t is only a local variable so this re-setting
// the arity doesn't get into the docdata structure yet.
ngraphs = ARITY(t); // used below in this function
}
init_params(mainchoice,t,2);
break;
case PARAMETRIC:
g = graphs[0];
g->independent_variable = eigenvariable;
/* graphs[0]->dependent_variable not used */
g->xvariable = atom_string(ARG(0,ARG(0,t)));
g->yvariable = atom_string(ARG(0,ARG(1,t)));
g->rvariable = atom_string(eigenvariable);
g->xfunction = ARG(1,ARG(0,t));
g->yfunction = ARG(1,ARG(1,t));
g->fprime = derivative(g->xfunction,eigenvariable);
g->gprime = derivative(g->yfunction,eigenvariable);
g->tmin = 0.0;
g->tmax = 2*PI_DECIMAL;
g->tselected = 0.5 * (g->tmax + g->tmin);
savet = VALUE(g->independent_variable);
SETVALUE(g->independent_variable,g->tselected);
deval(g->xfunction,&g->selectedx);
deval(g->yfunction,&g->selectedy);
SETVALUE(g->independent_variable,savet);
init_params(mainchoice,t,3);
if(ARITY(t) == 3) // as it will be when entered by user
{ g->parameter_interval = ARG(2,t);
}
break;
case POLAR:
case POLAR_CIRCULAR:
g = graphs[0];
if(FUNCTOR(t) == AND && ARITY(t) == 2) // as it will be when entered by user
{ g->parameter_interval = ARG(1,t);
t = ARG(0,t);
}
assert(ISATOM(ARG(0,t)));
g->rvariable = atom_string(ARG(0,t));
g->dependent_variable = ARG(0,t);
g->independent_variable = eigenvariable;
g->function = ARG(1,t);
g->xvariable = "x";
g->yvariable = "y";
/* You can't use x,y in polar graphs for any other
purpose, see input_check */
g->xfunction = product(ARG(1,t),cos1(eigenvariable));
g->yfunction = product(ARG(1,t),sin1(eigenvariable));
g->fprime = derivative(g->xfunction,eigenvariable);
g->gprime = derivative(g->yfunction,eigenvariable);
savet = VALUE(g->independent_variable);
SETVALUE(g->independent_variable,g->tselected);
deval(g->xfunction,&g->selectedx);
deval(g->yfunction,&g->selectedy);
SETVALUE(g->independent_variable,savet);
init_params(mainchoice,t,2);
break;
case CONTOUR:
graphs[0]->function = t;
graphs[0]->xvariable = atom_string(varlist[0]);
graphs[0]->yvariable = atom_string(varlist[1]);
graphs[0]->independent_variable = varlist[0];
graphs[0]->dependent_variable = varlist[1];
init_params(mainchoice,t,2);
break;
case MC_INEQ:
graphs[0]->independent_variable = eigenvariable;
saveit = get_nextassumption();
/* Input of the form f(x) < y < g(x) is legal, but y must not occur in
f(x) and g(x) */
if(interval_as_and(t) && ISATOM(ARG(1,ARG(0,t))))
{ graphs[0]->independent_variable = eigenvariable;
graphs[1]->independent_variable = eigenvariable;
/* independent_variable is needed to compute singularities */
graphs[0]->xfunction = ARG(0,ARG(0,t));
graphs[1]->yfunction = ARG(1,ARG(1,t));
graphs[0]->dependent_variable = ARG(1,ARG(0,t));
graphs[1]->dependent_variable = ARG(1,ARG(0,t));
/* graphs[0]->function is used to make the title */
graphs[0]->function = t;
graphs[1]->function = falseterm; // just so it's not garbage
set_nextassumption(-1);
graphs[0]->yvariable = atom_string(graphs[0]->dependent_variable);
set_nextassumption(-1);
graphs[0]->fprime = derivative(graphs[0]->xfunction,graphs[0]->independent_variable);
graphs[1]->fprime = derivative(graphs[1]->yfunction,graphs[0]->independent_variable);
set_nextassumption(saveit);
init_params(mainchoice,t,2);
set_nextassumption(saveit);
graphs[0]->xvariable = atom_string(eigenvariable);
graphs[1]->xvariable = atom_string(eigenvariable);
graphs[0]->yvariable = atom_string(graphs[0]->dependent_variable);
graphs[1]->yvariable = atom_string(graphs[0]->dependent_variable);
break;
}
/* Input of the form f(x) < g(x) is legal and is interpreted as
f(x) < y < g(x). But then y must not occur in f(x) and g(x)
2.4.25 This is no longer the case, input_check will reject such input,
but this code does no harm so I didn't remove it; maybe someday we will
want to allow such input.
*/
if(INEQUALITY(FUNCTOR(t)))
{ graphs[0]->function = t;
if(ISATOM(ARG(0,t)) && !equals(ARG(0,t),eigenvariable))
{ graphs[0]->dependent_variable = ARG(0,t);
graphs[0]->yvariable = atom_string(ARG(0,t));
if(FUNCTOR(t) == '<' || FUNCTOR(t) == LE)
{ graphs[0]->yfunction = ARG(1,t);
graphs[0]->xfunction = minusinfinity;
set_nextassumption(-1);
graphs[0]->gprime = derivative(graphs[0]->yfunction,graphs[0]->independent_variable);
graphs[0]->fprime = zero;
set_nextassumption(saveit);
}
else
{ graphs[0]->xfunction = ARG(1,t);
graphs[0]->yfunction = infinity;
set_nextassumption(-1);
graphs[0]->fprime = derivative(graphs[0]->xfunction,graphs[0]->independent_variable);
graphs[0]->gprime = zero;
set_nextassumption(saveit);
}
graphs[0]->xvariable = atom_string(eigenvariable);
graphs[0]->function = t;
init_params(mainchoice,t,2);
set_nextassumption(saveit);
break;
}
else if(ISATOM(ARG(1,t)) && !equals(ARG(1,t),eigenvariable))
{ graphs[0]->dependent_variable = ARG(1,t);
graphs[0]->yvariable = atom_string(ARG(1,t));
if(FUNCTOR(t) == '<' || FUNCTOR(t) == LE)
{ graphs[0]->yfunction = infinity;
graphs[0]->xfunction = ARG(0,t);
set_nextassumption(-1);
graphs[0]->gprime = zero;
graphs[0]->fprime = derivative(graphs[0]->xfunction,graphs[0]->independent_variable);
set_nextassumption(saveit);
}
else /* example, tan x > y */
{ graphs[0]->xfunction = minusinfinity;
graphs[0]->yfunction = ARG(0,t);
set_nextassumption(-1);
graphs[0]->gprime = derivative(graphs[0]->yfunction,graphs[0]->independent_variable);
graphs[0]->fprime = zero;
set_nextassumption(saveit);
}
graphs[0]->xvariable = atom_string(eigenvariable);
graphs[0]->function = t;
init_params(mainchoice,t,2);
set_nextassumption(saveit);
break;
}
}
/* for example -4 < 3x-2 < 4 is not acceptable */
assert(0);
break;
case MC_SET: /* fall through */
graphs[0]->xvariable = atom_string(varlist[0]);
graphs[0]->yvariable = atom_string(varlist[1]);
graphs[0]->independent_variable = varlist[0];
graphs[0]->dependent_variable = varlist[1];
graphs[0]->function = t;
init_params(mainchoice,t,2);
break;
case RELATION:
graphs[0]->xvariable = atom_string(varlist[0]);
graphs[0]->yvariable = atom_string(varlist[1]);
graphs[0]->independent_variable = varlist[0];
graphs[0]->dependent_variable = varlist[1];
graphs[0]->function = t;
/* We put it in as an equation so it can be used
for the title of the graph */
init_params(mainchoice,t,2);
break;
case COMPLEX_CONTOUR: /* fall-through */
case POLYROOT :
graphs[0]->independent_variable = eigenvariable;
/* graphs[0]->dependent_variable not used */
init_params(mainchoice,t,1);
graphs[0]->function = t;
temp = atom_string(varlist[0]);
graphs[0]->xvariable = callocate((int) strlen(temp) + 5,sizeof(char));
graphs[0]->yvariable = callocate((int) strlen(temp) + 5,sizeof(char));
strcpy(graphs[0]->xvariable,"Re ");
strcat(graphs[0]->xvariable,temp);
strcpy(graphs[0]->yvariable,"Im ");
strcat(graphs[0]->yvariable,temp);
if(mainchoice == POLYROOT)
{ term u;
if(nonconstant_exponent(t))
{ err = fix_exponents(t,&u);
assert(!err); /* input has been checked already */
}
else
u = t;
err = makepoly(u,eigenvariable,&graphs[0]->yfunction);
if(err)
{ err = polyform(u,eigenvariable,&graphs[0]->yfunction);
}
/* draw expects a POLY term at graphs[0]->yfunction */
assert(!err); /* input is supposed to have been checked already */
}
break;
case ODE:
/* t is y' = f(x,y) -- direction fields will work */
initialize_ode(t,graphs[0]);
break;
case ODE2:
assert(FUNCTOR(t)==AND);
/* t is y'=f(x,y) x' = g(x,y), where f,g don't contain t */
initialize_two_odes(t,1,graphs[0]);
break;
case ODESYSTEM:
assert(FUNCTOR(t) == AND);
initialize_two_odes(t,0,graphs[0]);
break;
case HDE:
initialize_high_order_ode(t,graphs[0]);
break;
case SPACECURVE:
graphs[0]->independent_variable = eigenvariable;
/* graphs[0]->dependent_variable not used */
graphs[0]->xvariable = atom_string(ARG(0,ARG(0,t)));
graphs[0]->yvariable = atom_string(ARG(0,ARG(1,t)));
graphs[0]->zvariable = atom_string(ARG(0,ARG(2,t)));
graphs[0]->xfunction = ARG(1,ARG(0,t));
graphs[0]->yfunction = ARG(1,ARG(1,t));
graphs[0]->zfunction = ARG(1,ARG(2,t));
graphs[0]->lamp1flag =100; /* lamp intensity in percent */
init_params(mainchoice,t,4);
break;
case NONPARAMETRICSURFACE:
{ term *varlist = get_varlist();
int nvariables = get_nvariables();
assert(nvariables >= 2); /* enhance_problem ensures it */
assert(FUNCTOR(t) == '=');
graphs[0]->lamp1flag = 100;
graphs[0]->xfunction = varlist[0];
graphs[0]->yfunction = varlist[1];
graphs[0]->xvariable = atom_string(varlist[0]);
graphs[0]->yvariable = atom_string(varlist[1]);
graphs[0]->zvariable = atom_string(ARG(0,t));
graphs[0]->zfunction = ARG(1,t);
graphs[0]->independent_variable = varlist[0];
graphs[0]->independent_variable2 = varlist[1];
}
init_params(mainchoice,t,3); /* x,y,z */
break;
case POLARNONPARAMETRICSURFACE:
{ term *varlist = get_varlist();
term r,theta;
int nvariables = get_nvariables();
assert(nvariables >= 2); /* enhance_problem ensures it */
assert(FUNCTOR(t) == '=');
r = varlist[0];
theta = varlist[1];
graphs[0]->lamp1flag = 100;
graphs[0]->xfunction = product(r, cos1(theta));
graphs[0]->yfunction = product(r, sin1(theta));
graphs[0]->zfunction = ARG(1,t);
graphs[0]->zvariable = atom_string(ARG(0,t));
graphs[0]->independent_variable = r;
graphs[0]->independent_variable2 = theta;
}
init_params(mainchoice,t,3); /* r,theta,z */
break;
case PARAMETRICSURFACE:
{ term *varlist = get_varlist();
int nvariables = get_nvariables();
assert(nvariables >= 2);
graphs[0]->independent_variable = varlist[0];
graphs[0]->independent_variable2 = varlist[1];
}
graphs[0]->lamp1flag = 100;
graphs[0]->xvariable = atom_string(ARG(0,ARG(0,t)));
graphs[0]->yvariable = atom_string(ARG(0,ARG(1,t)));
graphs[0]->zvariable = atom_string(ARG(0,ARG(2,t)));
graphs[0]->xfunction = ARG(1,ARG(0,t));
graphs[0]->yfunction = ARG(1,ARG(1,t));
graphs[0]->zfunction = ARG(1,ARG(2,t));
init_params(mainchoice,t,5); /* x,y,z,u,v */
break;
case TRAPEZOIDRULE: /* fall-through */
case SIMPSONSRULE:
/* we have to initialize graphs[0] -> left and ->right
and ->nintervals as well as ->function.
The term t should be an OR of the
function, the left and right endpoints,
the number of intervals. */
graphs[0]->xvariable = atom_string(eigenvariable);
assert((FUNCTOR(t) == OR || FUNCTOR(t) == AND) && ARITY(t)==4);
graphs[0]->function = graphs[1]->function = ARG(0,t);
err = deval(ARG(1,t),&graphs[0]->left);
if(err)
{ // happens if this graph came from an improper integral with -infinity as
// lower limit; otherwise parameters should have been initialized
graphs[0]->left = -100;
}
err = deval(ARG(2,t),&graphs[0]->right);
if(err)
{ // happens if this graph came from an improper integral with infinity as
// upper limit; otherwise parameters should have been initialized
graphs[0]->right = graphs[0]->left + 100;
}
graphs[0]->nintervals = ARG(3,t);
graphs[1]->xvariable = graphs[0]->xvariable;
graphs[0]->yvariable = FUNCTOR(eigenvariable) == 'y' ? "z" : "y";
graphs[1]->yvariable = graphs[0]->yvariable;
graphs[0]->independent_variable =
graphs[1]->independent_variable = eigenvariable;
graphs[0]->xfunction = ARG(1,t);
graphs[0]->yfunction = ARG(2,t); // that's where make_title looks for lo and hi
SETFUNCTOR(graphs[1]->dependent_variable,0,0);
/* make_title looks for this condition */
saveit = get_nextassumption();
set_nextassumption(-1); /* do not make any assumptions while
calculating the derivative */
graphs[1]->fprime = graphs[0]->fprime = derivative(graphs[1]->function,graphs[1]->independent_variable);
set_nextassumption(saveit);
/* When this is a legitimate approximate integration graph, 2 is the right number of
variables if there are no parameters in the formula being graphed, since one of the
variables is the independent variable x and the other is n, the number of divisions of x.
But if this is the Graph Button graph of a definite integral, than n has been set to some fixed
value and does not occur in the varlist. Hence 1 is the correct number of non-parameter variables.
Example: integral(1/(x-a,x,0,1). We want a to be initialized as a parameter. */
if(mainchoice==SIMPSONSRULE)
{ PGRAPHDATA pGraphData = GetActiveDoc();
if(pGraphData->creator) // pGraphData was created by the graph button
{ init_params(mainchoice,t,1); /* from the Graph Button on a definite integral */
if(get_nparameters() > 0 && !entire(ARG(0,t)))
/* if it's an entire function then the default parameters are OK */
set_parameters(t);
}
else
{ // problem entered by user or from Problem Library
init_params(mainchoice,t,2);
}
}
else
init_params(mainchoice,t,2);
break;
case RIEMANNSUMS:
/* graphs[0] will be a RIEMANN_SUMS, graphs[1] the integrand.
We have to initialize graphs[0] -> left and ->right
and ->nintervals as well as ->function, as for
SIMPSONSRULE, but we also have to initialize for left sums.
*/
graphs[0]->xvariable = atom_string(eigenvariable); // x, for the second graph
assert((FUNCTOR(t) == OR || FUNCTOR(t)==AND) && (ARITY(t)==5 || ARITY(t) == 4));
term summand, n, lo, hi, integrand;
if(FUNCTOR(t) == OR) // from integral_test
{
// the arguments are the indexed sum , lo, hi, the number of intervals, and the style of rectangle (left,right,center are 0,1,2)
summand = ARG(0,ARG(0,t));
n = ARG(1,ARG(0,t)); // the index variable
lo = ARG(1,t);
hi = ARG(2,t);
subst(get_eigenvariable(),n,summand,&integrand);
graphs[1]->function = integrand;
graphs[0]->independent_variable = n; // not m
graphs[1]->independent_variable = eigenvariable;
}
else
{ // FUNCTOR(t) == AND, problem is a Riemann sum,
// the args are the integrand, lo, hi, and the number of intervals
summand = ARG(0,t);
lo = ARG(1,t);
hi = ARG(2,t);
n = ARG(3,t);
graphs[1]->function = summand;
init_params(mainchoice,t,1); // the 1 says varlist[0] will not be a param
graphs[0]->independent_variable = eigenvariable; // not m
graphs[1]->independent_variable = eigenvariable;
}
graphs[0]->xfunction = lo;
graphs[0]->yfunction = hi;
graphs[0]->function = summand;
deval(ARG(1,t),&graphs[0]->left); // lo
deval(ARG(2,t),&graphs[0]->right); // hi
graphs[0]->right += 1; // so last rectangle begins at 'hi'
graphs[0]->nintervals = ARG(3,t);
graphs[1]->nintervals = ARG(3,t); // the other graph needs it too for initializing the range
if(ARITY(t)==4)
graphs[0]->riemannflag = 0; /* left sums by default */
else
{ assert(ISINTEGER(ARG(4,t)));
graphs[0]->riemannflag = (int) INTDATA(ARG(4,t));
}
graphs[1]->xvariable = graphs[0]->xvariable;
graphs[0]->yvariable = FUNCTOR(eigenvariable) == 'y' ? "z" : "y";
graphs[1]->yvariable = graphs[0]->yvariable;
graphs[1]->left = graphs[0]->left;
graphs[1]->right = graphs[0]->right;
SETFUNCTOR(graphs[1]->dependent_variable,0,0);
/* make_title looks for this condition */
saveit = get_nextassumption();
set_nextassumption(-1); /* do not make any assumptions while
calculating the derivative */
graphs[1]->fprime = graphs[0]->fprime = derivative(graphs[1]->function,graphs[1]->independent_variable);
set_nextassumption(saveit);
break;
}
for(i=0;i<ngraphs;i++)
{ create_graph(mainchoice,graphs[i],i,ngraphs);
if(graphs[i]->labels != 1)
flag = 1;
if(mainchoice == COMPARE_SAME && i > 0)
{ graphs[i] ->titlebackgroundcolor = graphs[0]->titlebackgroundcolor;
graphs[i] ->grpaper.ncolors = 0; // so AdjustGraphRanges will be called.
}
// for SIMPSONSRULE graphs arising from improper integrals, we need to
// increase graphs[i]->right (if infinity is the upper limit). That
// can't be done in create_graph because infinity isn't anywhere in graphs[i]
// so it has to be done here.
if(mainchoice==SIMPSONSRULE && ARITY(t) == 4 && equals(ARG(2,t),infinity))
{ double width = graphs[i]->xmax-graphs[i]->xmin;
graphs[i]->right += width;
}
if(mainchoice==SIMPSONSRULE && ARITY(t) == 4 && equals(ARG(1,t),minusinfinity))
{ double width = graphs[i]->xmax-graphs[i]->xmin;
graphs[i]->left -= width;
}
make_title(graphs[i],&graphs[i]->title);
// txmax and txmin are normalized device coordinates
double width = graphs[i]->pxmax - graphs[i]->pxmin;
double height = graphs[i]->pymax - graphs[i]->pymin;
double pixeltitlewidth = WIDTH(graphs[i]->title)+10;
// The 10 is for hard-coded padding of 5 on all edges.
// Client file must provide padding:5 in the title's container.
double pixeltitleheight = HEIGHT(graphs[i]->title)+10;
double devicewidth = graphs[i]->dxmax-graphs[i]->dxmin;
double deviceheight = graphs[i]->dymax-graphs[i]->dymin;
double devicetitlewidth = (devicewidth/width)*pixeltitlewidth;
double devicetitleheight = (deviceheight/height)*pixeltitleheight;
// Here is where to set txmax and tymin
graphs[i]->txmax = graphs[i]->txmin + devicetitlewidth;
graphs[i]->tymin = graphs[i]->tymax - devicetitleheight;
}
if(mainchoice == COMPARE_SAME || mainchoice == COMPARE_DIFFERENT ||
mainchoice == RIEMANNSUMS || mainchoice == SIMPSONSRULE || mainchoice == TRAPEZOIDRULE
)
{ /* if one of the graphs has ticks at multiples of pi they all should */
int piflag = 1;
for(i=0;i<ngraphs;i++)
{ if(graphs[i]->ticks == 2)
piflag = 2;
}
if(piflag == 2)
{ for(i=0;i<ngraphs;i++)
graphs[i]->ticks = 2;
}
}
if(
mainchoice == RIEMANNSUMS ||
mainchoice == SIMPSONSRULE ||
mainchoice == TRAPEZOIDRULE
)
{
graphs[1]->axeslabelcolor = graphs[0]->axeslabelcolor;
graphs[1] ->titlebackgroundcolor = graphs[0]->titlebackgroundcolor;
graphs[1] ->grpaper.ncolors = 0; // so AdjustGraphRanges will be called.
}
/* If any of the graphs has had labels set to 2
(symbolic labels) because it's using ticks at multiples
of pi_term, make sure ALL of the graphs use no labels,
because they can't be changed individually.
And also, in that case default_range may have set
the x-ranges differently! So fix them up. */
if(flag && ngraphs > 1)
{ graphs[0]->labels = 2;
for(i=1;i<ngraphs;i++)
{ graphs[i]->labels = 2;
graphs[i]->xmin = graphs[0]->xmin;
graphs[i]->xmax = graphs[0]->xmax;
graphs[i]->ymin = graphs[0]->ymin;
graphs[i]->ymax = graphs[0]->ymax;
}
}
calculate_singularities(ngraphs,mainchoice,t,graphs);
}
/*__________________________________________________________*/
void calculate_singularities(int ngraphs, int mainchoice, term t, graph**graphs)
/* Automatic calculation of singularities */
{ int i;
switch(mainchoice)
{ case MC_INEQ:
if(interval_as_and(t))
{ fill_in_singularities(ARG(0,ARG(0,t)),graphs[0]);
fill_in_singularities(ARG(1,ARG(1,t)),graphs[1]);
}
else if(INEQUALITY(FUNCTOR(t)))
{ if(ISATOM(ARG(0,t)))
fill_in_singularities(ARG(1,t),graphs[0]);
else if(ISATOM(ARG(1,t)))
fill_in_singularities(ARG(0,t),graphs[0]);
else
assert(0);
}
break;
case ORDINARY:
fill_in_singularities(ARG(1,t),graphs[0]);
break;
case COMPAREDERIV: /* fall-through */
case COMPAREDERIVS: /* fall-through */
case COMPARE_DIFFERENT:
for(i=1;i<ngraphs;i++)
{
graphs[i]->newaxes = 1;
}
for(i=0;i<ngraphs;i++)
fill_in_singularities(ARG(1,ARG(i,t)),graphs[i]);
break;
case COMPARE_SAME:
for(i=0;i<ngraphs;i++)
fill_in_singularities(ARG(1,ARG(i,t)),graphs[i]);
for(i=1;i<ngraphs;i++)
{
graphs[i]->newaxes = 0;
}
break;
case RIEMANNSUMS:
case TRAPEZOIDRULE:
case SIMPSONSRULE:
for(i=1;i<ngraphs;i++)
{
graphs[i]->newaxes = 0;
}
fill_in_singularities(graphs[1]->function,graphs[1]);
break; /* needed for improper integrals */
}
}
/*____________________________________________________________________*/
static void create_graph(int kind, graph *g, int whichgraph, int ngraphs)
/* Assumes that g points to a valid graph structure, however
it contains garbage data. This function creates a graph structure
as specified by 'kind' (the value of mainchoice to use).
Assumes that the following fields of *g (or the doc data) have been
initialized already:
mainchoice, ngraphs, (done by setupdata above)
problemtype, (done by problem_type in topics.c)
varlist, parameters, nvariables, nparameters (by get_problem)
Also assumes that
g->independent_variable, g->dependent_variable
have been properly initialized already, and in case of COMPARE_SAME,
RIEMANNSUMS, SIMPSONSRULE, also g->whichgraph.
Does NOT set the fields
function, xfunction, yfunction, xvariable, yvariable,
title, title2, titleoffset
but (for non-differential equation types) does set all other fields.
For ODE,HDE,ODE2,ODESYSTEM:
does not fill in the dynamic arrays g->slist,
of right-hand-sides, or g->elist, of dependent variable names,
or g->initial_values
It is sometimes called before 'set_device', in which case
set_pixel_fields won't work, and will have to be called again later.
'whichgraph' is used in case of COMPARE_DIFFERENT to set
the y-coordinates of the graph viewport, and in COMPARE_SAME to set
title window coordinates. This graph will become graphs[whichgraph].
*/
{ /* g->graphtype must be recognized by 'draw'. The possible values are
POLAR,POLAR_CIRCULAR,PARAMETRIC,ORDINARY,ODE,RELATION,POLYROOT,LDE,
ODESYSTEM, ODE2 */
int problemtype = get_problemtype();
int currenttopic = get_currenttopic();
int piflag;
int graphpaper;
unsigned init_colors[NGRAPHINITCOLORS];
unsigned init_colors2[NGRAPHINITCOLORS];
/* Set various fields to valid values so they don't wind up
containing garbage. Garbage causes crashes when saving documents.
Integer fields are zero by default since space for the graph
structure is initialized to zero. */
g->adjustToPaper = 1;
if(kind != PARAMETRIC)
g->rvariable = "r";
if(kind != RIEMANNSUMS && kind != SIMPSONSRULE && kind != TRAPEZOIDRULE)
g->nintervals = zero; /* but it may have already been set on
those problem types, so don't wipe it out. */
if(kind == RIEMANNSUMS || kind == SIMPSONSRULE || kind == TRAPEZOIDRULE)
{ graphpaper = (whichgraph == 0) ? 1 : 0;
// this tells the next line that we want graph paper on graphs[0] but not on graphs[1]
get_init_graphcolors(whichgraph ? 0 : 1, init_colors,&g->linewidth, &graphpaper);
if(graphpaper == 0)
{ g->grpaper.ncolors = 0;
g->titlebackgroundcolor = init_colors[7];
}
else
{ g->grpaper = GetGraphPaper(graphpaper);
g->titlebackgroundcolor = g->grpaper.titlebackgroundcolor;
}
g->linewidth = whichgraph ? THICK : THIN;
/* thin lines for the rectangles */
if (whichgraph == 1)
g->titlecolor = g->graphcolor;
}
else if(kind == COMPARE_SAME)
{ graphpaper = g->whichgraph ? 0: 1; // should we draw graphpaper
get_init_graphcolors(0,init_colors,&g->linewidth,&graphpaper);
get_init_graphcolors(whichgraph,init_colors2,&g->linewidth,&graphpaper);
g->graphcolor = g->titlecolor = init_colors[0];
g->titlebackgroundcolor = init_colors[7];
if(g->grpaper.index)
g->titlebackgroundcolor = g->grpaper.titlebackgroundcolor;
else
g->titlebackgroundcolor = init_colors2[7];
if(whichgraph)
{ g->graphcolor = g->titlecolor = init_colors2[0];
}
if(graphpaper)
{ g->grpaper = GetGraphPaper(graphpaper);
}
else
g->grpaper.ncolors = 0;
}
else
{ graphpaper = 1; // we want graphpaper
get_init_graphcolors(whichgraph,init_colors,&g->linewidth,&graphpaper);
g->grpaper = GetGraphPaper(graphpaper);
g->titlebackgroundcolor = g->grpaper.titlebackgroundcolor;
}
if(kind==COMPARE_SAME ||
kind == COMPARE_DIFFERENT ||
kind == COMPAREDERIV ||
kind == COMPAREDERIVS
)
g->graphtype = ORDINARY;
else if(whichgraph == 1 && (kind == RIEMANNSUMS || kind == SIMPSONSRULE || kind == TRAPEZOIDRULE))
g->graphtype = ORDINARY;
else
g->graphtype = kind;
g->numberofpoints= 601; /* how many points to plot */
if(kind == PARAMETRICSURFACE ||
kind == NONPARAMETRICSURFACE ||
kind == POLARNONPARAMETRICSURFACE
)
g->graphcolor = WHITE24;
else if(kind != COMPARE_SAME) // for that kind it is already set
g->graphcolor = init_colors[0];
g->background = init_colors[1];
g->border = init_colors[6]; /* border color, if same as background then no border */
g->fillcolor = init_colors[2];
g->axescolor = init_colors[3];
g->axeslabelcolor = init_colors[4];
if(kind == COMPARE_DIFFERENT || kind == MC_INEQ || kind == COMPARE_SAME ||
(whichgraph == 1 && (kind == RIEMANNSUMS || kind == TRAPEZOIDRULE || kind == SIMPSONSRULE))\
)
g->titlecolor = g->graphcolor; /* make it match the graph line color */
else
g->titlecolor = init_colors[5];
if(whichgraph > 0 && g->graphtype == MC_INEQ)
g->showtitles = 0;
else
g->showtitles = 1;
g->ticks = 1;
g->labels = 2; /* symbolic labels by default */
g->whichgraph=whichgraph;
/* already set for ordinary graphs in order that fprime could be set */
if(kind != ODE && kind != HDE &&
kind != ODESYSTEM && kind != ODE2
)
{ g->dimelist = 0; /* by default */
g->dimslist = 0;
g->dimsrestrictions = 0;
g->nsingularities = 0;
g->tempsing = 0;
}
if(kind == CONTOUR || kind == COMPLEX_CONTOUR)
g->tmin = 0.5; /* default spacing of contours */
else if(g->tmin == g->tmax)
/* ranges not already set from the Parametric Range dialog or the problem itself */
{ g->tmin = 0.0;
g->tmax = 2*PI_DECIMAL;
}
if(fabs(g->xmin- g->xmax) > 1.0e-100)
/* don't just test for inequality, or we crash with g->xmin a "denormalized number" */
{ /* ranges already set; but still must set g->ticks and g->labels */
term t = g->function;
if(!contains(t,PI_ATOM) &&
(contains(t,SIN) || contains(t,COS) || contains(t,TAN) ||
contains(t,COT) || contains(t,SEC) || contains(t,CSC)
)
)
{ g->ticks = 2; /* put ticks at multiples of pi */
g->labels = 2; /* symbolic axis labels */
}
}
else /* ranges not set yet */
{ piflag = default_range(g->function,g->graphtype,currenttopic,&g->xmin,&g->xmax,&g->ymin,&g->ymax,g);
// 12.1.14, changed kind to g->graphtype, e.g. for graphing definite integrals, g->graphtype can be ORDINARY while kind is SIMPSONSRULE
if(piflag)
{ g->ticks = 2; /* put ticks at multiples of pi */
g->labels = 2; /* symbolic axis labels */
}
}
if(fabs(g->ymin- g->ymax) <= 1.0e-100)
{ /* This can happen if the problem or the user
specifies only the x-ranges, so the y-ranges
are not set yet. */
g->ymin = -2.0;
g->ymax = 2.0;
}
if(kind != ODE && kind != ODE2 &&
kind != HDE && kind != ODESYSTEM
)
g->erase = 1;
else
g->erase = 0; /* zero value says not to erase old
graph before drawing next one */
g->crosshairsflag = 0;
g->rectangle_flag = 0;
if(kind == ODE )
{ g->dfield = 1;
g->dfield_applicable = 1;
}
// on ODE2 it will be set in fill_graph_structures
if(kind == ODE || kind == ODE2)
{ g->linewidth = 2; // medium thickness looks better with direction fields
}
/* Next set the crosshair cursor location for this graph */
if(SYMBOLTOPIC(currenttopic) && FUNCTOR(history(0)) == LIMIT)
/* position crosshairs at the limit point */
{ double z;
term temp;
deval(ARG(1,ARG(0,history(0))),&z);
assert(z != BADVAL); /* or graphlimit should have rejected it */
g->selectedx = z;
subst(make_double(z),ARG(0,ARG(0,history(0))),LIMITAND(history(0)),&temp);
deval(temp,&z);
if(z == BADVAL) /* an infinite or undefined limit */
g->selectedy = 0.5*(g->ymax + g->ymin);
else
g->selectedy = z;
}
else if(problemtype == MINMAX)
{ g->selectedx = 0.55 *(g->xmax + g->xmin);
g->selectedy = 0.55 *(g->ymax + g->ymin);
/* a little off-center to avoid hitting the origin */
}
else /* the default location for non-limit, non-minmax problems */
{ g->selectedx = 0.1;
g->selectedy = 0.1; /* a little off-center */
}
if(whichgraph == 0 ||
(kind != COMPARE_SAME && kind != RIEMANNSUMS && kind != SIMPSONSRULE && kind != TRAPEZOIDRULE)
)
g->newaxes=1; /* nonzero indicates that the next time we
draw this graph we need to redraw the
axes etc.; otherwise only the graph line
itself is redrawn */
/* second graph on same axes should always
have this field set to zero */
else
g->newaxes = 0;
g->dxmin = 0.0;
g->dxmax = 1.0;
if((kind == COMPARE_DIFFERENT && ngraphs <= 3)
|| kind == COMPAREDERIV || (kind == COMPAREDERIVS && ngraphs <= 4)
)
{ double height = 1.0 - ((double) ngraphs-1.0)* 0.002;
height /= ngraphs; // height of one graph in normalized device coordinates
g->dymax = 1.0 - whichgraph*height + (whichgraph-1) * 0.002;
g->dymin = g->dymax - height;
}
else if (kind == COMPARE_DIFFERENT && ngraphs == 4)
{ /* arrange them two-by-two */
if(whichgraph % 2 == 0)
{ g->dxmin = 0.0;
g->dxmax = 0.49;
}
else
{ g->dxmin = 0.51;
g->dxmax = 1.0;
}
if(whichgraph < 2)
{ g->dymin = 0.51;
g->dymax = 1.0;
}
else
{ g->dymin = 0.0;
g->dymax = 0.49;
}
}
else if (kind == COMPARE_DIFFERENT || kind == COMPAREDERIVS) /* 5 or 6 graphs */
{ /* arrange them two-by-three */
if(whichgraph % 3 == 0)
{ g->dxmin = 0.0;
g->dxmax = 0.32;
}
else if (whichgraph %3 == 1)
{ g->dxmin = 0.34;
g->dxmax = 0.66;
}
else
{ g->dxmin = 0.68;
g->dxmax = 1.0;
}
if(whichgraph < 3)
{ g->dymin = 0.51;
g->dymax = 1.0;
}
else
{ g->dymin = 0.0;
g->dymax = 0.49;
}
}
else
{ g->dymin = 0.0;
g->dymax = 1.0;
}
pixel_fields(g); /* set g->pxmin etc., provided set_device has
already been called. */
if(needs_circular_aspect(kind,currenttopic))
// Now we can get the aspect ratio and adjust g->ymin and g->ymax.
{
double aspect = (g->pymax-g->pymin)/(double)(g->pxmax - g->pxmin);
g->ymax *= aspect;
g->ymin *= aspect;
g->circular = 1;
}
else
{
g->circular = 0;
}
/* Now set the title viewport, in normalized device coordinates
(which have lower left = (0,0), both directions size 1.0) */
if(kind != COMPARE_SAME && kind != MC_INEQ &&
kind != RIEMANNSUMS && kind != SIMPSONSRULE && kind != TRAPEZOIDRULE
)
{ g->txmin = g->dxmin + 0.7*(g->dxmax - g->dxmin);
g->tymax = g->dymin + 0.7 *(g->dymax - g->dymin);
g->txmax = g->dxmax; /* these will be reset anyway */
g->tymin = g->dymin;
}
else
/* comparing graphs on the same axes, try to make the titles
not overlap. Of course if they are too big, they WILL overlap. */
{ double left;
if(ngraphs <= 3 ||
(ngraphs == 4 && whichgraph < 2) ||
(ngraphs != 4 && whichgraph < 3)
)
left = 0.6;
else
left = 0.1;
g->txmin = g->dxmin + left*(g->dxmax - g->dxmin);
g->tymax = g->dymax - 0.20 - 0.33 *(whichgraph % (ngraphs==4 ? 2 : 3)) *(g->dymax - g->dymin);
g->txmax = g->dxmax; /* these will be reset anyway */
g->tymin = g->dymin;
}
if(kind==POLYROOT)
{ /* g->linewidth will be the horizontal world-coordinate of the
radius of the filled circle used to indicate the roots. */
g->linewidth = 5.0; /* default value; in type POLYROOT, for
screen devices, g->linewidth is how many pixels in
radius we want the spots that show the roots. */
}
/* Set the default camera location for 3d graphs */
g->camera[0] = g->camera[1] = g->camera[2] = 10.0;
/* and the default lamp locations */
g->lamp1[0] = 20.0;
g->lamp1[1] = -20.0;
g->lamp1[2] = -20.0;
g->lamp2[0] = -20.0;
g->lamp2[1] = -20.0;
g->lamp2[2] = -20.0;
if(kind == SPACECURVE)
g->rendermode = 1; /* wire frame */
/* rendermode for 3d surface graphs is set in the GetProblem dialog */
}
/*________________________________________________________________*/
static void initialize_ode(term t, graph *g)
/* initialize g for graphing a single ode */
{ int i;
int nvariables = get_nvariables();
int currentline = get_currentline();
term a,b,x,y;
char buf[2];
term *varlist = get_varlist();
varinf *varinfo = get_varinfo();
g->function = t; /* used to make the title, e.g. y' = y */
g->xvariable = atom_string(varlist[0]);
x = g->independent_variable = varlist[0];
g->yvariable = atom_string(ARG(0,ARG(0,t)));
/* works whether it's DIFF(y,x) or PR(y,1) */
y = g->dependent_variable = ARG(0,ARG(0,t));
g->slist = (term *) callocate(2,sizeof(term));
if(g->slist==NULL)
nospace();
g->slist[0] = one;
g->slist[1] = ARG(1,t);
g->dimslist = 2;
g->elist = (term *) callocate(2,sizeof(term));
if(g->elist == NULL)
nospace();
g->elist[0] = varlist[0];
g->elist[1] = ARG(0,ARG(0,t));
g->dimelist = 2;
g->dfield_applicable = 1;
init_params(ODE,t,2);
gensym(0); /* reset the subscript counter */
buf[0] = (char) FUNCTOR(x);
buf[1] = '\0';
a = getnewvar(x,buf); /* This will come out x0 */
gensym(0); /* reset the subscript counter */
buf[0] = (char) FUNCTOR(y);
b = getnewvar(y,buf); /* for y0 */
initialize_parameter(nvariables,currentline,1.0);
initialize_parameter(nvariables+1,currentline,1.0);
SETVALUE(a,1.0);
SETVALUE(b,1.0); /* default initial values */
g->initial_values = (double **) callocate(2,sizeof(double *));
if(g->initial_values == NULL)
nospace();
for(i=0;i<2;i++)
g->initial_values[i] = &varinfo[nvariables+i].realpart;
}
/*_________________________________________________________________*/
static void initialize_two_odes(term p, int dfield, graph *g)
/* p is and(t,s), where t is x'=f, s is y'=g; diff(x,t) can be used instead of x' */
/* dfield is 1 if f and g do not contain the independent variable,
0 otherwise */
{ term left1,left2,a,b,t,s,x,y;
int i;
char buf[2];
int nvariables = get_nvariables();
term *varlist = get_varlist();
varinf *varinfo = get_varinfo();
assert(FUNCTOR(p) == AND);
t = ARG(0,p);
s = ARG(1,p);
assert(FUNCTOR(t) == '=' && FUNCTOR(s) == '=');
left1 = ARG(0,t);
left2 = ARG(0,s);
assert(FUNCTOR(left1) == DIFF || FUNCTOR(left1)== PR);
x = ARG(0,left1);
y = ARG(0,left2);
g->function = p; /* used to make the title */
g->xvariable = atom_string(ARG(0,ARG(0,t)));
/* works whether it's DIFF(y,x) or PR(y,1) */
g->independent_variable = varlist[0];
#if 0 // we don't make a parameter for t0
gensym(0); /* reset the subscript counter */
buf[0] = (char) FUNCTOR(time);
buf[1] = '\0';
t0 = getnewvar(x,buf); /* This will come out t0 */
#endif
g->yvariable = atom_string(ARG(0,ARG(0,s)));
/* works whether it's DIFF(y,x) or PR(y,1) */
/* g->dependent_variable not used */
g->elist = (term *) callocate(2,sizeof(term));
if(g->elist == NULL)
nospace();
g->elist[0] = ARG(0,left1);
g->elist[1] = ARG(0,left2);
g->dimelist = 2;
g->slist = (term *) callocate(2,sizeof(term));
if(g->slist == NULL)
nospace();
g->slist[0] = ARG(1,t);
g->slist[1] = ARG(1,s);
g->dimslist = 2;
g->dfield_applicable = dfield;
gensym(0); /* reset the subscript counter */
buf[0] = (char) FUNCTOR(x);
buf[1] = '\0';
a = getnewvar(x,buf); /* This will come out x0 */
gensym(0); /* reset the subscript counter */
buf[0] = (char) FUNCTOR(y);
b = getnewvar(y,buf); /* for y0 */
SETVALUE(a,1.0);
SETVALUE(b,1.0); /* default initial values */
init_params(ODE2,p,3); /* all parameters, if any, besides the initial values. The 3 says that t,x,y are not parameters. */
// later there will be variables for x0, y0, but not yet.
g->initial_values = (double **) callocate(3,sizeof(double *));
if(g->initial_values == NULL)
nospace();
for(i=0;i<3;i++)
g->initial_values[i] = &varinfo[nvariables+i+1].realpart;
// but the variables themselves don't exist yet!
if (dfield)
g->dfield = 1; // this makes it come on by default
}
/*___________________________________________________________________*/
static void initialize_high_order_ode(term t, graph *g)
/* t has the form y''''... = f(x,y,y',....); input_check has already
checked that it is admissible. */
/* introduce new variables yk for k=1,...,n-1 where n is the order
of the equation. (so yk stands for k-th deriv of y.)
Convert the equation to the system of n+1 equations
x' = 1;
y' = y1;
y1' = y2;
... y(n-1)' = f
*/
{ term left,right;
term temp,temp2,foo,y,x;
int n,i;
char buf[2];
term a,b;
int nvariables = get_nvariables();
term *varlist = get_varlist();
varinf *varinfo = get_varinfo();
int currentline = get_currentline();
assert(FUNCTOR(t) == '=');
left = ARG(0,t);
y = ARG(0,left); /* the dependent variable */
right = ARG(1,t);
n = (int) INTDATA(ARG(1,left)); /* whether it's PR(u,n) or DIFF(u,n,x) */
g->function = t; /* used to make title */
g->xvariable = atom_string(varlist[0]);
x = g->independent_variable = varlist[0];
g->yvariable = atom_string(ARG(0,left));
/* works whether it's DIFF(y,x) or PR(y,1) */
g->dependent_variable = ARG(0,left);
g->slist = (term *) callocate(n+1,sizeof(term));
if(g->slist==NULL)
nospace();
g->elist = (term *) callocate(n+1,sizeof(term));
if(g->elist == NULL)
nospace();
g->dimelist = n+1;
g->dimslist = n+1;
g->elist[0] = varlist[0]; /* left-hand sides of first two equations */
g->elist[1] = ARG(0,left);
g->slist[0] = one; /* right side of x' = 1 */
for(i=1;i<n;i++)
{ g->slist[i] = /* yi on the right of equation i */
g->elist[i+1] = getvar(t,"y"); /* and the left of equation i+1 */
}
/* now the right side of equation number n (the last one) is still not done */
temp = right;
assert(n<=31);
for(i=1;i<n;i++)
{ if(contains(temp,DIFF))
subst(g->elist[i],diff(y,varlist[0]),temp,&temp2);
else
temp2 = temp;
if(contains(temp2,PR))
{ foo = i==1 ? one : i==2 ? two : i==3 ? three : i==4 ? four : i== 5? five : make_int(i);
subst(g->elist[i+1],pr(y,foo),temp2,&temp);
}
else
temp= temp2;
}
g->slist[n] = temp;
nvariables += n-1; /* for the n-1 variables we created */
assert(nvariables == get_nvariables());
init_params(HDE,t,2); // before creating new parameters for the initial values
/* Now for the initial values */
g->initial_values = (double **) callocate(n+1,sizeof(double *));
if(g->initial_values == NULL)
nospace();
gensym(0); /* reset the subscript counter */
buf[0] = (char) FUNCTOR(x);
buf[1] = '\0';
a = getnewvar(x,buf); /* This will come out x0 */
gensym(0); /* reset the subscript counter */
buf[0] = (char) FUNCTOR(y);
b = getnewvar(y,buf); /* for y0 */
initialize_parameter(nvariables,currentline,1.0);
++nvariables;
g->initial_values[0] = &varinfo[nvariables-1].realpart;
initialize_parameter(nvariables,currentline, 1.0);
++nvariables;
g->initial_values[1] = &varinfo[nvariables-1].realpart;
SETVALUE(a,0.0);
SETVALUE(b,1.0);
/* Now for parameters for the initial values of y', y'', and so on. These just
get letters a,b,c,d. Five is the maximum order so we won't
go beyond d, unless there are already parameters in the equation. */
for(i=1;i<n;i++)
{ a = getnewvar(t,"abcdpqrs");
initialize_parameter(-nvariables,currentline,1.0); // pass first argument negative to tell it to create a print-name with a prime
SETVALUE(a,1.0); /* default initial value */
g->initial_values[i+1] = &varinfo[nvariables].realpart;
++nvariables;
}
}
/*_____________________________________________________*/
static void fill_in(term t, term **ans, term **restrictions, int *dim1, int *dim2)
/* t is the symbolic formula for the jumps or singularities.
It can be a single expression or an AND. If it is an AND,
it can include inequalities. At exit, *ans and *restrictions are
arrays holding the non-inequalities and inequalities respectively,
of dimensions dim1 and dim2, unless dim1 or dim2 is zero. Those
arrays are allocated by this function. */
{ unsigned short i,n,h;
term u;
int j,k;
*dim1 = *dim2 = 0;
if(FUNCTOR(t) != AND) /* only one formula */
{ *dim1 = 1;
*dim2 = 0;
*ans = (term *) mallocate(sizeof(term));
if(*ans == NULL)
nospace();
(*ans)[0] = t;
return;
}
n = ARITY(t);
/* compute the required array dimensions */
for(i=0;i<n;i++)
{ u = ARG(i,t);
h = FUNCTOR(u);
if(h=='<' || h=='>' || h==LE || h==GE || h==NE || interval_as_and(u))
++*dim2;
else
++*dim1;
}
if(*dim1)
{ *ans = (term *) callocate(*dim1, sizeof(term));
if(*ans == NULL)
nospace();
}
if(*dim2)
{ *restrictions = (term *) callocate(*dim2, sizeof(term));
if(*restrictions == NULL)
nospace();
}
j=k=0;
for(i=0;i<n;i++)
{ u = ARG(i,t);
h = FUNCTOR(u);
if(h=='<' || h=='>' || h==LE || h==GE || h==NE || interval_as_and(u))
{ (*restrictions)[j] = u;
++j;
}
else
{ (*ans)[k] = u;
++ k;
}
}
assert(k==*dim1 && j==*dim2);
}
/*_____________________________________________________*/
static void fill_in_singularities(term t, graph *g)
/* calculate the singularities of t and enter them in g->slist,
entering the inequalities in g->srestrictions */
/* If the singularities can't be calculated, enter 'undefined'
in g->slist and set g->dimslist = 1 */
/* Ensure that the values of the variables are restored before
exiting; singularity calculation can change them.
*/
{ term temp,temp2,u,v,p,q,originalt;
int i,err;
term x = g->independent_variable;
term *varlist = get_varlist();
int savegcdflag,savecomdenomflag,savelogflag,savenegexpflag,
savefractexpflag,savetrigsqflag,nvars;
double *values;
void *savenode;
term *atomlist;
int nvariables = get_nvariables();
if(nvariables == 0)
return;
originalt = t;
values = (double *) callocate(nvariables, sizeof(double));
if(!values)
{ nospace();
return;
}
for(i=0;i<nvariables;i++)
values[i] = VALUE(varlist[i]); /* to be restored at the end */
if(!equals(get_eigenvariable(),x))
{ // assert(0);
/* x should be the eigenvariable, but we ensure that here anyway. */
for(i=0;i<nvariables;i++)
{ if(equals(varlist[i],x))
{ set_eigenvariable(i);
break;
}
}
}
if(mvpoly2(t) || algpoly(t) || entire(t))
{ temp = falseterm;
temp2 = falseterm;
err = 0;
}
else if(trigrational(t))
{ temp = trigsing(t);
temp2 = falseterm;
err = 0;
}
else
{ savegcdflag = get_polyvalgcdflag();
savelogflag = get_polyvallogflag();
savetrigsqflag = get_polyvaltrigsqflag();
savenegexpflag = get_polyvalnegexpflag();
savefractexpflag = get_polyvalfractexpflag();
savecomdenomflag = get_polyvalcomdenomflag();
set_comdenomflag(0); /* first try without common denoms
completely turned on. However, you must use them
in sums in factors, because otherwise the sum can
have a singularity which is cancelled by one of the
other factors, and singularities doesn't check for that. */
set_polyvalsumflag(1); /* this makes polyval use comdenoms
on sums in products with nonconstant factors. */
set_polyvalgcdflag(1);
set_polyvallogflag(1);
set_polyvaltrigsqflag(1);
set_polyvalfractexpflag(0);
set_polyvalnegexpflag(-1);
savenode = heapmax();
polyval(t,&u);
save_and_reset(u,savenode,&u);
err = singularities(u,&temp,&temp2);
set_polyvallogflag(savelogflag);
set_polyvaltrigsqflag(savetrigsqflag);
set_polyvalcomdenomflag(savecomdenomflag);
set_polyvalgcdflag(savegcdflag);
set_polyvalfractexpflag(savefractexpflag);
set_polyvalnegexpflag(savenegexpflag);
set_polyvalsumflag(0);
if(err)
{ /* bring out the big guns */
/* try it with comdenoms and factorflag2 on */
save_and_reset(u,savenode,&u);
ratsimp(u,&v);
if(equals(u,v))
err = 1;
else
err = singularities(v,&temp,&temp2);
if(!err)
{ p = and(temp,temp2);
save_and_reset(p,savenode,&q);
temp = ARG(0,q);
temp2 = ARG(1,q);
RELEASE(q);
/* ratsimp can enlarge the domain, as in log(x-4)- log(3x-12),
where the original domain is x > 4 but after ratsimp
is true. Officially it's not an error if we return a
singularity that is outside the domain, but let's catch
what we can without actually computing the domain. */
t = originalt;
if(contains_log(t) || contains(t,ROOT) || contains(t,SQRT))
temp = domain_filter(temp,x,t);
}
else
reset_heap(savenode);
}
}
if(err)
{ nvars = variablesin(originalt,&atomlist);
if(nvars && contains(originalt,FUNCTOR(x)))
{ /* start over again from t, using automode's simplification
algorithm first */
int flag;
int saveproblemtype = get_problemtype();
int nsteps;
int nextassumption = get_nextassumption();
set_problemtype(SIMPLIFY);
nsteps = autoans(originalt,25,&u,&flag);
if(nextassumption != get_nextassumption())
err = 1; /* assert(0); */
set_problemtype(saveproblemtype);
if(!nsteps)
err = 1;
else
err = singularities(u,&temp,&temp2);
if(!err)
{ p = and(temp,temp2);
save_and_reset(p,savenode,&q);
temp = ARG(0,q);
temp2 = ARG(1,q);
RELEASE(q);
}
}
}
if(err)
{ g->dimslist = 1;
g->dimjumplist = 1;
g->slist = (term *) mallocate(sizeof(term));
if(g->slist == NULL)
{ nospace();
goto out;
}
g->slist[0] = undefined;
g->jumplist = (term *) mallocate(sizeof(term));
if(g->jumplist == NULL)
{ nospace();
goto out;
}
g->jumplist[0] = undefined;
goto out;
}
erasecolors(&temp);
erasecolors(&temp2);
if(equals(temp,falseterm) && equals(temp2,falseterm))
/* no singularities or jumps */
{ g->dimslist = 0;
g->dimjumplist = 0;
g->njumps = 0; /* dimension of array of numerical jumps */
goto out;
}
if(equals(temp,falseterm)) /* jumps but no singularities */
{ fill_in(temp2,&g->jumplist,&g->jumprestrictions,
&g->dimjumplist,&g->dimjumprestrictions
);
g->dimslist = 0;
goto out;
}
if(equals(temp2,falseterm)) /* singularities but no jumps*/
{ fill_in(temp,&g->slist,&g->srestrictions,
&g->dimslist,&g->dimsrestrictions
);
g->dimjumplist = 0;
g->njumps = 0;
goto out;
}
fill_in(temp,&g->slist,&g->srestrictions,&g->dimslist,&g->dimsrestrictions);
fill_in(temp2,&g->jumplist,&g->jumprestrictions,
&g->dimjumplist,&g->dimjumprestrictions
);
out:
for(i=0;i<nvariables;i++)
SETVALUE(varlist[i],values[i]);
free2(values);
}
/*_____________________________________________________________*/
static void init_params(int mainchoice, term t, int k)
/* initialize all non-bound variables contained in t (existential or not)
as parameters, except for the varlist[eigenvariable] and dependent
variables. Note that when this is called, graphs[0]->independent_variable
may not have been initialized yet, but it will eventually be made
varlist[eigenvariable] anyway. It assumes that atoms on the left of '=' are
dependent variables. */
/* k is the total number of dependent and independent variables,
not counting parameters already introduced, e.g. in differentiate from defn.
so usually it is 2, but for parametric graphs and some ODE types
it is 3. In other words varlist[0]...varlist[k-1] will NOT be
parameters even if they occur in t. */
/* When doing minmax problems, we don't use parameters, because we
don't want the user to be able to change them while graphing, else
they won't match the values used in the symbolic solution. */
{ int i,j,natoms;
double z;
int nparameters = get_nparameters(); // there may already be some!
// e.g. in differentiate_from_defn there are already 2
term x = get_eigenvariable();
term *atomlist;
int nvariables = get_nvariables();
varinf *varinfo = get_varinfo();
term *varlist = get_varlist();
parameter *parameters;
int currentline = get_currentline();
unsigned f = FUNCTOR(t);
int currenttopic = get_currenttopic();
if(currenttopic == _minmax)
return; /* doing nothing */
natoms= variablesin(t,&atomlist); /* don't count e, pi_term, complexi */
if(natoms <= k + nparameters && contains(t,FUNCTOR(varlist[0])))
{ free2(atomlist);
return; /* dependent and independent variables only, no parameters */
}
/* Now remove the dependent and independent variables from atomlist */
for(i=0;i<natoms;i++)
{ if(equals(atomlist[i],x))
{ SETFUNCTOR(atomlist[i],ILLEGAL,0);
continue;
}
if(f == '=' && equals(atomlist[i],ARG(0,t)))
SETFUNCTOR(atomlist[i],ILLEGAL,0);
if(f == AND || f == OR)
{ for(j=0;j<ARITY(t);j++)
{ if(FUNCTOR(ARG(j,t)) == '=' && equals(atomlist[i],ARG(0,ARG(j,t))))
{ SETFUNCTOR(atomlist[i],ILLEGAL,0);
break; /* out of the j-loop */
}
}
}
if(mainchoice == RELATION || /* varlist[1] is the 'dependent' variable, i.e. the one
to go on the y-axis */
mainchoice == MC_SET ||
mainchoice == MC_INEQ ||
mainchoice == CONTOUR ||
mainchoice == PARAMETRICSURFACE || /* varlist[1] is the second independent variable */
mainchoice == NONPARAMETRICSURFACE ||
mainchoice == POLARNONPARAMETRICSURFACE ||
mainchoice == HDE ||
mainchoice == ODE ||
mainchoice == ODE2 ||
mainchoice == ODESYSTEM
)
{
if(equals(atomlist[i],varlist[1]))
SETFUNCTOR(atomlist[i],ILLEGAL,0);
}
if(k==3)
{ if(equals(atomlist[i],varlist[2]))
SETFUNCTOR(atomlist[i],ILLEGAL,0);
}
}
for(j=0;j<natoms;j++)
{ if(FUNCTOR(atomlist[j]) == ILLEGAL)
continue;
for(i=0;i<nvariables;i++)
{ if(equals(atomlist[j], varlist[i]))
break;
}
assert(i < nvariables); /* you must find the variable */
if(varinfo[i].scope != BOUND &&
/* removed: varinfo[i].scope != EXISTENTIAL &&
because when a trig equation has been reduced to 4x = (2n+1)pi_term,
we want n to be a parameter. */
contains(t,FUNCTOR(varlist[i]))
)
{ double c = get_default_value(varlist[i],t);
initialize_parameter(i,currentline,c);
}
}
free2(atomlist);
/* Now test whether we have chosen an unfortunate combination of
parameter values that makes the function everywhere undefined. */
while(FUNCTOR(t) == AND || FUNCTOR(t) == OR || FUNCTOR(t) == '=')
t = ARG(1,t);
for(j = 0;j < 10; j++)
{ SETVALUE(x,-0.4321 + j); /* a perfectly random value */
deval(t,&z);
if(z!=BADVAL)
return;
}
/* Oops, these parameter values are no good. Try different ones. */
parameters = get_parameters();
nparameters = get_nparameters();
for(i=0;i<nparameters;i++)
SETVALUE(varlist[parameters[i].index],i+1);
}
/*_________________________________________________________________*/
int default_range(term t, int mainchoice, int currenttopic, double *xmin, double *xmax, double *ymin, double *ymax, graph *g)
/* determine the initial (default) ranges of the horizontal and vertical
variables. The graph pointer is used only for RIEMANNSUMS and SIMPSONSRULE and TRAPEZOIDRULE.
Return 1 if multiples of pi are used, else return 0.
*/
{ if(needs_circular_aspect(mainchoice,currenttopic))
// This will have to be adjusted later as we can't get the aspect ratio of the
// viewport now, as set_pixel_fields hasn't been called yet.
{
*xmin = -2.0;
*xmax = 2.0;
*ymax = 2.0; //
*ymin = -*ymax;
return 0;
}
switch(mainchoice)
{ case RELATION:
if(!contains(t,PI_ATOM) &&
(contains(t,SIN) || contains(t,COS) || contains(t,TAN) ||
contains(t,COT) || contains(t,SEC) || contains(t,CSC)
)
)
{ *xmin = - 2*PI_DECIMAL;
*xmax = 2*PI_DECIMAL;
*ymin = -2.0;
*ymax = 2.0;
return 1;
}
*xmin = -8.0;
*xmax = 8.0;
*ymin = -8.0;
*ymax = 8.0;
return 0;
case RIEMANNSUMS:
case TRAPEZOIDRULE:
case SIMPSONSRULE:
{
double y,max,min,w,gap,n_intervals;
int i;
deval(g->nintervals,&n_intervals);
if(n_intervals == 0.0)
n_intervals = 1.0; // should never happen, but let's NEVER divide by zero.
w = g->right-g->left;
gap = w/n_intervals;
*xmin = g->left - 0.1 *w;
*xmax = g->right + 0.1 *w;
int i0 = g->riemannflag== 0 ? 0 : 1; // avoid evaluating 1/x at zero when we are using left rectangles.
SETVALUE(g->independent_variable,g->left);
for(i = i0; VALUE(g->independent_variable) <= g->right && i <= 500;i++) // 500 is just to be SURE there's no infinite loop here
{ SETVALUE(g->independent_variable, g->left + i * gap);
deval(g->function,&y);
if(i==i0)
max = min = y;
else if(y < min)
min = y;
else if(y > max)
max = y;
}
*ymax = max + 0.1*(max-min);
*ymin = min - 0.1*(max-min);
if(*ymin > 0.0)
*ymin = -1.0;
if(*ymax < 0.0)
*ymax = 1.0;
/* This ensures that the x-axis will show, and also that
*ymin < *ymax even if the function is constant. Otherwise
compute_tick_gap will crash.
*/
return 0;
}
case SPACECURVE:
case NONPARAMETRICSURFACE:
g->umin = -1.0;
g->umax = 1.0;
g->vmin = - 1.0;
g->vmax = 1.0;
*xmin = - 1.0;
*xmax = 1.0;
*ymin = -1.0;
*ymax = 1.0;
g->zmin = -1.0;
g->zmax = 1.0;
return 0;
case POLARNONPARAMETRICSURFACE:
g->umin = 0.0;
g->umax = 1.0;
g->vmin = 0.0;
g->vmax = 2*PI_DECIMAL;
*xmin = - 1.0;
*xmax = 1.0;
*ymin = -1.0;
*ymax = 1.0;
g->zmin = -1.0;
g->zmax = 1.0;
return 0;
case PARAMETRICSURFACE:
*xmin = - 2.0;
*xmax = 2.0;
*ymin = -2.0;
*ymax = 2.0;
g->zmin = -2.0;
g->zmax = 2.0;
/* If parameters are r and theta */
if(FUNCTOR(g->independent_variable) == 'r')
{ g->umin = 0.0;
g->umax = 1.0;
g->vmin = 0.0;
g->vmax = 2*PI_DECIMAL;
}
else
{ g->umin = -1.0;
g->umax = 1.0;
g->vmin = - 1.0;
g->vmax = 1.0;
}
return 0;
case HDE:
case ODE:
if(!contains(t,PI_ATOM) &&
(contains(t,SIN) || contains(t,COS) || contains(t,TAN) ||
contains(t,COT) || contains(t,SEC) || contains(t,CSC)
)
)
{ *xmin = - 2*PI_DECIMAL;
*xmax = 2*PI_DECIMAL;
*ymin = -10.0;
*ymax = 10.0;
return 1;
}
*xmin = -2.0;
*xmax = 2.0;
*ymin = -12.0;
*ymax = 12.0;
return 0;
default:
if(!contains(t,PI_ATOM) &&
(contains(t,SIN) || contains(t,COS) || contains(t,TAN) ||
contains(t,COT) || contains(t,SEC) || contains(t,CSC)
)
)
{ *xmin = - 2*PI_DECIMAL;
*xmax = 2*PI_DECIMAL;
*ymin = -2.0;
*ymax = 2.0;
return 1;
}
*xmin = -2.0;
*xmax = 2.0;
*ymin = -8.0;
*ymax = 8.0;
return 0;
}
}
/*_____________________________________________________________________*/
void pixel_fields(graph *g)
/* Set the fields g->pxmin etc., provided
set_device has been called so the 'device' will be valid;
otherwise this function does nothing.
*/
{ svgDevice *device = get_device();
if(device == NULL)
return; /* set_device hasn't been called yet */
g->pxmin = (int) (g->dxmin * HRES);
g->pxmax = (int) (g->dxmax * HRES);
g->pymin = (int) ((1.0 - g->dymax) * VRES);
g->pymax = (int) ((1.0 - g->dymin) * VRES);
}
/*____________________________________________________________________*/
static void adjust_graph(graph *g, term q)
/* q is a graph_interval or an AND of graph_intervals.
Extract the data from q and put it
into the right place in g.
*/
{ unsigned short i,n;
int kind;
double z,w;
term a,b,x,u;
unsigned f, h;
SETFUNCTOR(a,ILLEGAL,0);
SETFUNCTOR(b,ILLEGAL,0); /* so we can check they get found */
h = FUNCTOR(q);
assert(h==AND || h == '<' || h == LE || h == GE || h == LE);
if(FUNCTOR(ARG(0,q)) == AND)
/* q does not consist of a single graph_interval but a list of them */
{ n = ARITY(q);
for(i=0;i<n;i++)
adjust_graph(g,ARG(i,q));
return;
}
/* Now we're handling a single graph_interval */
assert(graph_interval(q));
for(i=0;i<2;i++)
{ u = ARG(i,q);
f = FUNCTOR(u);
if(f == '<' || f == LE)
{ if(ISATOM(ARG(0,u)))
{ b = ARG(1,u);
x = ARG(0,u);
}
else if(ISATOM(ARG(1,u)))
{ a = ARG(0,u);
x = ARG(1,u);
}
else
assert(0);
}
else if(f == '>' || f == GE)
{ if(ISATOM(ARG(0,u)))
{ a = ARG(1,u);
x = ARG(0,u);
}
else if(ISATOM(ARG(1,u)))
{ b = ARG(0,u);
x = ARG(1,u);
}
else
assert(0);
}
else
assert(0);
}
assert(FUNCTOR(a) != ILLEGAL);
assert(FUNCTOR(b) != ILLEGAL);
assert(seminumerical(a)); /* parameters can't appear in the graph intervals */
assert(seminumerical(b));
kind = g->graphtype;
/* Fine, now we have a,b, and x. From x we must
deduce where to put a and b in the graph structure *g.
*/
if(equals(x,g->independent_variable))
{ if(kind == PARAMETRIC || kind == POLAR || kind == POLAR_CIRCULAR || kind == ODESYSTEM || kind == ODE2)
{ deval(a,&g->tmin);
deval(b,&g->tmax);
g->parameter_interval= q;
}
else if(kind == PARAMETRICSURFACE)
{ deval(a,&g->umin);
deval(b,&g->umax);
}
else
{ deval(a,&g->xmin);
deval(b,&g->xmax);
}
}
else if(kind == PARAMETRICSURFACE &&
equals(x,g->independent_variable2)
)
{ deval(a,&g->vmin);
deval(b,&g->vmax);
}
else if(equals(x,g->dependent_variable))
{ if(kind == POLAR || kind == POLAR_CIRCULAR)
{ deval(a,&z);
deval(b,&w);
z = fabs(z);
w = fabs(w);
if(w > z)
z = w;
g->xmax = w;
g->xmin = -w;
double aspect = (g->pymax-g->pymin)/(g->pxmax-g->pxmin);
g->ymin = g->xmin * aspect;
g->ymax = g->xmax * aspect;
}
else if(kind == NONPARAMETRICSURFACE || kind == POLARNONPARAMETRICSURFACE)
{ deval(a,&g->zmin);
deval(b,&g->zmax);
}
else
{ deval(a,&g->ymin);
deval(b,&g->ymax);
}
}
if(!strcmp(atom_string(x), g->xvariable))
{ deval(a,&g->xmin);
deval(b,&g->xmax);
if(needs_circular_aspect(g->graphtype,get_currenttopic()))
{ g->ymax = g->xmax ;
g->ymin = g->xmin ;
}
}
else if(!strcmp(atom_string(x), g->yvariable))
{ deval(a,&g->ymin);
deval(b,&g->ymax);
if(needs_circular_aspect(g->graphtype,get_currenttopic()))
{ g->xmax = g->ymax;
g->xmin = g->ymin;
}
}
}
/*____________________________________________________________________*/
void adjust_graph_structures(int ngraphs, term q, graph **graphs)
/* the array graphs contains ngraphs graph structures, already completely
filled in. q is an interval, or several intervals combined into an AND,
which are to be used instead of default range values. The intervals
have passed graph_interval.
*/
{ int i;
for(i=0;i<ngraphs;i++)
adjust_graph(graphs[i],q);
}
/*___________________________________________*/
int nonconstant_exponent(term t)
/* return 1 if t contains a non-numerical exponent */
{ unsigned short i,n;
if(ATOMIC(t))
return 0;
if(FUNCTOR(t) == '^' && !OBJECT(ARG(1,t)))
return 1;
n = ARITY(t);
for(i=0;i<n;i++)
{ if(nonconstant_exponent(ARG(i,t)))
return 1;
}
return 0;
}
/*___________________________________________*/
int fix_exponents(term t, term *ans)
/* If t contains a non-numerical exponent which
must always be an integer (tested by isinteger),
substitute the specific integer to which it
evaluates using current parameter values. Do
this for all exponents so that polynomial terms
with variable exponents will come out as terms
with specific integer exponents, which will
then pass makepoly successfully. Bignum
exponents are rejected, since this is used for
graphing.
*/
{ unsigned short n;
int i;
term u;
long kk;
double z;
if(ATOMIC(t))
{ *ans = t;
return 0;
}
if(FUNCTOR(t) == '^')
{ u = ARG(1,t);
if(ISINTEGER(u))
{ *ans = t;
return 0;
}
if(seminumerical(u))
return 1;
if(!isinteger(u))
return 1;
deval(u,&z);
if(z==BADVAL || !nearint(z,&kk))
return 1;
*ans = make_power(ARG(0,t),make_int(kk));
return 0;
}
n = ARITY(t);
*ans = make_term(FUNCTOR(t),n);
for(i=0;i<n;i++)
{ if(fix_exponents(ARG(i,t),ARGPTR(*ans)+i))
return 1;
}
return 0;
}
/*_________________________________________________________________*/
int needs_circular_aspect(int mainchoice, int topic)
/* return 1 if a graph with the specified mainchoice and topic
should (by default) have an aspect
ratio calculated to make circles round, 0 otherwise.
*/
{ if (mainchoice == POLYROOT ||
mainchoice == POLAR ||
mainchoice == POLAR_CIRCULAR ||
mainchoice == PARAMETRIC ||
mainchoice == RELATION ||
mainchoice == MC_SET ||
mainchoice == CONTOUR
)
return 1;
if(topic == _graph_circle ||
topic == _graph_ellipse ||
topic == _graph_parabola ||
topic == _graph_hyperbola ||
topic == _ordinary_graph_linear
)
return 1;
return 0;
}
/*____________________________________________________________________*/
term domain_filter(term t, term x, term u)
/* t is a singularity (equation) or list of singularities of u as a
function of x. Reject those singularities which lie outside the
closure of the domain of u. Example: if u = log(x-4) - log(3x-12) - 1/x
then x = 0 will come up in the singularities of u (since ratsimp combines
the logs), and this function will reject it.
*/
{ unsigned short n;
int i,k;
term c,ans,v,temp;
double z,epsilon = .011, savexval,uval;
if(FUNCTOR(t) == '=' && equals(ARG(0,t),x))
{ c = ARG(1,t);
if(!seminumerical(c))
return t;
deval(c,&z);
if(z == BADVAL)
return t;
savexval = VALUE(x);
for(i=0;i<4;i++)
{ SETVALUE(x,z+epsilon);
deval(u,&uval);
if(uval != BADVAL)
return t;
SETVALUE(x,z-epsilon);
if(uval != BADVAL)
return t;
epsilon *= 0.1;
}
SETVALUE(x,savexval)
return falseterm;
}
if(FUNCTOR(t) != AND)
return t;
n = ARITY(t);
ans = make_term(AND,n);
k = 0;
for(i=0;i<n;i++)
{ v = domain_filter(ARG(i,t),x,u);
if(!equals(v,falseterm))
{ ARGREP(ans,k,v);
++k;
}
}
if(k==0)
{ RELEASE(ans);
return falseterm;
}
if(k==1)
{ temp = ARG(0,ans);
RELEASE(ans);
ans = temp;
return ans;
}
SETFUNCTOR(ans,AND,k);
return ans;
}
/*___________________________________________________________________________*/
static void set_parameters(term t)
/* t is of the form OR(F,p,q,n). If there are any parameters in F
then try to set their values in such a way that F is defined for the eigenvariable in [p,q],
because this is called when the Graph Button is pressed on integral(F,x,p,q).
*/
{ term x = get_eigenvariable();
int nparameters = get_nparameters();
parameter *parameters = get_parameters();
term *varlist = get_varlist();
term F,p,q,G;
double *addr;
term a;
int i;
if(FUNCTOR(t) != OR || ARITY(t) != 4)
return; // incorrectly called
for(i=0;i<nparameters;i++)
{ unsigned short b = parameters[i].functor;
if(contains(ARG(0,t),b))
{ a = varlist[parameters[i].index];
addr = parameters[i].addr;
break;
}
}
if(i==nparameters)
return; /* parameters were only in the endpoints, so default values are OK */
F = ARG(0,t);
p = ARG(1,t);
q = ARG(2,t);
/* Now set the value of a so that F is defined for x in [p,q]. Just try 2,3,4,... 9 */
for(i=2;i<10;i++)
{ subst(make_int(i),a,F,&G);
if(equals(trueterm,defined_on_interval(G,x,p,q)))
{ *addr = (double) i;
return;
}
}
return; /* failure, just have to live with the default values. */
}
/*___________________________________________________________*/
static double get_default_value(term x, term t)
/* Assumes x is a variable that will be a parameter for a graph, and
and t is the function to be graphed.
Find a good initial value for x; in particular if x is the upper
limit of a sum, the answer should be the value of the lower limit,
if that lower limit is at least 1.
Otherwise, return 1.0.
*/
{ unsigned short i, n= ARITY(t);
int err;
double p;
if(ATOMIC(t))
return 1.0;
if(FUNCTOR(t) == SUM && ARITY(t) == 4)
{ if(equals(x,ARG(3,t)) && seminumerical(ARG(2,t)))
{ err = deval(ARG(2,t),&p);
if(!err)
return p;
}
if(equals(x,ARG(2,t)) && seminumerical(ARG(3,t)))
{ err = deval(ARG(3,t),&p);
if(!err)
return p;
}
return 1.0;
}
for(i=0;i<n;i++)
{ p = get_default_value(x,ARG(i,t));
if(p > 1.0)
return p;
}
return 1.0;
}
#if 0
/* The following code converts an ordinary graph to a RELATION graph
if its singularities can't be calculated. Well, there's just one problem
with this: the graph will be wrong under RELATION too, since RELATION
just plots pixels where the sign changes. Anyway, if in the future
RELATION can be made to graph discontinuous functions correctly, this
might be useful.
*/
if(*mainchoice == ORDINARY ||
*mainchoice == COMPAREDERIV ||
*mainchoice == COMPAREDERIVS ||
*mainchoice == COMPARE_DIFFERENT
)
{ for(i=0;i<*ngraphs;i++)
{ if(graphs[i]->dimslist == 1 && equals(graphs[i]->slist[0], undefined))
{ /* singularities of this graph could not be calculated. Change
it to a RELATION graph */
*mainchoice = graphs[i]->graphtype = RELATION;
graphs[i]->function = equation(graphs[i]->dependent_variable,graphs[i]->function);
}
}
}
#endif
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists