Sindbad~EG File Manager
/* M. Beeson, for Mathpert */
/* Functions to build the SelectOperator menu */
/*
1.29.95 Original date
11.22.99 modified
1.4.00 added cothi and tanhi
1.9.00 modified condition for writeaspoly
1.10.00 added differenceofnth2
modified conditions for differenceofnthpowers
1.13.00 don't show factornumerically on complex polynomials
1.18.00 don't show "order terms" in complex expressions when solving equations
2.24.00 added include sselect.h
3.26.00 moved position of complexfactorsofinteger so it will show up on '5' too.
3.27.00 added code to throw in factorcomplexinteger
3.28.00 added code to throw in commondenomandsimp2
4.2.00 added code to throw in intervaltoabs1, intervaltoabs2
absevenpowerrev, abspowerrev
4.4.00 broke off selectintervalops and selectminmaxops because
'compiler limit exceeded in selectops1' (by the Borland compiler)
6.17.00 modified under POWERSERIES
11.21.00 modified conditions for mod2pi to avoid duplicate entries
6.17.04 modified for rectangulartopolar on complex atoms.
6.21.04 added minusintoproduct1, minusintoproduct2, minusintoproduct3,
lnrecip, logrecip, and logbrecip
6.23.04 added applyfunction0 etc.
6.24.04 added sqrtofi, sqrtofminusi, sqrtofaplusbi,sqrtofaminusbi
8.19.04 added complexarithmetic, complexpowers
8.20.04 tinkered further with conditions for complexarithmetic
1.21.06 removed shiftindex under finite sums, since it's thrown in for ALL sums; this avoids a duplicate entry.
1.23.06 put shiftindex in under sum(i,i), before the break statement
Added sumtodifofsums and sumtodifofsums0
1.25.06 added rejectpoint under MATRIX and took off conditions for it under OR
1.27.06 added sgtoabs, sgrecip2, sgrecip3, abstimessg and modified conditions for sgfract1 and sgfract2
8.30.07 added ARITY(t) > 2 in the conditions for orderterms
8.17.10 added code at line 634 to not show "express as polynomial" on two simple topics where it solves problems in one step.
1.14.11 added code to include sinhasinh and five similar operations
5.6.13 include <stddef.h>
5.6.13 changed limitflag to limitflag2 to avoid masking global limitflag.
changed = to == at line 2133.
5.10.13 eliminated calls to "access_optable" and removed optable.h
don't show seriesintdif if there's already an integral
5.11.13 added call to select_series_op on products
5.15.13 added call to select_series_op on trig functors.
5.17.13 added evaluatebernoulli
5.21.13 added contains_numerical_euler
5.24.13 added code to show evaluateeulernumber
5.29.13 removed 'static' from contains_numerical_euler
5.31.13 changed conditions for plainbinomialtheorem, binomialtheorem, binomialseries
6.2.13 changed conditions for binomialcoeftofactorials
6.4.13 added recipofi to selectexpops
6.4.13 added exponenttosqrtpower and exponenttorootpower
6.5.13 put in zetatobernoulli
6.8.13 added writeaspoly for products under POWERSERIES
6.10.13 don't show arithmetic, devalop for expressions containing RIEMANNZETA
6.17.13 removed mention of dead topic simplify_polynomial
9.26.14 removed unused function contains_nonconstant_powers
4.7.24 imported MakeSelectorMenu
4.8.24 added code to MakeSelectorMenu to make the svg elements
4.9.24 modified that code near "wrapper"
4.16.24 gave MakeSelectorMenu a third argument, menuops.
4.20.24 added code to parse and call termtoSVG to make displayed menu items
in MakeSelectorMenu
5.30.24 added two calls to SetCommentStop in MakeSelectorMenu
12.9.24 added factorbydemoivre
12.10.24 added line with comment 'need to get difeqn'
1.10.25 changed the condition for calling remove_duplicate_ops
3.13.25 added evalatpoint
*/
#define sincos _foo
#include <math.h>
#undef sincos
#include <string.h> /* memset */
#include <assert.h>
#include <stddef.h>
#include <stdio.h> /* sprintf */
#include "globals.h"
#include "graphstr.h"
#include "display.h"
#include "display1.h" /* needed by lterm.h */
#include "mpdoc.h"
#include "tdefn.h"
#include "checkarg.h"
#include "ops.h"
#include "trig.h"
#include "calc.h"
#include "bigrect.h"
#include "lterm.h"
#include "selectop.h"
#include "automode.h"
#include "cflags.h"
#include "exec.h" /* erasecolors, finish_exec */
#include "probtype.h"
#include "ops.h"
#include "trig.h"
#include "calc.h"
#include "series.h"
#include "pvalaux.h" /* content_factor */
/* rawcollectpowers */
/* contains_at_toplevel */
/* expandable_sum */
#include "order.h" /* additive_sortargs, common_variables */
#include "prover.h" /* NOTDEFINED */
#include "cancel.h" /* cancel */
#include "mplimits.h" /* LIMITAND */
#include "match.h" /* matchstring */
#include "polynoms.h" /* ispolyin */
#include "sigma.h" /* freevars */
#include "factor.h" /* numerical_poly, long_quadratic */
#include "eqn.h" /* econstant */
#include "solvelin.h" /* is_linear_in */
#include "intsub.h" /* readpending */
#include "exponent.h" /* possible_int */
#include "deval.h"
#include "docdata.h" /* history */
#include "chkinput.h" /* mathematical */
#include "pda.h" /* seminumerical2 */
#include "nfactor.h" /* small_prime */
#include "select2.h" /* select_integration_op etc */
#include "selineq.h" /* select_inequality_ops */
#include "userfunc.h" /* is_defined */
#include "numpower.h" /* possible_power */
#include "preops.h" /* almost_algebraic */
#include "sqrts.h" /* fractexps */
#include "dcomplex.h" /* needed by ceval.h */
#include "ceval.h" /* complexnumerical */
#include "domain.h" /* contains_defined_variables */
#include "relrates.h" /* used, DIFEQN */
#include "binders.h"
#include "mpminmax.h" /* used2, ADDLIMITS, etc. */
#include "autosimp.h" /* contains_calc */
#include "selfract.h" /* selectfractops */
#include "islinear.h" /* islinear */
#include "sselect.h" /* select_series_op */
#include "operator.h"
#include "preops.h" /* contains_numerical_bernoulli */
#include "dowith.h" /* on_path */
#include "svgSymbolText.h" /* MakeSelectorMenu */
#define MAXFLAGS 300
#define ZEROFLAG 255
#define ONEFLAG 256
#define SINSQ 257
#define TANSQ 258
#define COTSQ 259
#define COSSQ 260
#define SECSQ 261
#define CSCSQ 262
#define SINHSQ 263
#define COSHSQ 264
#define TANHSQ 265
#define SECHSQ 266
#define CSCHSQ 267
#define COTHSQ 268
#define DEFINT 269
#define ALLNEG 270
#define NEGEXP 271
#define TRIGPRODUCT 272
#define POWEROFSUM 273
#define COMPLEXCOS 274
#define COMPLEXSIN 275
#define COMPOUNDFRACTION 276
#define TRIGF(h) (h==SIN || h==COS || h==TAN || h==COT || h==SEC || h==CSC)
#define ISIPI(v) ((equals(ARG(0,v),complexi) && equals(ARG(1,v),pi_term)) || (equals(ARG(0,v),pi_term) && equals(ARG(1,v),complexi)))
#define SIGNED_INTEGERP(x) (INTEGERP(x) || (NEGATIVE(x) && INTEGERP(ARG(0,x))))
static int computefunctionflag;
static void selectops1(int binderflag,term t, actualop *o, int *nops);
static void selectops2(unsigned short, term, actualop *, int *);
static void analyze_exponents(term t, int flag, int *exponents);
static void trigsq(term v, int flag[MAXFLAGS]);
static int possiblepoly(term t);
static int contains_integer_factor(term u);
static void selectexpops(term t, actualop *o, int *nops);
static void selectminmaxops(term t, actualop *o, int *nops);
static void selectintervalops(term t, actualop *o, int *nops);
static void count_aux(int count[6], unsigned short hh);
static int ok_sumleadingterm(pathlist *p);
static int limitflag2;
static int topflag;
static int ctestflag; /* used to control convergence tests */
static int inexact(term t);
static int contains_binders(term t);
static int binders_on_path(lterm t, term a);
static int contains_numerical_subterm(term t);
static int contains3(term t, unsigned short f);
/*__________________________________________________________________*/
#define MAXEXPS 10
static void selectops1(int binderflag,term t, actualop *o, int *nops)
/* Fill pre-allocated array o with the operators that
could be applied to t. Return the dimension of the
array in *nops. binderflag is passed as -1 if the path to t contains DIFF or
indefinite INTEGRAL, 1 if t contains a true binding operator, 0 otherwise.
*/
{ unsigned short f = FUNCTOR(t);
unsigned short n = ARITY(t);
term u,v,w,c,a,b,x,y,s,cancelled,trash;
POLYnomial p;
term *atomlist;
char buffer[DIMREASONBUFFER];
unsigned short g,h;
term temp;
int mflag = 0; /* set when multiplyoutandsimp is thrown in,
and check to avoid duplicates */
int i=0;
int k,count,err,maxarity,nfree,ct33=0;
int flag[MAXFLAGS];
int exponents[MAXEXPS];
int problemtype = get_problemtype();
int currenttopic = get_currenttopic();
int numberflag = 0;
int nvariables = variablesin(t,&atomlist);
int nfractexps;
int index;
term fractexp[3];
int stopflag = GetStopFlag();
SetStopFlag(1); /* turn off inhibit/release mechanism so that
operators tried here do not release inhibitions
or create new ones */
free2(atomlist);
computefunctionflag = 0; /* prevent duplicate entry of computefunction */
if(numerical(t) && FUNCTOR(t) == RIEMANNZETA && isinteger(ARG(0,t)) && iseven(ARG(0,t)))
{ o[i] = zetatobernoulli; ++i;
}
if(contains_numerical_bernoulli(t))
{ o[i] = evaluatebernoulli; ++i;
}
if(contains_numerical_euler(t))
{ o[i] = evaluateeulernumber; ++i;
}
if(topflag && currenttopic == _evaluate_numerically && get_nvariables() > 0)
{ o[i] = evalatpoint; ++i;
}
if(topflag && problemtype == POWERSERIES &&
!contains(t,INTEGRAL) /* don't show these operations if they've already been used */
)
{ /* power series */
o[i] = seriesintdif; ++i;
o[i] = seriesintdifdef; ++i;
/* o[i] = seriesdifint; ++i; Is this ever useful? I don't think so */
if(contains(t,CONSTANTOFINTEGRATION))
{ o[i] = eliminatecofi; ++i;
}
}
if((index = is_defined(f)) >= 0 )
{ // a user-defined function
switch(index)
{ case 0: o[i] = applyfunction0; break;
case 1: o[i] = applyfunction1; break;
case 2: o[i] = applyfunction2; break;
case 3: o[i] = applyfunction3; break;
case 4: o[i] = applyfunction4; break;
case 5: o[i] = applyfunction5; break;
case 6: o[i] = applyfunction6; break;
case 7: o[i] = applyfunction7; break;
case 8: o[i] = applyfunction8; break;
case 9: o[i] = applyfunction9; break;
case 10: o[i] = applyfunction10; break;
case 11: o[i] = applyfunction11; break;
case 12: o[i] = applyfunction12; break;
case 13: o[i] = applyfunction13; break;
case 14: o[i] = applyfunction14; break;
case 15: o[i] = applyfunction15; break;
default: assert(0);
}
++i;
}
if(f == '=' &&
currenttopic == _logarithmic_differentiation
)
{ if(!contains(t,DIFF))
{ o[i] = difeqn; ++i;
o[i] = lneqn; ++i;
}
else
{ o[i] = muleqn; ++i;
}
SetStopFlag(stopflag);
*nops = i;
return;
}
if(f == '=' &&
currenttopic == _int_by_substitution &&
!contains(t,DIFF)
)
{ o[i] = difsubstitution; ++i;
*nops = i;
SetStopFlag(stopflag);
return;
}
if(f == '=' && problemtype == INTEGRATION)
{ o[i] = changesigns; ++i;
o[i] = addeqn; ++i;
o[i] = subeqn; ++i;
o[i] = muleqn; ++i;
o[i] = diveqn; ++i;
if(get_pending() != NULL &&
FUNCTOR(ARG(0,t)) == DIFF &&
!contains(ARG(1,t),DIFF)
)
{ o[i] = showcallingproblem; ++i;
}
*nops = i;
SetStopFlag(stopflag);
return;
}
if(f == '=' && problemtype == TRIG_IDENTITY)
{ select_identity_op(t,o+i,nops);
SetStopFlag(stopflag);
return;
}
if(problemtype == TESTCONVERGENCE && ctestflag && !contains_calc(t) && !contains_series(t))
/* t is allowed to contain a finite indexed sum */
{ int k;
series_toplevel(t,o+i,&k);
i+= k;
}
if(problemtype == TESTCONVERGENCE && ctestflag && FUNCTOR(t) == '=' && ISINFINITE(ARG(1,t))
&& FUNCTOR(ARG(0,t)) == SUM
)
{ int k;
series_toplevel(t,o+i,&k);
i+=k;
}
if(problemtype == TESTCONVERGENCE && ctestflag && FUNCTOR(t) == LE
&& !contains_series(ARG(1,t))
&& FUNCTOR(ARG(0,t)) == SUM
)
{ int k;
series_toplevel(t,o+i,&k);
i+=k;
}
if(f == '=' && topflag && problemtype == MINMAX)
{ selectminmaxops(t,o+i,&k);
i+=k;
if(i > 0)
{ *nops = i;
SetStopFlag(stopflag);
return;
}
}
if(f == '=' && nvariables > 0 &&
(SOLVETYPE(problemtype) ||
problemtype == TRIG_IDENTITY ||
problemtype == LINEAR_EQUATIONS ||
problemtype == DIFFERENTIATE // need to get difeqn
)
)
/* You can't solve an equation when there are no variables */
{ select_equation_ops(t,o,nops);
SetStopFlag(stopflag);
return;
}
if(f == '=' && currenttopic == _higher_order_diff)
{ o[i] = difeqn; ++i;
}
if(f == '=' && nvariables == 0)
{ if(equals(ARG(0,t),ARG(1,t)))
{ o[i] = trueeqn; ++i;
}
else
{ o[i] = rejecteqn; ++i;
}
*nops = i;
SetStopFlag(stopflag);
return;
}
if(INEQUALITY(f) && f != '=')
{ select_inequality_ops(t,o+i,nops);
SetStopFlag(stopflag);
return;
}
memset(flag,0,sizeof(flag));
if(
( contains(t,UNDEFINED) ||
contains(t,BOUNDED_OSCILLATIONS) ||
contains(t,UNBOUNDED_OSCILLATIONS)
) &&
( get_problemtype() != MINMAX ||
!contains(t,'=')
)
)
{ o[i] = undefinedpart; ++i;
*nops = i;
SetStopFlag(stopflag);
return;
}
if(ISATOM(t))
{ if(equals(t,pi_term))
{ o[i] = evalpi; ++i; /* decimal value of pi */
}
else if(equals(t,eulere))
{ o[i] = evaleulere; ++i; /* decimal value of e */
}
else if(equals(t,complexi))
{ o[i] = rectangulartopolar; ++i;
}
else if(equals(t,trueterm) && problemtype == INEQUALITIES && get_nextassumption() > 0)
{ o[i] = explicitdomain; ++i; /* use assumptions */
}
if(get_complex() && TYPE(t) == DCOMPLEX)
{ o[i] = rectangulartopolar; ++i;
}
*nops = i; /* nothing else you can do to an atom */
SetStopFlag(stopflag);
return;
}
if(INTEGERP(t))
{ long k;
if(TYPE(t) == BIGNUM)
{ o[i] = factorinteger; ++i;
o[i] = writeintegeraspower; ++i;
o[i] = writeassum; ++i;
*nops = i;
SetStopFlag(stopflag);
return;
}
k = INTDATA(t);
o[i] = writeassum; ++i;
if(get_complex() && status(complexfactorsofinteger) >= LEARNING)
{ o[i] = complexfactorsofinteger; ++i;
}
if(k <= 10 && k != 4 && k != 6 && k != 8 && k != 9 && k != 10)
{ *nops = i;
SetStopFlag(stopflag);
return; /* don't present the choice Factor Integer
on primes <= 10 */
}
o[i] = factorinteger; ++i;
if(!writenumberassquare(t,zero,&w,buffer))
{ o[i] = writenumberassquare; ++i;
}
if(!writenumberascube(t,zero,&w,buffer))
{ o[i] = writenumberascube; ++i;
}
if(k > 1000 || !writeintegeraspower(t,zero,&w,buffer))
{ o[i] = writeintegeraspower; ++i; /* express as power */
o[i] = writenumberaspower; ++i; /* express in form a^? */
o[i] = writenumberaspowerof; ++i; /* express as power of ? */
}
*nops = i;
SetStopFlag(stopflag);
return; /* nothing else you can do to an integer */
}
if(OBJECT(t)) /* that is, a double */
{ if(!decimaltofraction(t,zero,&w,buffer))
/* don't show it unless it will work */
{ o[i] = decimaltofraction; ++i;
}
*nops = i;
SetStopFlag(stopflag);
return;
}
if(NUMBER(t))
{ if(!writenumberassquare(t,zero,&w,buffer))
{ o[i] = writenumberassquare; ++i;
}
if(!writenumberascube(t,zero,&w,buffer))
{ o[i] = writenumberascube; ++i;
}
if(possible_power(t))
{ o[i] = writenumberaspower; ++i;
}
}
if((nfractexps = fractexps(t,fractexp)) != 0 &&
!(f == '^' && FRACTION(ARG(1,t)))
)
{ if(nfractexps == 1 && ONEHALF(fractexp[0]))
{ o[i] = backtosqrts; ++i;
}
else
{ o[i] = backtoroots; ++i;
}
}
if(f != ROOT &&
f != SQRT &&
(contains(t,ROOT) || contains(t,SQRT))
)
{ o[i] = eliminatesqrts; ++i;
}
if(f == ROOT && (contains(ARG(1,t),ROOT) || contains(ARG(1,t),SQRT)))
{ o[i] = eliminatesqrts; ++i;
}
if(f == SQRT && (contains(ARG(0,t),ROOT) || contains(ARG(0,t),SQRT)))
{ o[i] = eliminatesqrts; ++i;
}
if(seminumerical2(t) && !contains(t,MATRIX) && !contains(t,VECTOR) && !contains(t,RIEMANNZETA) &&
/* don't show "arithmetic" on every number; but do show it on 2/4 */
(!NUMBER(t) ||
( RATIONALP(t) && !cancel(ARG(0,t),ARG(1,t),&a,&b)) ||
( FRACTION(t) && ONE(ARG(0,t)) && ONE(ARG(1,t))) ||
/* special case 1/1 isn't covered otherwise since cancel(1,1,...) fails! */
( NEGATIVE(t) && RATIONALP(ARG(0,t)) && !cancel(ARG(0,ARG(0,t)),ARG(1,ARG(0,t)),&a,&b))
)
)
{ assert(!INEQUALITY(f)); /* select_inequality_ops has handled inequalities */
o[i] = arithmetic; ++i; /* arithmetic */
if (f == ROOT || f == SQRT)
{ o[i] = computeroot; ++i;
if(f == SQRT)
{ u = ARG(0,t);
if(INTEGERP(u) || RATIONALP(u))
{ o[i] = evaltorational; ++i;
}
}
else /* f == ROOT */
{ u = ARG(1,t);
if(NEGATIVE(u))
u = ARG(0,u);
if(INTEGERP(u) || RATIONALP(u))
{ o[i] = evaltorational; ++i;
}
}
}
else if(
(n == 1 && f != '-') ||
(
n == 2 && f != '/' && f != '+' &&
f != '*' && f != '^' && f != AND && f != OR
)
)
{ o[i] = computefunction; ++i;
computefunctionflag = 1;
}
else if (f == '^' && NUMBER(ARG(1,t)))
{ o[i] = computepower; ++i;
}
else
{ o[i] = devalop; ++i; /* decimal arithmetic */
}
/* But don't stop here, because you can also perform
algebraic operations on purely numerical expressions */
}
else if(complexnumerical(t) && f != CONSTANTOFINTEGRATION && !contains(t,RIEMANNZETA) &&
!contains(t,MATRIX) && !contains(t,VECTOR)
)
{ if(!arithmetic(t,zero,&w,buffer))
{ o[i] = arithmetic; ++i; /* 3 + i + 9 for example */
}
if(complex_number(t))
{ parts(t,&a,&b);
if(NEGATIVE(a))
a = ARG(0,a);
if(NEGATIVE(b))
b = ARG(0,b);
if(ISINTEGER(a) && ISINTEGER(b) && !ZERO(a))
{ o[i] = factorcomplexinteger; ++i;
}
}
if(iscomplex(t) && FUNCTOR(t) != '^')
{ o[i] = weakcomplexarithmetic; ++i;
o[i] = cevalop; ++i; /* complex decimal arithmetic */
}
else if( FUNCTOR(t) == '^' && iscomplex(ARG(0,t)))
{ if(AE(t) || CE(t)) // otherwise these won't work anyway
{ o[i] = complexpowers; ++i;
o[i] = complexarithmetic; ++i;
}
o[i] = cevalop; ++i;
}
else if(iscomplex(t) || (get_complex() && contains(t,'^')))
{ if(!(FUNCTOR(t) == '^' && equals(ARG(0,t),eulere))) // won't work in this case
{ o[i] = complexarithmetic; ++i;}
o[i] = cevalop; ++i;
}
else if(f == ROOT || f == SQRT)
{ o[i] = computeroot; ++i;
}
else if(f == '^' && NUMBER(ARG(1,t)))
{ o[i] = computepower; ++i;
}
else if(
(n == 1 && f != '-') ||
(
n == 2 && f != '/' && f != '+' &&
f != '*' && f != '^' && f != AND && f != OR
)
)
{ o[i] = computefunction; ++i;
computefunctionflag = 1;
}
else if(!INTEGERP(t) && !(NEGATIVE(t) && INTEGERP(ARG(0,t))))
{ o[i] = devalop; ++i;
}
}
else if(contains_numerical_subterm(t) > 1)
{ if(is_complex(t))
{ o[i] = complexarithmetic; ++i;
}
o[i] = arithmetic; ++i;
}
else if(select_arith_aux(t))
/* Arithmetic can be used even on non-numerical terms
such as 2 + x + 3 */
{ if(is_complex(t))
{ o[i] = complexarithmetic ; ++i;
/* but arithmetic can also be used on complex terms, e.g.
on (-1)^2 u where u is complex, yielding u */
if(contains_numerical_subterm(t) > 1)
{ o[i] = arithmetic; ++i;
}
}
else
{ o[i] = arithmetic; ++i;
}
}
if(is_complex(t) && !mathematical(t)) /* mathematical returns 0 for success */
{ o[i] = complexform; ++i; /* write in form x+yi */
}
u = ARG(0,t);
g = FUNCTOR(u);
nfree = freevars(t,&atomlist);
free2(atomlist);
if(topflag && nfree > 1 &&
!interval_as_and(t) &&
!contains_calc(t)
)
/* can't succeed anyway unless there is a free variable */
/* on interval_as_and terms, this would result in a duplicate entry */
{ term dummy;
SETFUNCTOR(dummy, ILLEGAL,0);
if(
(f != '+' || problemtype == SIMPLIFY) &&
test_invisiblesub(dummy)
/* This asks whether automode could find a substitution */
)
/* It will be thrown in below on sums anyway except on SIMPLIFY */
{ o[i] = invisiblesub; ++i;
}
o[i] = makesubstitution; ++i;
}
if(f == AND && currenttopic == _eqns_by_substitution)
{ if(!polyvalop(t,seven,&w,buffer))
{ o[i] = polyvalop; ++i;
}
}
if(f == AND &&
problemtype == LINEAR_EQUATIONS &&
!LINEUP(t)
)
{ o[i] = lineupvars; ++i;
}
/* Throw in 'simplify' for sums, products, fractions, powers, and
interval_as_and terms,
if it will do something, and is KNOWN or better in the user model;
throw in 'write as polynomial' if there are no impossible functors
and there's only one variable */
if(f == '^' || f == '+' || f == '-' || f == '/' || f == '*' || f == OR ||
f == SQRT || f == ROOT ||
(f == INTEGRAL && FUNCTOR(ARG(0,t)) == '+') ||
(f == AND && interval_as_and(t))
)
{ if(status(polyvalop) > LEARNING || problemtype == TRIG_IDENTITY)
{ if((f == '+' && expandable_sum(t)))
{ o[i] = multiplyoutandsimp; ++i;
++mflag;
}
else if((f == '*' || f == '^') && expandable_for_polyval(t))
{ o[i] = multiplyoutandsimp; ++i;
++mflag;
}
if(f == '^' && NEGATIVE(ARG(1,t)))
/* polyvalop eliminates negative exponents in the denom,
but if called on such a thing directly in auto mode
it doesn't know it's in the denom and fails. It works
on the fraction and then calls set_path. So trying
it as below doesn't work. This gets it on the term
selection menu anyway. Now, when Show Step shows it
and the user chooses it, it should not fail. Therefore,
polyvalop must, in Selection Mode, eliminate a toplevel
negative exponent. */
{ o[i] = polyvalop; ++i;
}
else if(!polyvalop(t,seven,&w,buffer))
/* We must call polyvalop, not just polyval, because polyvalop sets
some flags before calling polyval. Passing 'seven' tells it not
to call set_pathtail etc. */
{ o[i] = polyvalop; ++i;
}
}
else if(complexnumerical(t) &&
(contains(t,SQRT) || contains(t,ROOT)) &&
!ALREADY(t) && !polyvalop(t,seven,&w,buffer)
)
{ o[i] = polyvalop; ++i; /* pre_ops puts in polyvalop under related
conditions, e.g. to simplify 1/2 + sqrt(5)/2 before commondenom gets it.
But polyvalop 'tightens' the focus, so we may get only the fraction
or the root here. To avoid putting 'simplify' on the menu when it
is irrelevant, we actually test it first */
}
else if(f == '+' && almost_algebraic(t))
{ o[i] = polyvalop; ++i;
}
if(possiblepoly(t)) /* no functors but the above listed ones */
{ if(nvariables == 1 &&
currenttopic != _multiply_polynomials &&
currenttopic != _simplify_polynomials &&
!univariatepoly(t,zero,&w,buffer)
)
/* Express as polynomial--only show it if it will work. */
{ o[i] = univariatepoly; ++i;
}
}
}
switch(f)
{ case '-' :
if(get_complex())
{ if(equals(u,complexi) || OBJECT(u))
{ o[i] = rectangulartopolar; ++i;
}
o[i] = minustopolar; ++i;
}
if(ATOMIC(u))
break;
switch(g)
{ case '-':
o[i] = doubleminus; ++i;
break;
case '+':
o[i] = pushminusin; ++i;
break;
case '*':
o[i] = minusintoproduct1; ++i;
o[i] = minusintoproduct2; ++i;
if(get_complex() && status(rectangulartopolar) >= LEARNING &&
!rectangulartopolar(t,zero,&w,buffer)
)
{ o[i] = rectangulartopolar; ++i;
}
if(FRACTION(ARG(0,u)))
{ o[i] = minusintonum; ++i;
/* example: -((e^-x - e^x)/2)i */
}
if(contains(u,'+') && !distriblaw(u,zero,&w,buffer))
{ o[i] = distriblaw; ++i;
}
break;
case '/':
if(NEGATIVE(ARG(0,u)))
{ o[i] = minusoutfromnum; ++i;
}
if(NEGATIVE(ARG(1,u)))
{ o[i] = minusoutfromdenom; ++i;
}
if(FUNCTOR(ARG(0,u)) == '+' && all_args_negative(ARG(0,u)))
{ o[i] = minusoutfromnum22; ++i;
}
if(FUNCTOR(ARG(1,u)) == '+' && all_args_negative(ARG(1,u)))
{ o[i] = minusoutfromdenom22; ++i;
}
if(FUNCTOR(ARG(1,u)) == '+' && ARITY(ARG(1,u))==2 &&
(NEGATIVE(ARG(0,ARG(1,u))) || NEGATIVE(ARG(1,ARG(1,u)))) &&
!(NEGATIVE(ARG(0,ARG(1,u))) && NEGATIVE(ARG(1,ARG(1,u))))
)
{ o[i] = minusoutfromdenom33; ++i;
}
o[i] = minusintonum; ++i;
o[i] = minusintodenom; ++i;
break;
case ROOT:
if(isodd(ARG(0,u)))
{ o[i] = pushminusunderroot; ++i;
}
}
break;
case '+':
for(k=0;k<n;k++)
{ w = ARG(k,t);
if(NEGATIVE(w))
w = ARG(0,w);
if(FUNCTOR(ARG(k,t)) != DEG)
break;
}
if(k==n)
{ o[i] = adddegrees; ++i;
}
for(k=0;k<n;k++)
{ w = ARG(k,t);
if(NEGATIVE(w))
w = ARG(0,w);
if(FUNCTOR(w) == SUM)
++ct33;
}
if(ct33 > 1)
{ if(n == 2 && NEGATIVE(ARG(1,t)))
{ o[i] = subseries; ++i;
}
else
{ o[i] = addseries; ++i;
}
}
count = 0;
for(k=0;k<n;k++)
{ if(contains(ARG(k,t),INFINITYFUNCTOR))
++count;
}
if(count > 1)
{ if(!infinityminusinfinity(t,zero,&w,buffer))
{ o[i] = infinityminusinfinity; ++i;
break;
}
if(!infinityplusinfinity(t,zero,&w,buffer))
{ o[i] = infinityplusinfinity; ++i;
}
}
err = content_factor(t,&c,&a);
/* Just go ahead and check if it works, so we don't
display it for EVERY sum */
if(!err)
{ o[i] = contentfactor; ++i;
if( ( seminumerical(c) ||
(FUNCTOR(c) == '*' && seminumerical(ARG(0,c)))
) &&
!factoroutnumber(t,zero,&w,buffer)
)
{ o[i] = factoroutnumber; ++i;
/* This should show it only when it will work;
since it doesn't pull out fractions, the only
way to be sure is to try it. */
}
if(SOLVETYPE(problemtype) || problemtype >= LIMITS)
{ o[i] = factoroutconstant; ++i;
}
}
nvariables = variablesin(t,&atomlist);
/* we did this once, but we freed atomlist, so now we have to do it again */
if(nvariables == 1 && !makepoly(t,atomlist[0],&w) && !ONE(ARG(ARITY(w)-1,w)))
{ /* polynomial with leading coefficient not 1 */
o[i] = monicpoly; ++i;
RELEASE(w);
}
free2(atomlist);
if(contains(t,'/') && !cleardenoms(t,zero,&w,buffer))
{ o[i] = cleardenoms; ++i;
}
if(!collectall(t,zero,&w,buffer))
{ o[i] = collectall; ++i;
/* to use collectterms instead, you select the terms to collect */
if(status(collectall) < KNOWN)
/* automode uses collecterms then; if we don't put it in,
ShowStep can't find it. */
{ o[i] = collectterms; ++i;
}
}
if(!additivecancel(t,zero,&w,buffer))
{ o[i] = additivecancel; ++i;
/* or you could select the particular terms to cancel */
}
if(!seminumerical(t))
/* can't succeed anyway unless there is a variable */
{ if(status(writeaspoly) != UNKNOWN)
/* this keeps it from being shown on topic _binomial_theorem */
{ o[i] = writeaspoly; ++i;
}
if(problemtype != SIMPLIFY && test_invisiblesub(t))
{ o[i] = invisiblesub; ++i;
}
}
if(!get_complex() || problemtype != SOLVE_EQUATION || !iscomplex(t) || ARITY(t) > 2)
/* we need ARITY(t) > 2 to cover a case like 2 + x + i, which is complex, but we still need to make it x + 2 + i */
{ w = additive_order(t);
if(!equals(w,t))
{ o[i] = orderterms; ++i; /* Show it only when it would work */
RELEASE(w); /* created by additive_order */
}
}
if(ARITY(t) == 2)
{ o[i] = additivecommute; ++i;
}
for(k=0;k<n;k++)
{ v = ARG(k,t);
if(NEGATIVE(v))
{ ++flag['-'];
v = ARG(0,v);
}
while(NEGATIVE(v)) /* Example: v is -(-(a+b)) */
v = ARG(0,v); /* but only increment flag[-] once */
if(FUNCTOR(v) != '^') /* '^' is handled by trigsq below */
++flag[FUNCTOR(v)];
if(FUNCTOR(v) == SIN && iscomplex(ARG(0,v)) &&
(FUNCTOR(ARG(0,v)) == '/' || FUNCTOR(ARG(0,v)) == '*' || equals(ARG(0,v), complexi))
)
++flag[COMPLEXSIN];
if(FUNCTOR(v) == COS && iscomplex(ARG(0,v)) &&
(FUNCTOR(ARG(0,v)) == '/' || FUNCTOR(ARG(0,v)) == '*' || equals(ARG(0,v), complexi))
)
++flag[COMPLEXCOS];
if(ISZERO(v)) /* ISZERO counts 0.0 as well as 0 */
++flag[ZEROFLAG];
trigsq(v,flag); /* also increments flag['^'] on products
with an exponent in a factor */
if(FRACTION(v) && FUNCTOR(ARG(1,v)) == '^')
++flag['^']; /* so we can factor 1-1/x^2 etc */
if(FRACTION(v) && contains3(ARG(1,v),'/'))
++flag[COMPOUNDFRACTION];
if(FUNCTOR(v)==INTEGRAL && ARITY(v) == 4)
++flag[DEFINT];
if(seminumerical(v))
{ ++numberflag;
if(ISONE(v) || (NEGATIVE(v) && ISONE(ARG(0,v))))
++flag[ONEFLAG];
}
if(FUNCTOR(v) == '*')
{ /* check for a fraction with integer denom in v */
int jj;
for (jj = 0;jj < ARITY(v); ++jj)
{ if(FRACTION(ARG(jj,v)) &&
INTEGERP(ARG(1,ARG(jj,v)))
)
{ ++flag['/']; /* commondenom can work here too */
break;
}
}
}
}
if(flag[COMPLEXSIN] && flag[COMPLEXCOS])
{ o[i] = cosisin; ++i;
o[i] = cosminusisin; ++i;
}
if(flag[INTEGRAL] > 1 && !additivity(t,zero,&w,buffer))
/* combine two definite integrals with the same integrand */
{ o[i] = additivity; ++i;
}
if(flag[TRIGPRODUCT] == 2)
{ /* show reverse_trig operations if they will work */
if(!sinsumrev(t,zero,&w,buffer))
{ o[i] = sinsumrev; ++i;
}
if(!sindifrev(t,zero,&w,buffer))
{ o[i] = sindifrev; ++i;
}
if(!cossumrev(t,zero,&w,buffer))
{ o[i] = cossumrev; ++i;
}
if(!cosdifrev(t,zero,&w,buffer))
{ o[i] = cosdifrev; ++i;
}
}
if(flag['^'] > 1 && contains(t,'e'))
{ if(!coshdefrev2(t,zero,&w,buffer))
{ o[i] = coshdefrev2; ++i;
}
if(!sinhdefrev2(t,zero,&w,buffer))
{ o[i] = sinhdefrev2; ++i;
}
}
if(flag['^'] > 1 && contains(t,'e') && iscomplex(t))
{ if(!complexsinrev2(t,zero,&w,buffer))
{ o[i] = complexsinrev2; ++i;
}
if(!complexcosrev2(t,zero,&w,buffer))
{ o[i] = complexcosrev2; ++i;
}
}
if(flag[CONSTANTOFINTEGRATION] && numberflag)
{ o[i] = absorbconstant; ++i;
}
if(flag[COS])
{ if(!doublecos5(t,zero,&w,buffer)) /* cos 2theta + 1 = 2cos^2 theta */
{ o[i] = doublecos5; ++i;
}
if(!doublecos6(t,zero,&w,buffer)) /* cos 2theta - 1 = - 2 sin^2 theta */
{ o[i] = doublecos6; ++i;
}
if(!reversesinsq(t,zero,&w,buffer)) /* 1-cos theta = 2 sin^2 (theta /2) */
{ o[i] = reversesinsq; ++i;
}
}
if(flag[SINH] && flag[COSH])
{ o[i] = coshplussinh; ++i;
if(flag['-'])
{ o[i] = coshminussinh; ++i;
}
}
if(flag['+'])
{ o[i] = regroupterms; ++i;
}
if(flag['-'] == n)
{ o[i] = pullminusout; ++i;
}
if(flag[ZEROFLAG])
{ o[i] = dropzero; ++i;
}
if(flag['/'] > 1 && !addfractions(t,zero,&w,buffer))
/* show addfractions only when it will work */
{ o[i] = addfractions; ++i;
}
if(flag['/'])
{ if(status(commondenom) <= LEARNING)
{ o[i] = findcommondenom; ++i;
}
if(!flag[COMPOUNDFRACTION])
/* these high-powered operations have confusing
results if there are fractions in the denominators.
*/
{ o[i] = commondenom; ++i;
o[i] = commondenomandsimp; ++i;
if(ARITY(t) > 2)
{ /* check whether there is a non-fraction and
at least two fractions */
int nonfraction = 0;
int fraction = 0;
term temp;
for(k=0;k<ARITY(t);k++)
{ temp = ARG(k,t);
if(NEGATIVE(temp))
temp = ARG(0,t);
if(FRACTION(temp))
++fraction;
else
++nonfraction;
}
if(nonfraction && fraction > 1)
{ o[i] = commondenomandsimp2; ++i;
}
}
}
}
if(n==2 && flag['-']==1 && status(lauringson) >= LEARNING &&
differenceofsquares(t,zero,&w,buffer) &&
contains(history(get_currentline()),SQRT)
)
/* Need to use it for x-9 = (sqrt(x)-3)(sqrt(x)+3) too,
so we don't require flag['^'].
On the other hand, usually this is useless clutter
so we don't include it unless there is a SQRT somewhere
in the current line, and don't include it if
differenceofsquares will do the job.
*/
{ o[i] = lauringson; ++i; /* a^2-b = (a- sqrt b)(a+ sqrt b) */
}
if(is_complex(t))
{ o[i] = rectangulartopolar; ++i; /* x + iy = r exp(itheta ) */
}
if(flag[LOG] > 1)
{ o[i] = collectlogs; ++i;
if(flag['-'])
{ o[i] = collectlogs2; ++i;
}
}
if(flag[LOGB] > 1)
{ o[i] = collectlogb; ++i;
if(flag['-'])
{ o[i] = collectlogb2; ++i;
}
}
if(flag[LN] > 1)
{ o[i] = collectlns; ++i;
if(flag['-'])
{ o[i] = collectlns2; ++i;
}
}
/* Now for the trig rules */
if(flag[SINSQ] && flag[COSSQ])
{ o[i] = sinsquare1; ++i; /* sin^2 u + cos^2 u = 1 */
o[i] = reversedoublecos1; ++i; /* sin^2 u - cos^2 u = cos 2u */
}
else if(contains(t,SIN) && contains(t,COS) && !sinsquare1(t,zero,&w,buffer))
{ o[i] = sinsquare1; ++i;
/* it can work e.g. on sin^3 x + log x + sin x cos^2 x */
}
if(flag[SINSQ] && flag[ONEFLAG])
{ o[i] = sinsquare2; ++i; /* 1 - sin^2 u = cos^2 u */
o[i] = reversedoublecos2; ++i; /* 1 - 2 sin^2 theta = cos(2theta ) */
}
if(flag[COSSQ] && flag[ONEFLAG])
{ o[i] = sinsquare3; ++i; /* 1 - cos^2 u = sin^2 u */
o[i] = reversedoublecos3; ++i; /* 2 cos^2 theta - 1 = cos(2theta ) */
}
if(flag[SECSQ] && flag[TANSQ])
{ o[i] = secsqminustansq; ++i; /* sec^2 u - tan^2 u = 1 */
}
if(flag[TANSQ] && flag[ONEFLAG])
{ o[i] = tansquare1; ++i; /* tan^2 u + 1 = sec^2 u */
}
if(flag[SECSQ] && flag[ONEFLAG])
{ o[i] = tansquare2; ++i; /* sec^2 u - 1 = tan^2 u */
}
if(flag[CSCSQ] && flag[COTSQ])
{ o[i] = cscsqminuscotsq; ++i; /* csc^2 u - cot^2 u = 1 */
}
if(flag[COTSQ])
{ o[i] = cotsquare1; ++i; /* cot^2 u + 1 = csc^2 u */
}
if(flag[CSCSQ])
{ o[i] = cotsquare2; ++i; /* csc^2 u - 1 = cot^2 u */
}
if(flag[SINHSQ])
{ o[i] = sinhsqtocoshsq; ++i; /* sinh^2u = cosh^2 u - 1 */
o[i] = sinhsqplus1; ++i; /* sinh^2u + 1 = cosh^2u */
}
if(flag[COSHSQ])
{ o[i] = coshsqminussinhsq; ++i;
o[i] = coshsqminus1; ++i; /* cos^2 - 1 = sinh^2 */
}
if(flag[COSHSQ] && flag[SINHSQ])
{ o[i] = coshsqminussinhsq; ++i;
}
if(flag[TANHSQ])
{ o[i] = tanhsqtosechsq; ++i;
o[i] = oneminustanhsq; ++i;
}
if(flag[SECHSQ])
{ o[i] = sechsqtotanhsq; ++i;
o[i] = oneminussechsq; ++i;
}
if(flag[TANHSQ] && flag[SECHSQ])
{ o[i] = tanhsqplussechsq; ++i;
}
if(flag[SIN] > 1)
{ if(status(sumofsin) >= LEARNING)
{ if(n > 2 || flag['-'] != 1)
{ o[i] = sumofsin; ++i;
}
if(flag['-'])
{ o[i] = difofsin; ++i;
}
}
}
if(flag[COS] > 1)
{ if(status(sumofcos) >= LEARNING)
{ if(n > 2 || flag['-'] != 1)
{ o[i] = sumofcos; ++i;
}
if(flag['-'])
{ o[i] = difofcos; ++i;
}
}
}
if(flag[ASIN] && flag[ACOS])
{ o[i] = sumofarcsin; ++i; /* arcsin x + arccos x = pi /2 */
}
if(flag[ATAN] > 1)
{ o[i] = sumofarctan; ++i; /* arctan x + arctan 1/x = pi x/2|x| */
}
if(flag[INFINITYFUNCTOR])
{ o[i] = addinfinity; ++i;
}
if(limitflag2 && status(limrationalfunction) >= KNOWN)
{ o[i] = sumleadingterm; ++i;
}
if(flag[DEFINT]>1)
{ /* There are two or more definite integrals. Check if
they all have the same limits. If two have different limits
but the same integrand, put in 'additivity' */
int z = 0; /* set when we get the first definite integral */
term lo,hi,integrand,x;
for(k=0;k<n;k++)
{ w = ARG(k,t);
if(NEGATIVE(w))
w = ARG(0,w);
if(FUNCTOR(w) != INTEGRAL || ARITY(w) != 4)
continue;
if(z == 0)
{ lo = ARG(2,w);
hi = ARG(3,w);
integrand = ARG(0,w);
x = ARG(1,w);
z = 1;
}
else if(
(!equals(lo,ARG(2,w)) || !equals(hi,ARG(3,w))) && /* different limits */
equals(ARG(0,w),integrand) && equals(ARG(1,w),x) /* same integrand and integration variable */
)
break;
}
if(k < n)
{ o[i] = additivity; ++i;
/* combine two definite integrals with the same integrand */
}
}
/* Next come the factoring operators. In order not to
show too many we need to analyze the exponents occurring
in t. Go through t and put the numerical exponents found,
or the numerical PART of exponents found, if an integer,
into the 'exponents' array. */
memset(exponents,0,sizeof(exponents));
analyze_exponents(t,1,exponents);
/* exponents[j] contains the number of exponents in t
that are multiples of j for j = 2,3,4 (counting
things like 2n as a multiple of 2; also -4 counts
as a multiple of 2, so we can factor x^(-2) - 2 x^(-1) + 1
for example).
exponents[5] contains the number of symbolic exponents
*/
if(n==2)
{ if(get_complex() && exponents[2])
{ o[i] = sumofsquares; ++i; /* a^2+b^2 = (a-bi)(a+bi) */
}
if(get_complex() && equals(ARG(1,t),minusone))
{ o[i] = factorbydemoivre; ++i;
}
if(flag['-'] == 1 && exponents[2])
{ o[i] = differenceofsquares; ++i; /* a^2-b^2 = (a-b)(a+b) */
}
else if(flag['-'] == 1 && numerical(t))
{ /* example, 1/9 - 1/25 */
o[i] = differenceofsquares; ++i;
}
if(flag['-'] == 1 && exponents[7] && !differenceofsquares(t,zero,&w,buffer))
{ /* fractional exponents, show it if it works; example 1-x^(2/3) */
o[i] = differenceofsquares; ++i;
}
if(exponents[2] && status(completethesquare1) >= LEARNING &&
!SOLVETYPE(problemtype)
/* this is only for use in integration, and it confuses
people if it comes up as a choice when they have selected
a sum on one side of an equation. They should select
the whole equation to get the correct completethesquare. */
)
{ o[i] = completethesquare1; ++i;
}
if(exponents[3]) /* can't demand exponents[3]==2 because of x^3-1 */
{ if(flag['-'] == 1)
{ o[i] = differenceofcubes; ++i;
}
else
{ o[i] = sumofcubes; ++i;
}
}
if(exponents[4] && flag['-'] != 1)
{ o[i] = sumoffourthpowers; ++i;
}
if(exponents[4] || exponents[5] || exponents[6])
{ if(flag['-'] == 1)
{ o[i] = differenceofnth2; ++i;
o[i] = differenceofnthpowers; ++i;
}
}
if(exponents[5] || exponents[6])
{ o[i] = sumofnthpowers; ++i;
}
}
else if (n==3)
{ if(exponents[2] && exponents[3]!= 1 &&
/* but don't put in !exponents[4] because of x^4 - 2x^2 + 1 */
/* and not just !exponents[3] because of x^6 - 2x^3 + 1 */
isquadratic(t,&a,&b,&c,&x,&y)
/* don't show these opertions on e.g.
h^2k^2 + 4k^2 +(h^2k+4k) or on
7/a - 2/b + 3a/b^2
*/
)
{ /* quadratics */
o[i] = factorsquareofsum; ++i;
if(flag['-'])
{ o[i] = factorsquareofdif; ++i;
}
o[i] = factorquadraticwithdisplay; ++i;
if(status(factorquadratic) >= LEARNING)
{ o[i] = factorquadratic; ++i;
}
if(status(quadraticformula) >= LEARNING)
{ o[i] = quadraticformula; ++i;
}
if(status(completethesquare1) >= LEARNING &&
!SOLVETYPE(problemtype) /* see comments at
previous occurrence of completethesquare1 */
)
{ o[i] = completethesquare1; ++i;
/* completethesquare is for equations */
}
}
if(exponents[4] && exponents[3]!= 1)
{ o[i] = factorquartic; ++i;
}
}
else if (n==4 && exponents[3])
{ /* cubics */
o[i] = factorcubeofsum; ++i;
o[i] = factorcubeofdif; ++i;
}
else if (n==5 && exponents[4] && exponents[3]!= 1)
{ /* quartics */
o[i] = factor4thofsum; ++i;
o[i] = factor4thofdif; ++i;
}
else if (exponents[6] || exponents[5])
{ /* nth powers */
if(!factornthofdif(t,zero,&w,buffer))
{ o[i] = factornthofdif; ++i;
}
else if(!factornthofsum(t,zero,&w,buffer))
{ o[i] = factornthofsum; ++i;
}
}
if(n > 2)
{ if(nvariables > 0)
{ /* don't show these factoring operators for numerical terms */
o[i] = guessfactor; ++i;
if(mvpoly(t))
{ o[i] = factorbypolydiv; ++i;
}
else if(!makepoly(t,get_eigenvariable(),&w) && ARITY(w) > 3)
{ o[i] = factorbypolydiv; ++i;
/* Example: x^3 + x^2(1-a) + x(4-a) - 4a
is not an mvpoly, but can be factored by
factorbypolydiv
*/
}
if(n > 3)
{ o[i] = factorbygrouping; ++i;
}
}
/* Now we have to consider the possibility that
differenceofsquares applies to a sub-sum. But if
n is large, say 10, it can take a LONG time to
try all the pairs of subterms. And in practice it's
never useful if n is more than 5. */
if(n <= 5)
{ err = arity_aux2(t,zero,&w,buffer,differenceofsquares);
if(!err)
{ o[i] = differenceofsquares; ++i;
}
if(get_complex())
{ err = arity_aux2(t,zero,&w,buffer,sumofsquares);
if(!err)
{ o[i] = sumofsquares; ++i;
}
}
if(exponents[3])
{ err = arity_aux2(t,zero,&w,buffer, differenceofcubes);
if(!err)
{ o[i] = differenceofcubes; ++i;
}
err = arity_aux2(t,zero,&w,buffer, sumofcubes);
if(!err)
{ o[i] = sumofcubes; ++i;
}
}
if(exponents[4])
{ err = arity_aux2(t,zero,&w,buffer, sumoffourthpowers);
if(!err)
{ o[i] = sumoffourthpowers; ++i;
}
}
if(exponents[5] || exponents[6] || exponents[4])
{ err = arity_aux2(t,zero,&w,buffer, differenceofnthpowers);
if(!err)
{ o[i] = differenceofnthpowers; ++i;
}
err = arity_aux2(t,zero,&w,buffer, sumofnthpowers);
if(!err)
{ o[i] = sumofnthpowers; ++i;
}
}
}
}
if(n > 3 && n <= 6)
{ /* allow for the possibility that some operator meant
for arity 3 can work on a sub-sum */
err = arity_aux(t,zero,&w,buffer,factorsquareofsum);
if(!err)
{ o[i] = factorsquareofsum; ++i;
}
err = arity_aux(t,zero,&w,buffer,factorsquareofdif);
if(!err)
{ o[i] = factorsquareofdif; ++i;
}
err = long_quadratic(t,get_eigenvariable(),&a,&b,&c);
/* catch things like x^2 + x + 4 + e^2 */
if(!err)
{ o[i] = quadraticformula; ++i;
}
}
if(nvariables > 0 && exponents[6] && !exponents[5] &&
/* no symbolic exponents, but some exponent more than 4 */
!makepoly(t,get_eigenvariable(),&p) &&
intpolynomial(p)
)
{ if(ARITY(p) <= 25)
/* don't risk running out of space in polygcd. This 25 must
match the number in the operation itself in polyfact.c */
{ o[i] = squarefreefactors; ++i;
}
RELEASE(p);
}
if(numerical_poly(t) && contains(t, '^') && !iscomplex(t))
/* polynomial in one var with numerical coef, and not linear */
{ o[i] = factornumerically; ++i;
}
o[i] = writeassum; ++i;
break;
case '*':
count = 0;
for(k=0;k<n;k++)
{ w = ARG(k,t);
if(FUNCTOR(w)==MATRIX || FUNCTOR(w) == VECTOR)
++count;
}
if(count)
{ if(count > 1)
{ o[i] = multiplymatrices; ++i;
}
break;
}
if(n == 2 && FUNCTOR(ARG(0,t)) == SUM && FUNCTOR(ARG(1,t)) == SUM &&
equals(ARG(3,ARG(0,t)),infinity) && equals(ARG(3,ARG(1,t)),infinity)
)
{ term a,b,x;
x = get_eigenvariable();
o[i] = multiplyseries; ++i;
if(is_power_series(ARG(0,t),x,&a) &&
is_power_series(ARG(1,t),x,&b) &&
equals(a,b)
)
{ o[i] = multiplypowerseries; ++i;
}
}
/* Now there are no matrices or vectors involved */
if(!orderfactors(t,zero,&w,buffer))
/* making a toplevel copy of t and calling sortargs isn't enough
because orderfactors can also rearrange the additive order of
sums which are factors of t, and THEN sortargs. Example:
(3-x)(3+x)(x-5) doesn't change by sortargs but orderfactors makes it
(3-x)(x+5)(x+3) */
{ o[i] = orderfactors; ++i; /* show it only if it works */
}
if(!recip(t,zero,&w,buffer)) /* a (1/a) = 1; but it works also
on 3 (4/5) (1/3) for example, so the
easiest way to test for applicability is
just to try it out. */
{ o[i] = recip; ++i;
}
if(!pullminusout2(t,zero,&w,buffer)) /* a(b-c) = -a(c-b) */
{ o[i] = pullminusout2; ++i;
}
count = 0; /* count degree terms */
if(!writeascube(t,zero,&w,buffer)) /* 27x^3 = (3x)^3 */
{ o[i] = writeascube; ++i;
}
if(problemtype == POWERSERIES && !get_inpowerflag() && get_indenomflag()
&& !writeaspoly(t,get_eigenvariable(),&temp,buffer) && FUNCTOR(temp) == '+' && ARITY(temp) == 2
)
{ o[i] = writeaspoly; ++i;
}
for(k=0;k<n;k++)
{ if(NUMBER(ARG(k,t)))
continue;
if(FUNCTOR(ARG(k,t)) == DEG)
++count;
else
break; /* not a number and not a DEG term */
}
if(k==n && count) /* at least one degree term and all non-degree
terms are numbers */
{ o[i] = multdegrees; ++i;
}
if(is_complex(t))
{ if(!rectangulartopolar(t,zero,&w,buffer))
{ o[i] = rectangulartopolar; ++i;
}
if(!polartorectangular(t,zero,&w,buffer))
{ o[i] = polartorectangular; ++i;
}
}
maxarity = 0;
numberflag = 0;
for(k=0;k<n;k++)
{ v = ARG(k,t);
h = FUNCTOR(v);
if(ISZERO(v))
++flag[ZEROFLAG];
if(ISONE(v))
++flag[ONEFLAG];
++flag[h];
if(h == '+' && ARITY(v) > maxarity)
maxarity = ARITY(v);
if(h == '+' && all_args_negative(v))
flag[ALLNEG] = k+1;
if(NUMBER(v))
++numberflag;
if(h == '^' && NEGATIVE(ARG(1,v)))
++flag[NEGEXP];
if(h == '^' && FUNCTOR(ARG(0,v)) == '+' && isinteger(ARG(1,v)))
{ ++flag['+'];
++flag[POWEROFSUM];
}
}
if(numberflag > 1 && !get_arithflag().negexp)
{ o[i] = weakarithmetic; ++i;
}
if(flag['+'])
{ o[i] = distriblaw; ++i;
}
if(flag['+'] == 1 && flag[POWEROFSUM])
{ o[i] = multiplyout; ++i;
if(status(multiplyoutandsimp) >= LEARNING && !mflag)
{ o[i] = multiplyoutandsimp; ++i;
}
}
if(flag['-'])
{ o[i] = minusintoproduct3; ++i;
}
if(flag['+'] > 1)
{ o[i] = multiplyout; ++i;
if(status(multiplyoutandsimp) >= LEARNING && !mflag)
{ o[i] = multiplyoutandsimp; ++i;
}
if(!difofsquares(t,zero,&w,buffer))
{ o[i] = difofsquares; ++i;
}
if(!makesinpower(t,zero,&w,buffer))
{ o[i] = makesinpower; ++i;
}
if(!makecospower(t,zero,&w,buffer))
{ o[i] = makecospower; ++i;
}
if(maxarity >= 3)
{ o[i] = makedifofcubes; ++i;
o[i] = makesumofcubes; ++i;
}
if(is_complex(t) > 1)
{ o[i] = multiplycomplexconjugates; ++i;
/* (a-bi)(a+bi) = a^2+b^2 */
}
}
if(flag[ZEROFLAG])
{ o[i] = multbyzero; ++i;
}
if(flag[ONEFLAG])
{ o[i] = multbyone; ++i;
}
if(flag[NEGEXP])
{ o[i] = eliminatenegexp; ++i;
}
if(flag['+'] && flag[ALLNEG])
{ if(flag[ALLNEG] == 1)
{ o[i] = bringminusout3; ++i;
/* (-a-b)c = (a+b)c */
}
else
{ o[i] = bringminusout2; ++i;
/* a(-b-c) = -a(b+c) */
}
}
if(flag['-'])
{ o[i] = bringminusout; ++i; /* a(-b) = -ab */
}
/* Conditions for collectpowers are hard to formulate:
not if(flag['^'] > 1) , e.g. x^2 x;
not if(flag['^']), e.g. xyx;
and don't show for 2x^4 or 2pi x^3;
better just check if it will or won't work.
*/
err = rawcollectpowers(t,&u,1);
if(!err)
{ o[i] = collectpowers; ++i;
}
if(flag[0] > 1)
{ o[i] = collectnumbers; ++i;
}
if(flag['/']) /* not only for n == 2, e.g. 3x^2 (1/(3x)) */
{ o[i] = cancelop; ++i;
}
if(flag['/'])
{ o[i] = multiplyfractions2; ++i;
/* must also be able to select the individual fractions */
}
if(flag['/'] > 1)
{ o[i] = multiplyfractions; ++i;
}
if(flag['*'])
{ o[i] = regroupfactors; ++i;
}
if(flag[ABSFUNCTOR])
{ o[i] = multabs; ++i;
}
if(flag[SQRT])
{ o[i] = pushundersqrt; ++i;
}
if(flag[SQRT] > 1)
{ o[i] = productofsqrts; ++i;
}
if(flag[ROOT])
{ o[i] = pushunderoddroot; ++i;
o[i] = pushunderevenroot; ++i;
}
if(flag[ROOT] > 1)
{ o[i] = productofroots; ++i;
}
if(flag[SIN] && flag[COS])
{ if(n >= 3 && equals(ARG(0,t),two))
{ o[i] = reversedoublesin2; ++i; /* 2 sintheta cos theta = sin 2theta */
}
else
{ o[i] = reversedoublesin; ++i; /* sin theta cos theta = (1/2) sin 2 theta */
}
if(status(sincosop) >= LEARNING && problemtype != DIFFERENTIATE)
{ o[i] = sincosop; ++i;
o[i] = cossin; ++i;
}
}
if(flag[SIN] > 1)
{ if(status(sinsin) >= LEARNING)
{ o[i] = sinsin; ++i;
}
o[i] = sintocsc; ++i;
}
if(flag[TAN] > 1)
{ o[i] = tantodenom; ++i;
}
if(flag[COT] > 1)
{ o[i] = cottotan; ++i;
}
if(flag[SEC] > 1)
{ o[i] = secrule; ++i;
}
if(flag[CSC] > 1)
{ o[i] = cscrule; ++i;
}
if(flag[COS] > 1)
{ if(status(coscos) >= LEARNING)
{ o[i] = coscos; ++i;
}
o[i] = costosec; ++i;
}
if(flag['^'] > 1 && !prodofpowers(t,zero,&w,buffer))
{ o[i] = prodofpowers; ++i;
}
if(flag[LOG])
{ o[i] = attractlogs; ++i;
}
if(flag[LN])
{ o[i] = attractlns; ++i;
}
if(flag[LOGB])
{ o[i] = attractlogb2; ++i;
}
if(flag[ABSFUNCTOR] > 1)
{ o[i] = multiplyabsval; ++i; /* |u||v| = |uv| */
}
if(flag[SUM] == 1 && FUNCTOR(ARG((unsigned short)(n-1),t)) == SUM)
{ o[i] = constantintosigma; ++i;
}
if(flag[SUM] > 1)
{ o[i] = productofsigmas; ++i; /* (\sum u)(\sum v) = \sum \sum uv */
}
if(flag[INFINITYFUNCTOR])
{ o[i] = timesinfinity; ++i;
}
if(flag[SG])
{ o[i] = sgtoabs; ++i;
}
if(flag[SG] && flag[ABSFUNCTOR])
{ o[i] = abstimessg; ++i;
}
if(n == 2)
{ o[i] = multcommute; ++i;
if(ISINTEGER(ARG(0,t)) && INTDATA(ARG(0,t)) < 100 &&
FUNCTOR(ARG(1,t)) != '+'
)
{ o[i] = multdef; ++i;
}
}
if(n == 2 && FUNCTOR(ARG(1,t)) == LIMIT &&
POSNUMBER(ARG(0,t)) &&
FUNCTOR(ARG(1,ARG(1,t))) == '*' &&
!limlinear(t,zero,&w,buffer)
)
{ o[i] = limlinear; ++i;
}
if(problemtype == POWERSERIES)
{ select_series_op(t,o+i,&k);
i += k;
}
break;
case '^':
selectexpops(t,o+i,&k);
i += k;
break;
case '/':
selectfractops(t,o+i,&k);
i +=k;
break;
case SQRT:
if(g == '-' || obviously_negative(u))
{ o[i] = sqrtofneg; ++i; /* sqrt(-a) = i sqrt a if a>=0 */
if(
(
get_complex() && is_complex(u) &&
equals(ARG(0,u),complexi)
)
|| !sqrtofminusi(t,zero,&w,buffer)
)
{ o[i] = sqrtofminusi; ++i;
}
}
if(equals(u,complexi))
{ o[i] = sqrtofi; ++i;
}
switch(g)
{ case '*':
if(get_complex() && is_complex(u) && !sqrtofi(t,zero,&w,buffer))
{ o[i] = sqrtofi; ++i;
}
if(contains_integer_factor(u))
{ o[i] = factorundersqrt; ++i;
}
o[i] = sqrtofproduct; ++i;
memset(flag,0,sizeof(flag));
for(k=0;k<ARITY(u);k++)
{ v = ARG(k,u);
++flag[FUNCTOR(v)];
}
if(flag['^'])
{ o[i] = sqrtsimp; ++i;
if(contains(u,'e') && iscomplex(u))
{ o[i] = complexsqrt; ++i;
}
}
break;
case '^':
if(!iscomplex(u))
{ if(equals(ARG(1,u),two))
{ o[i] = sqrtofsquare; ++i;
}
else if(iseven(ARG(1,u)))
{ o[i] = sqrtofpower; ++i;
}
else if(isodd(ARG(1,u)))
{ o[i] = sqrtofpower2; ++i;
}
o[i] = sqrttoabs; ++i;
}
if(equals(ARG(0,u),eulere) && iscomplex(ARG(1,u)))
{ o[i] = complexsqrt; ++i;
}
break;
case '+':
if(mvpoly(u))
{ o[i] = factorpolyundersqrt; ++i;
}
if(get_complex() && is_complex(u) && !complexparts(u,&v,&w))
{ if(obviously_positive(w))
{ o[i] = sqrtofaplusbi; ++i;
}
else if (obviously_negative(w))
{ o[i] = sqrtofaminusbi; ++i;
}
else
{ o[i] = sqrtofaplusbi; ++i;
o[i] = sqrtofaminusbi; ++i;
}
}
break;
case '-':
if(ISONE(ARG(0,u)))
{ o[i] = sqrtofminus1; ++i; /* sqrt(-1) = i */
}
break;
case 0: /* sqrt of a number */
if(INTEGERP(u))
{ o[i] = factorundersqrt; ++i;
/* computeroot has already been thrown in */
}
break;
case '/':
if(!obviously_nonnegative(ARG(1,u)) ||
/* AND would be enough to cover automode here,
since if ONE of num and denom is nonnegative, you
don't need to introduce absolute value. But
students may not realize that and should see both
options. */
!obviously_nonnegative(ARG(0,u))
)
{ o[i] = sqrtofquotient2; ++i;
}
o[i] = sqrtofquotient; ++i;
break;
case SQRT:
o[i] = sqrtofsqrt; ++i;
break;
case ROOT:
o[i] = sqrtofroot; ++i;
break;
case INFINITYFUNCTOR:
o[i] = sqrtinfinity; ++i;
break;
}
if(status(sqrtexp) >= LEARNING)
{ o[i] = sqrtexp; ++i; /* fractional exponents */
}
if( status(rootofpower) > LEARNING)
{ if(get_complex()==0 || seminumerical(t))
{ o[i] = sqrttoroot; ++i;
}
o[i] = sqrttoroot2; ++i;
}
break;
case ROOT:
v = ARG(1,t);
h = FUNCTOR(v);
if(equals(u,two))
{ o[i] = roottosqrt; ++i;
SetStopFlag(stopflag);
*nops = i;
return;
}
if(get_complex() && !iscomplex(v) && !complexrootminus(t,zero,&w,buffer))
{ o[i] = complexrootminus; ++i;
}
if(ISINFINITE(v))
{ o[i] = rootinfinity; ++i;
}
else if(!NEGATIVE(v) && seminumerical(v) && isodd(u))
/* if v is negative, rootofminus will be include below,
so don't duplicate it. */
{ double z;
deval(v,&z);
if(z != BADVAL && z < 0.0)
{ o[i] = rootofminus; ++i;
}
}
switch(h)
{ case 0:
if(INTEGERP(v))
{ o[i] = factorunderroot; ++i;
}
break;
/* evaltorational has been thrown in already */
case '-':
o[i] = rootofminus; ++i; /* ^n sqrt (-a) = -root(n,a), n odd */
break;
case '*':
if(FUNCTOR(v) == '*' && contains_integer_factor(v))
{ o[i] = factorunderroot; ++i;
}
o[i] = rootofproduct; ++i; /* ^n sqrt (xy) = root(n,x) root(n,y)" */
memset(flag,0,sizeof(flag));
for(k=0;k<ARITY(v);k++)
++flag[FUNCTOR(ARG(k,v))];
if(flag['^'])
{ o[i] = rootsimp; ++i; /* root(n,x^ny) = x root(n,y)" */
if(contains(v,'e') && iscomplex(v))
{ o[i] = complexroot; ++i;
}
}
break;
case '^':
if(equals(ARG(0,v),eulere) && iscomplex(ARG(1,v)))
{ o[i] = complexroot; ++i;
}
if(equals(u,ARG(1,v)))
{ o[i] = rootofpower; ++i; /* root(n,x^n) = x if x>=0 */
}
if(iseven(u))
{ o[i] = rootofpower2; ++i; /* root(2n,x^n) = sqrt(x) */
/* also root(mn,x^n) = root(m,x) */
}
if(!ATOMIC(u) ||
(ISINTEGER(u) && !equals(u,two) && !equals(u,three)
&& !equals(u,five) && !equals(u,seven)
)
)
{ o[i] = rootofpower4; ++i; /* root(mn,a^n ) = root(m,a) */
}
o[i] = rootofpower3; ++i; /* ^n sqrt (x^(nm)) = x^m if x >= 0 or n odd */
o[i] = rootsimp; ++i; /* ^n sqrt (x^n y) = x root(n,y) or |x|root(n,y) */
/* Will also work e.g. on root(3,x^7) yielding x^2 root(3,x) */
o[i] = rootofpower5; ++i; /* root(n,x)^m = (root(n,x))^m */
break;
case '+':
if(mvpoly(v))
{ o[i] = factorpolyundersqrt; ++i;
}
/* Don't put in multiplyoutunderroot as they
can select the arg of the root to do that.
Of course the same is true of factorpolyundersqrt
but it may be helpful to suggest that. */
break;
case '/':
o[i] = rootofquotient; ++i;
break;
case SQRT:
o[i] = rootofsqrt; ++i;
break;
case ROOT:
o[i] = rootofroot; ++i;
break;
}
if(status(rootexp) >= LEARNING) /* fractional exponents */
{ if(FUNCTOR(v) == '^')
{ o[i] = rootpowerexp; ++i;
}
o[i] = rootexp; ++i;
}
break;
case MULTIPLICITY:
if(!collectmultiplesolns(t,zero,&w,buffer))
{ o[i] = collectmultiplesolns; ++i;
}
if(!rejecteqn(t,zero,&w,buffer))
{ o[i] = rejecteqn; ++i;
}
break;
case OR:
if(topflag && problemtype == MINMAX)
{ if(!used2(ADDCRITICALPOINTS))
{ o[i] = addcriticalpoints; ++i;
}
if(!used2(ADDENDPOINTS))
{ o[i] = addendpoints; ++i;
}
if(!used2(ADDUNDEFINEDPOINTS))
{ o[i] = addundefinedpoints; ++i;
}
if(!rejectpoint(t,zero,&w,buffer))
{ o[i] = rejectpoint; ++i;
}
if(used2(ADDCRITICALPOINTS) || used2(ADDENDPOINTS) || used2(ADDUNDEFINEDPOINTS))
{ o[i] = tabulate; ++i;
o[i] = tabulateexact; ++i;
}
}
if(SOLVETYPE(problemtype))
{ unsigned short n = ARITY(t);
int j;
unsigned short hh;
term uu;
int count[6]; /* count =, < , LE, >, GE, one empty slot */
memset(count,0,6*sizeof(int));
for(j=0;j<n;j++)
{ uu = ARG(j,t);
hh = FUNCTOR(uu);
if(interval_as_and(uu))
{ count_aux(count,FUNCTOR(ARG(0,uu)));
count_aux(count,FUNCTOR(ARG(1,uu)));
}
else if(INEQUALITY(hh))
count_aux(count,hh);
if(FUNCTOR(uu) == '=' && equals(ARG(0,uu),ARG(1,uu)))
{ o[i] = trueeqn; ++i;
}
if(equals(uu, trueterm))
{ o[i] = trueeqn; ++i;
}
}
if(count[0] && count[1])
{ o[i] = lessthantole; ++i;
}
if(count[0] && count[3])
{ o[i] = greaterthantoge; ++i;
}
x = get_eigenvariable();
for(j=0;j<n;j++)
{ if(!solved(ARG(j,t),x))
break;
}
if(j==n) /* all solved already */
{ o[i] = checkroot; ++i;
if(contains(t,PI_ATOM))
{ term *atomlist;
int nvars = integer_parameters(t,&atomlist);
free2(atomlist);
if(nvars)
{ o[i] = periodicform; ++i;
}
}
}
if(!collectmultiplesolns(t,zero,&w,buffer))
{ o[i] = collectmultiplesolns; ++i;
}
if(get_complex() && contains_existentials(t) &&
!contains_defined_variables(t) &&
!explicitparams(t,zero,&w,buffer)
)
{ o[i] = explicitparams; ++i;
}
}
if(!combineintervals(t,zero,&w,buffer))
{ o[i] = combineintervals; ++i;
}
if(!get_complex() && !introduceabs(t,zero,&w,buffer))
{ o[i] = introduceabs; ++i;
}
break;
case AND:
if(interval_as_and(t))
{ selectops1(binderflag,ARG(0,t),o+i,&k);
i += k;
selectintervalops(t,o+i,&k);
i += k;
*nops = i;
SetStopFlag(stopflag);
return;
}
if(!LINEUP(t) && contains(t, '='))
{ o[i] = varsleft; ++i;
}
else if(LINEUP(t) && currenttopic == _eqns_by_adding_eqns)
/* only show these operators when solving equations
by add-and-subtract */
{ o[i] = addtwoeqns; ++i;
o[i] = subtwoeqns; ++i;
o[i] = muleqns; ++i;
o[i] = diveqns; ++i;
o[i] = addmuleqns; ++i;
}
/* Add the next operations regardless of the topic */
o[i] = swapeqns; ++i;
if(!impossibleeqns(t,zero,&w,buffer))
{ o[i] = impossibleeqns; ++i;
}
o[i] = dropeqn; ++i;
/* Only show regardvarasconst when there are more variables
than equations. Don't count parameters as variables. */
if(get_nvariables() - get_nparameters() > ARITY(t))
{ o[i] = regardvarasconst; ++i;
}
if(status(matrixform) >= LEARNING && contains(t, '='))
{ o[i] = matrixform; ++i;
}
if(currenttopic == _cramers_rule)
{ o[i] = cramersrule; ++i;
}
if(contains(t,DET))
{ o[i] = evaluatedeterminant; ++i;
}
break;
case ASIN:
if(numerical(u) && !inexact(u))
{ o[i] = evalarcsin; ++i;
}
switch(g)
{ case '-':
o[i] = arcsinodd; ++i;
break;
case SIN:
o[i] = asinsin; ++i;
break;
}
break;
case ACOS:
if(numerical(u) && !inexact(u))
{ o[i] = evalarccos; ++i;
}
switch(g)
{ case '-':
o[i] = arccosodd; ++i;
break;
case COS:
o[i] = acoscos; ++i;
break;
}
break;
case ATAN:
if(problemtype == POWERSERIES)
{ o[i] = atanseries; ++i;
o[i] = atanseries2; ++i;
o[i] = atanseries3; ++i;
}
if(numerical(u) && !inexact(u))
{ o[i] = evalarctan; ++i;
}
if(ISINFINITE(u))
{ o[i] = ataninfinity; ++i;
}
switch(g)
{ case '-':
o[i] = arctanodd; ++i;
break;
case TAN:
o[i] = atantan; ++i; /* arctan(tan theta ) = theta if -pi /2<=theta <=pi /2 */
/* atantan2 is handled by dowith, rather than here */
break;
}
break;
case ACOT:
o[i] = evalarccot; ++i; /* arccot x = arctan(1/x) */
if(equals(u,minusinfinity))
{ o[i] = acotminusinfinity; ++i;
}
else if(equals(u,infinity))
{ o[i] = acotinfinity; ++i;
}
break;
case ASEC:
o[i] = evalarcsec; ++i; /* arcsec x = arccos (1/x) */
if(ISINFINITE(u))
{ o[i] = asecinfinity; ++i;
}
break;
case ACSC:
o[i] = evalarccsc; ++i; /* arccsc x = arcsin (1/x) */
if(ISINFINITE(u))
{ o[i] = acscinfinity; ++i;
}
break;
case COSH:
if(ISINFINITE(u))
{ o[i] = coshinfinity; ++i;
}
o[i] = coshdef; ++i;
if(ZERO(u))
{ o[i] = cosh0; ++i;
}
if(equals(u,complexi))
{ o[i] = coshi; ++i;
}
switch(FUNCTOR(u))
{ case '-':
o[i] = cosheven; ++i;
break;
case '+':
o[i] = coshsum; ++i;
break;
case '/':
if(iscomplex(ARG(0,u)))
{ o[i] = coshi; ++i;
}
break;
case ACOSH:
o[i] = coshacosh; ++i;
break;
case '*':
if(!cancel(u,two,&cancelled,&trash))
{ o[i] = doublecosh; ++i;
}
if(iscomplex(u))
{ o[i] = coshi; ++i;
}
break;
}
break;
case SINH:
if(ISINFINITE(u))
{ o[i] = sinhinfinity; ++i;
}
o[i] = sinhdef; ++i;
if(equals(u,complexi))
{ o[i] = sinhi; ++i;
}
if(ZERO(u))
{ o[i] = sinh0; ++i;
}
switch(FUNCTOR(u))
{ case '-':
o[i] = sinhodd; ++i;
break;
case '+':
o[i] = sinhsum; ++i;
break;
case '/':
if(iscomplex(ARG(0,u)))
{ o[i] = sinhi; ++i;
}
break;
case ASINH:
o[i] = sinhasinh; ++i;
break;
case '*':
if(iscomplex(u))
{ o[i] = sinhi; ++i;
}
if(!cancel(u,two,&cancelled,&trash))
{ o[i] = doublesinh; ++i;
}
break;
}
break;
case TANH:
if(ISINFINITE(u))
{ o[i] = tanhinfinity; ++i;
}
o[i] = tanhdef; ++i;
if(FUNCTOR(u) == LN)
{ o[i] = tanhln; ++i;
}
if(FUNCTOR(u) == ATANH)
{ o[i] = tanhatanh; ++i;
}
break;
case COTH:
o[i] = cothdef; ++i;
if(equals(u,complexi))
{ o[i] = cothi; ++i;
}
if(FUNCTOR(u) == '/' && iscomplex(ARG(0,u)))
{ o[i] = cothi; ++i;
}
if(FUNCTOR(u) == ACOTH)
{ o[i] = cothacoth; ++i;
}
if(FUNCTOR(u) == '*' && iscomplex(u))
{ o[i] = cothi; ++i;
}
break;
case SECH:
o[i] = sechdef; ++i;
if(FUNCTOR(u) == ASECH)
{ o[i] = sechasech; ++i;
}
break;
case CSCH:
o[i] = cschdef; ++i;
if(FUNCTOR(u) == ACSCH)
{ o[i] = cschacsch; ++i;
}
break;
case ACOSH:
o[i] = acoshtoln; ++i;
break;
case ATANH:
o[i] = atanhtoln; ++i;
break;
case ASINH:
o[i] = asinhtoln; ++i;
break;
case DET:
if(seminumerical(t))
{ o[i] = evaluatedeterminant; ++i;
}
break;
case SG:
if(NEGATIVE(ARG(0,t)))
{ o[i] = sgodd; ++i;
}
if(ZERO(ARG(0,t)))
{ o[i] = sgzero; ++i;
}
else if(obviously_positive(ARG(0,t)))
{ o[i] = sgpos; ++i;
}
else if(obviously_negative(ARG(0,t)))
{ o[i] = sgneg; ++i;
}
else
{ o[i] = sgpos; ++i;
o[i] = sgneg; ++i;
o[i] = sgabs1; ++i;
o[i] = sgabs2; ++i;
}
if(FUNCTOR(ARG(0,t)) == '*')
{ o[i] = sgprod1; ++i;
o[i] = sgprod2; ++i;
}
else if(FRACTION(ARG(0,t)))
{ if(ONE(ARG(0,ARG(0,t))))
{ o[i] = sgrecip2; ++i;
}
else if(obviously_positive(ARG(0,ARG(0,t))))
{ o[i] = sgrecip3; ++i;
}
else
{ o[i] = sgfract1; ++i;
o[i] = sgfract2; ++i;
}
}
else if(FUNCTOR(ARG(0,t)) == '^')
{ o[i] = sgpower; ++i;
}
break;
case ABSFUNCTOR:
if(is_complex(u))
{ o[i] = complexabs; ++i; /* |u + vi| = sqrt (u^2 + v^2) */
if(FUNCTOR(u) == '^' && equals(ARG(0,u),eulere))
{ o[i] = absofpolar; ++i; /* |e^(itheta )| = 1 */
}
}
if(problemtype == INTEGRATION)
{ o[i] = abssg; ++i; /* abs(x) = x sg x */
}
o[i] = abspos; ++i;
o[i] = absneg; ++i;
o[i] = absposandassume; ++i;
if(ATOMIC(u))
break;
switch(g)
{ case '*':
o[i] = abslinear; ++i;
o[i] = absofproduct; ++i;
break;
case '/':
o[i] = abslinearquo; ++i;
o[i] = absoffraction; ++i;
break;
case '^':
o[i] = abspower; ++i;
break;
case SQRT:
o[i] = abssqrt; ++i;
break;
case ROOT:
o[i] = absroot; ++i;
break;
}
break;
case MATRIXINVERSE:
if(n==2)
{ o[i] = twobytwoinverse; ++i;
}
if(numerical(t))
{ o[i] = exactmatrixinverse; ++i;
}
if(seminumerical(t))
{ o[i] = decimalmatrixinverse; ++i;
}
break;
case MATRIX:
if(get_problemtype() == MINMAX)
{ if(!used2(ADDLIMITS))
{ o[i] = addlimits; ++i;
}
o[i] = selectmax; ++i;
o[i] = selectmin; ++i;
}
else
{ o[i] = multbymatrixidentity; ++i;
}
if(!rejectpoint(t,zero,&w,buffer))
{ o[i] = rejectpoint; ++i;
}
break;
case BINOMIAL:
if(INTEGERP(u) && ISINTEGER(ARG(1,t)))
{ o[i] = binomialcoeftofactorials; ++i;
}
if(numerical(t))
{ o[i] = evaluatebinomialcoef; ++i;
}
break;
case FACTORIAL:
if(INTEGERP(u))
{ o[i] = evaluatefactorial; ++i;
o[i] = defnoffactorial; ++i;
}
if(!ZERO(u))
{ o[i] = factorialrecursion; ++i;
}
break;
case LOG:
if(status(logtoln) >= LEARNING)
{ o[i] = logtoln; ++i;
}
if(ISONE(u))
{ o[i] = logofone; ++i;
break;
}
if(equals(u,ten))
{ o[i] = logoften; ++i;
}
if(seminumerical(u))
{ o[i] = computelog; ++i;
}
if(INTEGERP(u) && !factoroutbase10(t,zero,&w,buffer))
{ o[i] = factoroutbase10; ++i;
}
o[i] = changebase; ++i;
switch(g)
{ case '^':
if(equals(ARG(0,u),ten))
{ o[i] = logofpowerof10; ++i;
}
else
{ o[i] = logofpower; ++i;
}
break;
case '*':
o[i] = logofproduct; ++i;
break;
case ABSFUNCTOR:
if(FUNCTOR(ARG(0,u)) == '*')
{ o[i] = logofproduct; ++i;
}
if(FRACTION(ARG(0,u)))
{ u = ARG(0,u);
if(ISONE(ARG(0,u)))
{ o[i] = logofreciprocal; ++i;
if(status(logofreciprocal) <= LEARNING)
{ o[i] = logofquotient; ++i;
}
}
else
{ o[i] = logofquotient; ++i;
}
}
break;
case SQRT:
o[i] = logsqrt; ++i;
break;
case ROOT:
o[i] = logroot; ++i;
break;
case '/':
if(ISONE(ARG(0,u)))
{ o[i] = logofreciprocal; ++i;
if(status(logofreciprocal) <= LEARNING)
{ o[i] = logofquotient; ++i;
}
}
else
{ o[i] = logofquotient; ++i;
o[i] = logrecip; ++i;
}
break;
}
break;
case LN:
if(problemtype == POWERSERIES)
{ if(FUNCTOR(u) == '+' && ARITY(u) == 2 &&
ONE(ARG(0,u)) && NEGATIVE(ARG(1,u))
)
{ o[i] = lnseries; ++i;
o[i] = lnseries2; ++i;
o[i] = lnseries3; ++i;
}
else if(FUNCTOR(u) == '+' && ARITY(u) == 2 &&
(ONE(ARG(0,u)) || ONE(ARG(1,u)))
)
{ o[i] = lnseriesminus; ++i;
o[i] = lnseriesminus2; ++i;
o[i] = lnseriesminus3; ++i;
}
}
if(get_currenttopic() == _change_log_base)
{ o[i] = lntolog; ++i;
o[i] = changebase; ++i;
}
if(ISONE(u))
{ o[i] = lnofone; ++i;
break;
}
else if(equals(u,complexi))
{ o[i] = lnofi; ++i;
}
else if(iscomplex(u))
{ if(!complexln(t,zero,&w,buffer))
{ o[i] = complexln; ++i;
}
else
{ o[i] = complexlntopolarform; ++i;
}
}
else if(equals(u,eulere))
{ o[i] = lnofe; ++i;
}
if(seminumerical(u))
{ o[i] = computeln; ++i;
}
switch(g)
{ case '^':
if(equals(ARG(0,u),eulere))
{ o[i] = lnofpowerofe; ++i;
}
else
{ o[i] = lnofpower; ++i;
}
break;
case '-':
if(ISONE(ARG(0,u)))
{ o[i] = lnofminusone; ++i;
}
else
{ o[i] = lnofnegative; ++i;
}
break;
case '*':
o[i] = lnofproduct; ++i;
if(iscomplex(u) && contains(u,'^') && contains(u,'e'))
{ o[i] = complexln; ++i; /* ln(re^(itheta )) = ln r + itheta (-pi <theta <=pi ) */
}
break;
case '+':
if(iscomplex(u))
{ o[i] = complexlntopolarform; ++i;
/* ln(u+iv) = ln(re^(itheta )) */
}
break;
case ABSFUNCTOR:
if(FUNCTOR(ARG(0,u)) == '*')
{ o[i] = lnofproduct; ++i;
}
if(FRACTION(ARG(0,u)))
{ u = ARG(0,u);
if(ISONE(ARG(0,u)))
{ o[i] = lnofreciprocal; ++i;
if(status(lnofreciprocal) <= LEARNING)
{ o[i] = lnofquotient; ++i;
}
}
else
{ o[i] = lnofquotient; ++i;
}
}
break;
case SQRT:
o[i] = lnsqrt; ++i;
break;
case ROOT:
o[i] = lnroot; ++i;
break;
case '/':
if(ISONE(ARG(0,u)))
{ o[i] = lnofreciprocal; ++i;
if(status(lnofreciprocal) <= LEARNING)
{ o[i] = lnofquotient; ++i;
}
}
else
{ o[i] = lnofquotient; ++i;
o[i] = lnrecip; ++i;
}
break;
}
if(equals(u,infinity))
{ o[i] = lninfinity; ++i;
}
if(ZERO(u))
{ o[i] = lnzero; ++i;
}
o[i] = lnofpowerreverse; ++i; /* ln u = (1/?) ln u^? */
/* ln u = (1/a) ln u^a , needed to differentiate
ln x from the definition of derivative */
break;
case LOGB:
if(equals(u,eulere))
{ o[i] = logbtoln; ++i;
}
else
{ o[i] = logbtolns; ++i;
}
if(equals(u,ten))
{ o[i] = logbtolog; ++i;
}
else
{ o[i] = logbtologs; ++i;
}
if(INTEGERP(u) && !factoroutbase(t,zero,&w,buffer))
{ o[i] = factoroutbase; ++i;
}
o[i] = changebase; ++i;
v = ARG(1,t);
if(ISONE(v))
{ o[i] = logbofone; ++i;
break;
}
if(equals(v,u))
{ o[i] = logbofb; ++i;
break;
}
if(INTEGERP(u))
{ o[i] = factorlogbase; ++i; /* factor base: log(4,x) = log(2^2,x) */
}
else if(FUNCTOR(u) == '^' ||
( FRACTION(u) && equals(ARG(1,u),v)) || /* as in: log(1/3,3) */
( FRACTION(u) && FUNCTOR(ARG(1,u)) == '^')
)
{ o[i] = logpowerofbofb; ++i;
}
switch(FUNCTOR(v))
{ case '^':
if(equals(ARG(0,v),u))
{ o[i] = logbofpowerofb; ++i;
/* logb b^n = n */
}
else
{ o[i] = logbofpower; ++i;
/* logb a^n = n logb a */
}
break;
case '*':
o[i] = logbofproduct; ++i;
break;
case ABSFUNCTOR:
if(FUNCTOR(ARG(0,u)) == '*')
{ o[i] = logbofproduct; ++i;
}
if(FRACTION(ARG(0,u)))
{ u = ARG(0,u);
if(ISONE(ARG(0,u)))
{ o[i] = logbofreciprocal; ++i;
if(status(logbofreciprocal) <= LEARNING)
{ o[i] = logbofquotient; ++i;
}
}
else
{ o[i] = logbofquotient; ++i;
}
}
break;
case SQRT:
o[i] = logsqrt; ++i;
break;
case ROOT:
o[i] = logroot; ++i;
break;
case '/':
if(ISONE(ARG(0,v)))
{ o[i] = logbofreciprocal; ++i;
if(status(logbofreciprocal) <= LEARNING)
{ o[i] = logbofquotient; ++i;
}
}
else
{ o[i] = logbofquotient; ++i;
o[i] = logbrecip; ++i;
}
break;
}
break;
case SUM:
if(!equals(ARG(3,t),infinity) && !equals(ARG(2,t),minusinfinity))
/* a finite sum */
{ o[i] = sigmatosum; ++i; /* expand sigma notation */
o[i] = showfirstterms; ++i;
o[i] = splitofflastterm; ++i;
if(!contains(u,FUNCTOR(ARG(1,t))))
{ o[i] = sigmaconstant; ++i;
}
nfree = numerical_sum(t);
if(nfree == 2)
{ o[i] = evaluatesigmatorational; ++i;
o[i] = evaluatesigmatodecimal; ++i;
}
else if(nfree == 1)
{ o[i] = evalpuresigmatorational; ++i;
o[i] = evalpuresigmatodecimal; ++i;
}
if(equals(u,ARG(1,t))) /* sum(i,i,...) */
{ o[i] = sumofi; ++i;
}
if(ATOMIC(u))
{ o[i] = shiftindex; ++i;
o[i] = sumtodifofsums; ++i;
if(!ZERO(ARG(2,t)))
{ o[i] = sumtodifofsums0;++i;
}
break;
}
switch(g)
{ case '-':
o[i] = minusoutofsigma; ++i;
break;
case '/':
case '*':
twoparts(u,ARG(1,t),&c,&s);
if(!ONE(c))
{ o[i] = constantoutofsigma; ++i;
}
break;
case '+':
o[i] = sigmasum; ++i;
if(ARITY(u) == 2 && !telescopingsum(t,zero,&w,buffer))
{ o[i] = telescopingsum; ++i;
}
break;
case '^':
if(equals(ARG(1,u),two) && equals(ARG(0,u),ARG(1,t)))
{ o[i] = sumofisquared; ++i;
}
if(equals(ARG(1,u),three) && equals(ARG(0,u),ARG(1,t)))
{ o[i] = sumoficubed; ++i;
}
if(equals(ARG(1,u),four) && equals(ARG(0,u),ARG(1,t)))
{ o[i] = sumofitothefourth; ++i;
}
if(equals(ARG(1,u),ARG(1,t)))
{ o[i] = sumofallpowers; ++i;
}
break;
case DIFF:
o[i] = reversedifsigma; ++i;
break;
case INTEGRAL:
o[i] = reverseintsigma; ++i;
break;
}
o[i] = shiftindex; ++i;
o[i] = sumtodifofsums; ++i;
if(!ZERO(ARG(2,t)))
{ o[i] = sumtodifofsums0;++i;
}
}
else
{ select_series_op(t,o+i,&k);
i+=k;
break;
}
break;
case SIN:
case TAN:
case COS:
case SEC:
case COT:
case CSC:
select_trig_ops(t,o+i,&k); /* see file tselect.c */
i += k;
if(problemtype == POWERSERIES)
{ select_series_op(t,o+i,&k);
i += k;
}
break;
case RIEMANNZETA:
if(problemtype == POWERSERIES)
{ select_series_op(t,o+i,&k);
i+=k;
}
break;
case PR:
o[i] = primerule; ++i;
break;
case DIFF:
if(currenttopic == _diff_from_def ||
currenttopic== _diff_exp_from_defn
)
{ o[i] = defnofderivative; ++i;
*nops = i; /* Don't show the differentiation formulas */
SetStopFlag(stopflag);
return;
}
if(ARITY(t) == 2)
{ select_dif_op(t,o,nops);
SetStopFlag(stopflag);
return;
}
else /* a higher derivative term */
{ if(equals(ARG(1,t),two))
{ o[i] = difdif; ++i;
}
else
{ o[i] = difdifn; ++i;
}
}
break;
case LIMIT:
select_limit_op(t,o,nops);
SetStopFlag(stopflag);
return;
case EVAL:
o[i] = evalbar; ++i;
if(FUNCTOR(ARG(0,t)) == LN)
{ o[i] = evalbarln; ++i;
}
*nops = i;
SetStopFlag(stopflag);
return;
case INTEGRAL:
select_integration_op(t,o+i,&k);
*nops = i+k;
SetStopFlag(stopflag);
return;
case DEG:
o[i] = degtorad; ++i; /* change degrees to radians */
break;
case VECTOR:
if(!contains(t, '='))
/* VECTOR can be used for a set of equations in which case
it's not sensible to multiply by the identity */
{ o[i] = multbyidentity; ++i; /* A = IA */
}
break;
}
if(TRIGFUNCTOR(f) &&
(contains(u,PI_ATOM)
||
(
(seminumerical(u) && !contains(u,DEG) && !(equals(u,pi_term)))
|| (FRACTION(u) && equals(ARG(0,u),pi_term))
)
)
)
{ o[i] = mod2pi; ++i;
/* observe in sin(x + 2pi), x + 2pi isn't seminumerical */
/* Don't show this operator when the arg is in degrees */
/* And don't show it for pi/2, pi_term/4, etc. */
}
if(DEFINED_FUNCTION(f))
{ int k = is_defined(f);
if(well_founded(k) && numerical(t))
{ o[i] = evalfunction; ++i;
}
switch(k)
{ case 0:
o[i] = applyfunction0; ++i;
break;
case 1:
o[i] = applyfunction1; ++i;
break;
case 2:
o[i] = applyfunction2; ++i;
break;
case 3:
o[i] = applyfunction3; ++i;
break;
case 4:
o[i] = applyfunction4; ++i;
break;
case 5:
o[i] = applyfunction5; ++i;
break;
case 6:
o[i] = applyfunction6; ++i;
break;
case 7:
o[i] = applyfunction7; ++i;
break;
case 8:
o[i] = applyfunction8; ++i;
break;
case 9:
o[i] = applyfunction9; ++i;
break;
case 10:
o[i] = applyfunction10; ++i;
break;
case 11:
o[i] = applyfunction11; ++i;
break;
case 12:
o[i] = applyfunction12; ++i;
break;
case 13:
o[i] = applyfunction13; ++i;
break;
case 14:
o[i] = applyfunction14; ++i;
break;
case 15:
o[i] = applyfunction15; ++i;
break;
}
}
if(!(f == '^' && FRACTION(ARG(1,t))) &&
!backtosqrts(t,zero,&w,buffer)
)
{ release(sqrtexp); /* inhibited by backtosqrts */
o[i] = exponenttosqrt; ++i;
}
if(get_complex() && !NEGATIVE(t) && obviously_negative(t))
{ o[i] = minustopolar; ++i;
}
*nops = i;
SetStopFlag(stopflag);
return;
}
/*__________________________________________________________________*/
static void selectops2(unsigned short f, term t, actualop *o, int *nops)
/* Fill pre-allocated array o with the operators that
could be applied to t when t occurs as an argument of a term with
functor f; that is, f is the parent functor of t. List only
operations that are NOT returned by selectops1 for t.
Return the dimension of the array in *nops.
*/
{ unsigned short g = FUNCTOR(t);
unsigned short n = ARITY(t);
int i=0,k,logflag,logpowerflag,expflag,negpowerflag,trigflag[6];
term u,v;
switch(f)
{ case TAN:
case SIN:
case COS:
case CSC:
case COT:
case SEC:
case TANH:
case SINH:
case COSH:
case SECH:
case CSCH:
case COTH:
if(g == '*' && n == 2 && INTEGERP(ARG(0,t)) && ISATOM(ARG(1,t)))
{ o[i] = decrementtrigarg; ++i;
o[i] = querytrigarg; ++i;
}
break;
case LOGB:
if(FUNCTOR(t) == '^')
{ u = ARG(0,t);
v = ARG(1,t);
if(!constant(v) && !equals(u,ten) && !equals(u,eulere) &&
!contains(v,LOGB) /* in that case selectops1 will throw it in */
)
{ o[i] = introducelogbinexponent; ++i;
}
}
break;
case '=':
if(g == '+')
{ term line = history(get_currentline());
if(FUNCTOR(line) == '=' &&
(ZERO(ARG(0,line)) || ZERO(ARG(1,line))) &&
!ispolyin(t,get_eigenvariable())
)
{ o[i] = makesubstitution; ++i;
}
}
break;
case LIMIT:
switch(g)
{ case '+':
if(contains_sqrt(t))
{ o[i] = rationalizenum; ++i;
}
break;
case '*':
logflag = expflag = negpowerflag = logpowerflag = 0;
memset(trigflag,0,6*sizeof(int));
for(k=0;k<n;k++)
{ u = ARG(k,t);
switch(FUNCTOR(u))
{ case LN:
++logflag;
break;
case '^':
if(equals(ARG(0,u),eulere) && !constant(ARG(1,u)))
++expflag;
else if(NEGATIVE(ARG(1,u)) && constant(ARG(1,u)) && !constant(ARG(0,u)))
++negpowerflag;
else if(FUNCTOR(ARG(0,u)) == LN && !constant(ARG(0,ARG(0,u))))
++logpowerflag;
break;
case SIN:
++trigflag[0];
break;
case COS:
++trigflag[1];
break;
case SEC:
++trigflag[2];
break;
case TAN:
++trigflag[3];
break;
case CSC:
++trigflag[4];
break;
case COT:
++trigflag[5];
break;
}
}
if(logflag)
{ o[i] = isolateln; ++i;
}
if(logpowerflag)
{ o[i] = isolatelnpower; ++i;
}
if(negpowerflag)
{ o[i] = negexptodenom; ++i;
}
if(expflag)
{ o[i] = exptodenom; ++i;
}
if(trigflag[0])
{ o[i] = sintocsc; ++i;
}
if(trigflag[1])
{ o[i] = costosec; ++i;
}
if(trigflag[2])
{ o[i] = secrule; ++i; /* sec x = 1/cos x */
}
if(trigflag[3])
{ o[i] = tantodenom; ++i; /* tan x = 1/cot x */
o[i] = tanrule; ++i; /* tan = sin /cos */
}
if(trigflag[4])
{ o[i] = cscrule; ++i; /* csc = 1/sin */
}
if(trigflag[5])
{ o[i] = cottotan; ++i; /* cot = 1/tan */
o[i] = cottosincos; ++i; /* cot = cos/sin */
o[i] = cottocscsec; ++i; /* cot = csc/sec */
}
break;
}
break;
}
*nops = i;
}
/*__________________________________________________________________*/
static int remove_duplicate_ops(actualop *o, int k)
/* given an array of actualops of dimension k,
remove duplicates, condensing the array, and
return the final dimension. Don't do anything
fancy because the dimension is usually going to
be less than 10, maybe at the extreme 20, and we
would like the original order of the non-duplicated
initial segment preserved if possible. */
{ int i,j;
int *workspace;
if(k==0)
return 0;
workspace = callocate(k,sizeof(int));
if(!workspace)
nospace();
for(i=0;i<k;i++)
{ /* mark all duplicates of o[i] further on */
if(workspace[i])
continue; /* this one's already a duplicate */
for(j=i+1;j<k;j++)
{ if( (void *) o[j] == (void *) o[i])
workspace[j] = 1;
}
}
j=0;
for(i=0;i<k;i++)
{ if(!workspace[i])
{ if(j != i)
o[j] = o[i];
j++;
}
}
return j;
}
/*__________________________________________________________________*/
void selectops(ltermlist *selected, actualop *o, int *nops)
/* given the list of selected terms, build an array of
relevant operators in o, and return its dimension in nops.
'active' is passed as history[activeline], it is the 'context' in
which the operators will be applied. */
{ ltermlist *marker;
term t;
unsigned short f;
pathlist *q;
int k,j,count,nbinders,binderflag;
int saveeigen = get_eigenindex();
int savecomdenomflag = get_polyvalcomdenomflag();
term active = history(get_activeline());
topflag = ctestflag = 0;
if(selected->next == NULL)
/* only one term is selected (the normal situation) */
{ if(selected->data.path == NULL)
{ topflag = 1;
ctestflag = 1;
}
else if(
(selected->data.path->functor == AND || selected->data.path->functor == OR) &&
selected->data.path->next == NULL &&
get_problemtype() != MINMAX
)
topflag = 1;
else if( INEQUALITY(selected->data.path->functor) &&
selected->data.path->next == NULL
)
{ int n = selected->data.path->data ? 0 : 1;
term tt = history(get_currentline());
topflag = ZERO(ARG(n,tt)) ? 1 : 0;
}
else
{ for(q = selected->data.path;q->next;q = q->next)
{ if(q->functor != '-' || q->functor != '*')
break;
}
if(q->next == NULL || q->functor == INTEGRAL)
ctestflag = 1;
}
}
k=count=0; /* if this is included in the initialization of
the for-loop, Borland screws up the compilation */
for(marker = selected; marker; marker=marker->next, ++count)
{ t = abstract(marker->data);
limitflag2 = ok_sumleadingterm(marker->data.path);
if(
(FUNCTOR(t) == AND || FUNCTOR(t) == OR) &&
marker->data.path &&
marker->data.path->next &&
marker->data.path->next->data < 0
)
{ /* subrange of a system of equations or inequalities selected */
/* Don't include all the things you could do to the whole system */
/* There is only one thing you can do: combine a < b and a= b to a <= b, etc. */
if(contains(t,'<') && contains(t,'='))
{ o[k] = lessthantole; ++k;
}
if(contains(t,'>') && contains(t,'='))
{ o[k] = greaterthantoge; ++k;
}
}
else if(!topflag && (binderflag = contains_binders(active)) != 0)
{ /* there are binders on the path to t. Specifically
binderflag is now 1 if there was a true binder, that is
a LIMIT, definite INTEGRAL, or SUM, and -1 if there was
an indefinite integral or DIFF.
If binderflag is 1, we must call fill_binders before
selectops1 and release_binders afterwards, otherwise e.g. in
an indexed sum containing x^k we won't be able to infer x is
nonzero. binders_on_path makes the fillbinders call(s) but
afterwards we must call releasebinders here.
Even for indefinite integrals and derivatives, as well as
for true binders, we must reset the eigenvariable, which is
set by binders_on_path.
*/
nbinders = binders_on_path(marker->data, active);
if(on_path(marker->data.path,'/'))
set_polyvalcomdenomflag(1);
/* so polyvalop will use it when called on the ShowStep
focus while constructing the Term Selection Menu */
selectops1(binderflag,t,o+k,&j);
set_eigenvariable(saveeigen);
k+=j;
set_polyvalcomdenomflag(savecomdenomflag);
for(j=0;j<nbinders;j++)
releasebinders();
}
else
{ if(on_path(marker->data.path,'/'))
set_polyvalcomdenomflag(1);
/* so polyvalop will use it when called on the ShowStep
focus while constructing the Term Selection Menu */
selectops1(0,t,o+k,&j);
k+=j;
set_polyvalcomdenomflag(savecomdenomflag);
}
}
if(k > 0) // befoe 1.1.25 it was count > 1 && k > 0)
*nops = remove_duplicate_ops(o,k);
else if(selected->data.path)
/* Not the entire line is selected, the selected term has a parent */
{ for(q = selected->data.path; q->next; q=q->next)
; /* empty body; this finds the last of the list */
f = q->functor; /* parent functor of the selected term */
selectops2(f,t,o+k,&j);
*nops = k+j;
if(f == AND &&
selected->data.path->data == 0 &&
selected->data.path->functor == VECTOR &&
get_problemtype() == RELATED_RATES &&
!contains(t,DIFF)
)
/* all these conditions are there so as not to
let them differentiate the equations true for only one time.
See relrate.mhw for a review of the structure of these problems.
*/
{ o[k+j] = difeqn; ++ *nops;
}
}
else
*nops = k; /* selectops1 doesn't put any duplicates in */
f = FUNCTOR(selected->data);
if(selected->data.path == NULL && get_nextdefn() > 0 &&
!interval_as_and(t) &&
!INEQUALITY(f) && /* in that case unwinddefinition has already been put in */
contains_defined_variables(t)
)
{ /* whole line is selected */
o[*nops] = unwinddefinition;
++ *nops;
}
}
/*_________________________________________________________*/
static void analyze_exponents(term t, int flag, int *exponents)
/* t is a sum, exponents a zero-filled array
at entrance, of dimension at least 7. At exit,
exponents[j] contains the number of exponents in the
summands of t that are multiples of j for j = 2,3,4 (counting
things like 2n as a multiple of 2). Also -4 counts
as a multiple of 2, etc.
exponents[5] contains the number of nonnumerical exponents.
exponents[6] contains the number of numerical exponents > 4.
exponents[7] contains the number of fractional exponents.
The function works on some other functors because it is
written recursively, but it is called by other
functions only on sums.
If the input flag is nonzero, then call it recursively on the
args of sums. If flag is zero, don't go into sums. The toplevel
call should pass flag = 1. This prevents counting the exponent
in 1/x - 1/(x^2 -1) for example.
*/
{ unsigned short n = ARITY(t);
unsigned short f = FUNCTOR(t);
term power,a,b;
unsigned long k;
int i,j;
if(ATOMIC(t))
return;
if(f == '^')
{ power = ARG(1,t);
if(NEGATIVE(power))
power = ARG(0,power);
if(FUNCTOR(power) == '+' && !content_factor(power,&a,&b) && isinteger(a))
{ power = a; /* example, e^(2x + 2y) - 1,
we get here with exponents[0] = 2x + 2y */
if(!numerical(b))
++exponents[5];
}
if(INTEGERP(power))
{ k = ISINTEGER(power) ? INTDATA(power) : BIGNUMDATA(power).val[0];
for(j=2;j<5;j++)
{ if(k%j == 0)
++exponents[j];
}
if(k > 4)
++ exponents[6];
}
if(FUNCTOR(power)=='*')
/* check for things like 2n, 3n, or 4n */
{ if(FRACTION(power))
power = ARG(0,power);
if(FUNCTOR(power) == '*') /* e.g. exponent of 2pi i x */
/* example, for x^4 the following code will make exponents[2]
and exponents[4] both nonzero. */
{ for(j=2;j<5;j++)
{ if(!cancel(power, j==2 ? two : j==3 ? three : four, &a,&b))
++exponents[j];
}
}
}
if(!numerical(power))
++exponents[5];
if(SIGNEDFRACTION(power))
++exponents[7];
}
else if (f == '-')
analyze_exponents(ARG(0,t),flag,exponents);
else if (f == '+' && flag)
{ for(i=0;i<n;i++)
analyze_exponents(ARG(i,t),flag,exponents);
}
else if( f == '/' || f == '*')
{ for(i=0;i<n;i++)
analyze_exponents(ARG(i,t),0,exponents);
}
}
/*_________________________________________________________*/
static void trigsq(term v, int flag[MAXFLAGS])
/* if v is a square of a trig function or a product
one of whose factors is a square of a trig function,
fillin flag[SINSQ], or flag[TANSQ], etc, that is,
incrementing those fields once for each sin^2, etc, that
is contained in v as a factor (or as v itself).
Also increments flag['^'] for each exponent found.
Also increments flag[TRIGPRODUCT] for each product of two sines or cosines.
*/
{ unsigned short h = FUNCTOR(v);
unsigned short n,f;
int sincosflag;
int k;
if(h == '/' && ISINTEGER(ARG(1,v)))
v = ARG(0,v);
if(h == '^')
{ ++flag['^'];
if(!equals(ARG(1,v),two) || ATOMIC(ARG(0,v)))
return;
f = FUNCTOR(ARG(0,v));
if(f == SINH)
{ ++flag[SINHSQ];
return;
}
if(f == COSH)
{ ++flag[COSHSQ];
return;
}
if(f == TANH)
{ ++flag[TANHSQ];
return;
}
if(f == SECH)
{ ++flag[SECHSQ];
return;
}
if(f == COTH)
{ ++flag[COTHSQ];
return;
}
if(f == CSCH)
{ ++flag[CSCHSQ];
return;
}
if(f == SIN)
{ ++flag[SINSQ];
return;
}
if(f == COS)
{ ++flag[COSSQ];
return;
}
if(f == TAN)
{ ++flag[TANSQ];
return;
}
if(f == SEC)
{ ++flag[SECSQ];
return;
}
if(f == COT)
{ ++flag[COTSQ];
return;
}
if(f == CSC)
{ ++flag[CSCSQ];
return;
}
if(!TRIGF(f))
return;
++flag[256 + f];
return;
}
if(h != '*')
return;
n = ARITY(v);
sincosflag = 0;
for(k=0;k<n;k++)
{ trigsq(ARG(k,v),flag);
if(FUNCTOR(ARG(k,v)) == SIN || FUNCTOR(ARG(k,v)) == COS)
++sincosflag;
}
if(sincosflag == 2)
++flag[TRIGPRODUCT];
}
/*_______________________________________________________________*/
int numerical_sum(term t)
/* Return 1 if evalpuresigmatorational can work on t,
2 if evaluatesigmatorational can work on t,
0 if neither. To succeed there must be not more than one
free variable in t. Evalpuresigmatorational can work
only if there are NO free variables.
*/
{ term *atomlist;
int nvars;
if(FUNCTOR(t) != SUM)
return 1;
nvars = freevars(t,&atomlist);
if(nvars > 1)
{ free2(atomlist);
return 0; /* too many variables */
}
return nvars + 1;
}
/*______________________________________________________________________*/
static int possiblepoly(term t)
/* return 1 if t contains no functors but ^, /, *, +, -,
and only seminumerical denominators and integer exponents.
Return 0 otherwise.
*/
{ unsigned short n,f;
int i;
if(ATOMIC(t))
return 1;
f = FUNCTOR(t);
if(f != '^' && f != '-' && f != '*' && f != '+' && f != '/')
return 0;
if(f == '/')
{ if(!seminumerical(ARG(1,t)))
return 0;
return possiblepoly(ARG(0,t));
}
if(f == '^')
{ if(!ISINTEGER(ARG(1,t)))
return 1;
return possiblepoly(ARG(0,t));
}
n = ARITY(t);
for(i=0;i<n;i++)
{ if(!possiblepoly(ARG(i,t)))
return 0;
}
return 1;
}
/*______________________________________________________________________*/
void select_trigeval_op(int flag, term t, actualop *o, int *nops)
/* t is a trig function with a seminumerical arg, or an arg
with functor DEG and a seminumerical arg. Select some
relevant operators into *o and return the number selected in *nops.
If flag is nonzero, this is being called from autosimp and the
operators will be tried. If flag is zero, it's being called
to construct the Term Selection Menu, and the operators chosen will
be shown.
*/
{ term a,b,v;
term u = ARG(0,t);
int err,i = 0;
double z;
long kk;
unsigned short f = FUNCTOR(t);
if(NEGATIVE(u))
u = ARG(0,u);
if(ISZERO(u))
{ switch(f)
{ case SIN:
o[i] = sin0; ++i;
break;
case COS:
o[i] = cos0; ++i;
break;
case TAN:
o[i] = tan0; ++i;
break;
case SEC:
case CSC:
case COT:
o[i] = sin90; ++i;
break;
}
*nops = i;
return;
}
if(equals(u,pi_term) && f == SIN)
{ o[i] = zeroesofsin; ++i;
*nops = i;
return;
}
if(FUNCTOR(u) == DEG)
{ v = ARG(0,u);
deval(v,&z);
if(z == BADVAL)
{ *nops = i;
return;
}
if(180 < z && z < 360)
{ switch(f)
{ case SIN:
o[i] = sinhalfperiod1; ++i;
/* sin u = -sin(u-pi_term) */
break;
case COS:
o[i] = coshalfperiod1; ++i;
/* cos u = -cos(u-pi_term) */
break;
}
}
else if(90 < z && z < 180)
{ switch(f)
{ case SIN:
o[i] = sinhalfperiod2; ++i;
/* sin u = sin(pi-u) */
break;
case COS:
o[i] = coshalfperiod2; ++i;
/* cos u = -cos(pi-u) */
break;
}
}
z = fabs(z);
if(nearint(z,&kk) && kk==0L)
{ switch(f)
{ case SIN:
o[i] = sin0; ++i;
break;
case COS:
o[i] = cos0; ++i;
break;
case TAN:
o[i] = tan0; ++i;
break;
}
*nops = i;
return;
}
if(z - 90 * floor(z/90) < 0.01)
{ o[i] = sin90; ++i;
}
else if (z - 45 * floor(z/45) < 0.01)
{ o[i] = sin45; ++i;
}
else if (z - 30 * floor(z/30) < 0.01)
{ o[i] = sin30; ++i;
}
else if (z - 15 * floor(z/15) < 0.01 &&
(!flag || get_currenttopic() != _trig_product)
/* example: sin(deg(15)) sin(deg(45)),
in topic _trig_product, do not write 15 = 60-45,
but proceed to sinsin. */
)
{ o[i] = combine3045; ++i;
}
if( z < 0 || z >= 360.0)
{ o[i] = mod360; ++i;
}
*nops = i;
return;
}
if(!contains(u,PI_ATOM))
{ *nops = i;
return;
}
polyval(product(make_int(12L),u),&v);
err = cancel(v,pi_term,&a,&b);
if(err)
{ *nops = i;
return;
}
deval(b,&z);
if(z < 0)
z = -z;
if(z == BADVAL || !nearint(z,&kk))
{ *nops = i;
return;
}
/* no need to throw in mod2pi here, it will just make a duplicate entry:
if(kk > 12 || kk < 0)
{ o[i] = mod2pi; ++i;
}
*/
kk = kk % 12L;
if(kk == 0)
{ switch(f)
{ case SIN:
o[i] = zeroesofsin; ++i;
break;
case TAN:
o[i] = zeroesoftan; ++i;
break;
default:
o[i] = sin90; ++i;
}
}
else if(kk % 6 == 0)
{ /* a multiple of pi/2 */
o[i] = sin90; ++i;
}
else if(kk % 3 == 0)
{ /* a multiple of pi/4 */
o[i] = sin45; ++i;
}
else if(kk % 2 == 0)
{ /* a multiple of pi/6 */
o[i] = sin30; ++i;
}
else
{ o[i] = combine3045; ++i;
}
o[i] = radtodeg; ++i;
if(flag || !computefunctionflag)
{ o[i] = computefunction; ++i; /* evaluate numerically */
}
*nops = i;
}
/*______________________________________________________________________*/
static void selectexpops(term t, actualop *o, int *nops)
/* finish selectops1 when FUNCTOR(t) =='^' */
{ term u = ARG(0,t);
term v = ARG(1,t);
term a,c,w,p,q;
char buffer[DIMREASONBUFFER];
int j,k,count;
int i=0;
unsigned short g = FUNCTOR(u);
unsigned short h = FUNCTOR(v);
int problemtype = get_problemtype();
if(equals(u,complexi) && equals(v,minusone))
{ o[i] = recipofi; ++i;
}
if(ZERO(v))
{ o[i] = zeroexponent; ++i;
}
if(ONE(v))
{ o[i] = unitexponent; ++i;
}
if(ZERO(u))
{ o[i] = zerobase; ++i;
}
if(ONE(u))
{ o[i] = unitbase; ++i;
}
if(FRACTION(v))
{ if(equals(ARG(1,v),two))
{ o[i] = exponenttosqrtpower; ++i;
}
else
{ o[i] = exponenttorootpower; ++i;
}
}
if(INTEGERP(u) && (INTEGERP(v) || RATIONALP(v)) && !powertopower(t,zero,&w,buffer))
/* example: 125^(1/4) = 5^(3 * 1/4) */
{ o[i] = powertopower; ++i;
}
if(INTEGERP(u) && RATIONALP(v) && !(TYPE(u) == INTEGER && small_prime(INTDATA(u))))
{ o[i] = producttopower; ++i;
}
if(NEGATIVE(u) && ONE(ARG(0,u)))
{ if(FRACTION(v) &&
possible_int(ARG(0,v)) &&
possible_int(ARG(1,v))
)
{ o[i] = powerofminusone; ++i; /* evaluate (-1)^(p/q) */
}
if(possible_int(v))
{ o[i] = intpowerofminusone; ++i;
}
}
if(problemtype == POWERSERIES && equals(u,eulere))
{ if(NEGATIVE(v))
{ o[i] = negexpseries; ++i;
o[i] = negexpseries2; ++i;
o[i] = negexpseries3; ++i;
}
else
{ o[i] = expseries; ++i;
o[i] = expseries2; ++i;
o[i] = expseries3; ++i;
}
}
if(equals(u,eulere) && status(coshdef) >= LEARNING)
{ o[i] = NEGATIVE(v) ? exptohyper2 : exptohyper1; ++i;
}
if(!constant(v) && !equals(u,eulere) &&
status(introducelninexponent) >= LEARNING &&
get_problemtype() != TESTCONVERGENCE // where it makes trouble!
)
{ o[i] = introducelninexponent; ++i;
}
if(equals(u,complexi) && !INTEGERP(v) && !(NEGATIVE(v) && INTEGERP(ARG(0,v))))
{ o[i] = introducelninexponent; ++i;
}
if(!constant(v) && !equals(u,ten) && status(introduceloginexponent) >= LEARNING)
{ o[i] = introduceloginexponent; ++i;
}
if(contains(v,LOGB))
{ o[i] = introducelogbinexponent; ++i;
}
if(equals(u,complexi))
{ if(equals(v,two))
{ o[i] = defnofi; ++i;
}
else if(INTEGERP(v))
{ long kk = ISINTEGER(v) ? INTDATA(v) : BIGNUMDATA(v).val[0];
k = (int) kk % 4;
switch(k)
{ case 0:
o[i] = powersofi0; ++i;
break;
case 1:
o[i] = powersofi1; ++i;
break;
case 2:
o[i] = powersofi2; ++i;
break;
case 3:
o[i] = powersofi3; ++i;
break;
}
}
else if(!powersofi0(t,zero,&w,buffer))
{ o[i] = powersofi0; ++i;
}
else if(!powersofi1(t,zero,&w,buffer))
{ o[i] = powersofi1; ++i;
}
else if(!powersofi2(t,zero,&w,buffer))
{ o[i] = powersofi2; ++i;
}
else if(!powersofi3(t,zero,&w,buffer))
{ o[i] = powersofi3; ++i;
}
}
if(equals(u,ten) && FUNCTOR(v) == LOG)
{ o[i] = loginexponent; ++i;
}
if(equals(u,ten) && FUNCTOR(v) == '*' && contains_at_toplevel(v,LOG))
{ o[i] = loginexponent2; ++i; /* 10^(n log a) = a^n */
}
if(equals(u,eulere))
{ if(FUNCTOR(v) == LN)
{ o[i] = lninexponent; ++i;
}
if(FUNCTOR(v) == '*' && contains_at_toplevel(v,LN))
{ o[i] = lninexponent2; ++i;
}
if(FUNCTOR(v) == '*' && ARITY(v) == 2 &&
equals(ARG(0,v),complexi) && equals(ARG(1,v),pi_term)
)
{ o[i] = etotheipi; ++i;
}
if(FUNCTOR(v) == '*' && ARITY(v) == 2 && ISIPI(v))
{ o[i] = etotheipi; ++i;
}
if(NEGATIVE(v) && FUNCTOR(ARG(0,v)) == '*' &&
ARITY(ARG(0,v)) == 2 && ISIPI(ARG(0,v))
)
{ o[i] = etotheminusipi; ++i;
}
w = NEGATIVE(v) ? ARG(0,v) : v;
if(iscomplex(w))
{ complexparts(w,&p,&q);
if(ZERO(p))
{ o[i] = complexexponential; ++i;
}
else
{ o[i] = complexexponential2; ++i;
}
}
if( (FUNCTOR(w) == '+' ||
FUNCTOR(w) == '*' ||
(NEGATIVE(w) &&
(FUNCTOR(ARG(0,w)) == '+' || FUNCTOR(ARG(0,w)) == '*')
)
) &&
iscomplex(w) && !etothecoterminal(t,zero,&q,buffer)
)
{ o[i] = etothecoterminal; ++i;
}
/* e^((pi i)4) can be worked on by etothei2pi, and we need to catch it */
if(FUNCTOR(w) == '*' && contains_at_toplevel(w,'*'))
w = topflatten(w);
if(FUNCTOR(w) == '*' && ARITY(w) > 2 && iscomplex(w))
{ /* check if w has the form 2npi i */
int j;
int iflag = 0, piflag = 0, evenflag = 0;
term q;
for(j=0;j<ARITY(w);j++)
{ q = ARG(j,w);
if(equals(q,pi_term))
piflag = 1;
else if(equals(q,complexi))
iflag = 1;
else if(INTEGERP(q) && ISEVEN(q))
evenflag = 1;
}
if(evenflag && piflag && iflag)
{ o[i] = etothei2npi; ++i; /* e^(2n pi i) = 1 */
}
else if(iflag && contains(w,PI_ATOM))
{ o[i] = etothecoterminal; ++i; /* e^((2n pi + theta )i) = e^(itheta ) */
}
}
}
if(FUNCTOR(v) == LOGB && equals(u,ARG(0,v)))
{ o[i] = logbinexponent; ++i;
}
if(FUNCTOR(v) == '*' && contains_at_toplevel(v,LOGB))
{ o[i] = logbinexponent2; ++i;
}
if(TRIGFUNCTOR(g) && NEGATIVE(ARG(0,u)) && iseven(v))
{ switch(g)
{ case SIN:
o[i] = sinsqeven; ++i;
break;
case COS:
o[i] = cossqeven; ++i;
break;
case TAN:
o[i] = tansqeven; ++i;
break;
case COT:
o[i] = cotsqeven; ++i;
break;
case SEC:
o[i] = secsqeven; ++i;
break;
case CSC:
o[i] = cscsqeven; ++i;
break;
}
}
if(TRIGFUNCTOR(g) && !(ISINTEGER(v) && ISODD(v)) &&
FUNCTOR(ARG(0,u)) == '+' &&
!decompose(ARG(0,u),&a,&c) && !ZERO(c)
)
{ switch(g)
{ case SIN:
o[i] = sinsqperiodic; ++i;
break;
case COS:
o[i] = cossqperiodic; ++i;
break;
case SEC:
o[i] = secsqperiodic; ++i;
break;
case CSC:
o[i] = cscperiodic; ++i;
break;
}
}
switch(g)
{ case '+':
if(equals(v,two))
{ if(ARITY(u) == 2 &&
(
(NEGATIVE(ARG(0,u)) && !NEGATIVE(ARG(1,u))) ||
(NEGATIVE(ARG(1,u)) && !NEGATIVE(ARG(0,u)))
)
)
{ o[i] = squareofdif; ++i;
}
else
{ o[i] = squareofsum; ++i;
}
}
else if(equals(v,three) && NEGATIVE(ARG(ARITY(u)-1,u)))
{ o[i] = cubeofdif; ++i;
}
else if(equals(v,three))
{ o[i] = cubeofsum; ++i;
}
else
{ if(ISINTEGER(v) && INTDATA(v) < 20)
{ o[i] = plainbinomialtheorem; ++i;
}
if(INTEGERP(v))
{ o[i] = binomialtheorem; ++i; /* with (n k) */
}
}
if(problemtype == POWERSERIES &&
ARITY(u) == 2 &&
(ONE(ARG(0,u)) || ONE(ARG(1,u)))
)
{ o[i] = binomialseries; ++i;
o[i] = binomialseries2; ++i;
o[i] = binomialseries3; ++i;
}
break;
case '^':
o[i] = powertopower; ++i;
break;
case '-':
if(!ONE(u))
{ o[i] = minustopower; ++i;
}
break;
case '/':
o[i] = quotienttopower; ++i;
break;
case '*':
o[i] = producttopower; ++i;
break;
case SQRT:
if(iseven(v))
{ o[i] = powerofsqrt; ++i;
}
else if(isodd(v))
{ o[i] = powerofsqrt2; ++i;
}
o[i] = powersqrtexp; ++i;
break;
case ROOT:
if(equals(ARG(0,u),v))
{ o[i] = powerofroot; ++i;
}
else
{ o[i] = powerofroot2; ++i;
if(INTEGERP(v) && INTEGERP(ARG(0,u)) && INTDATA(v) > INTDATA(ARG(0,u)))
{ o[i] = powerofroot3; ++i;
}
}
if(!cancel(ARG(0,u),v,&c,&w))
{ if(equals(w,two))
{ o[i] = powerofroot5; ++i; /* root(2n,x)^n = sqrt(x) */
}
else if(isinteger(w))
{ o[i] = powerofroot4; ++i; /* root(mn,x)^m = root(n,x) */
}
}
o[i] = powerrootexp; ++i;
break;
case SUM:
if(seminumerical(v) && is_power_series(u,get_eigenvariable(),&a))
{ o[i] = powerofseries; ++i;
}
break;
case SG:
if(isinteger(v))
{ if(iseven(v))
{ o[i] = sgevenpower; ++i;
}
else if(isodd(v))
{ o[i] = sgoddpower; ++i;
}
}
else if(FRACTION(v) &&
isinteger(ARG(1,v)) &&
isodd(ARG(1,v)) &&
isinteger(ARG(0,v))
)
{ if(iseven(ARG(0,v)))
{ o[i] = sgevenpower; ++i;
}
else if(isodd(ARG(0,v)))
{ o[i] = sgoddpower; ++i;
}
}
break;
case ABSFUNCTOR:
if(equals(v,two) && is_complex(u))
{ o[i] = squareofabs; ++i; /* |u + vi|^2 = u^2 + v^2 */
}
if(iseven(v))
{ o[i] = absevenpower; ++i; /* |u|^2^n =u^2^n if u is real */
}
else if(!iscomplex(v))
{ o[i] = abspowerrev; ++i; /*|u|^n = |u^n| if n is real */
}
break;
case SIN:
if(equals(v,two) || iseven(v))
{ o[i] = sinsqtocossq; ++i; /* sin^2 u = 1 - cos^2 u */
if(FRACTION(ARG(0,u))) /* sin^2(theta /2) = (1-cos theta )/2 */
{ o[i] = sinsqhalf; ++i;
}
else
{ o[i] = sinsqhalf2; ++i; /* sin^2(theta ) = (1-cos 2theta )/2 */
}
}
if(isodd(v))
{ o[i] = sinoddpower; ++i; /* sin^(2n+1) u = sin u (1-cos^2 u)^n */
}
o[i] = sintocsc; ++i; /* sin u = 1/csc u */
break;
case COS:
if(equals(v,two) || iseven(v))
{ o[i] = cossqtosinsq; ++i; /* cos^2 u = 1 - sin^2 u */
if(FRACTION(ARG(0,u)))
{ o[i] = cossqhalf; ++i; /* cos^2(theta /2) = (1+cos theta )/2 */
}
else
{ o[i] = cossqhalf2; ++i; /* cos^2(theta ) = (1+cos 2theta )/2 */
}
}
if(isodd(v))
{ o[i] = cosoddpower; ++i; /* cos^(2n+1) u = cos u (1-sin^2 u)^n */
}
o[i] = costosec; ++i; /* cos u = 1/sec u */
break;
case SEC:
if(equals(v,two) || iseven(v))
{ o[i] = secsqtotansq; ++i; /* sec^2 u = tan^2 u + 1 */
}
o[i] = secrule; ++i; /* sec^2 u = 1/cos^2 u */
if(isodd(v))
{ o[i] = secoddpower; ++i; /* sec^(2n+1) u = sec u (tan^2 u+1)^n */
}
break;
case TAN:
if(equals(v,two) || iseven(v))
{ o[i] = tansqtosecsq; ++i; /* tan^2 u = sec^2 u - 1 */
}
o[i] = tanrule; ++i; /* tan u = sin u / cos u */
o[i] = tantodenom; ++i; /* tan^2 u = 1/cot^2 u */
if(isodd(v))
{ o[i] = tanoddpower; ++i; /* tan^(2n+1) u = tan u (sec^2 u-1)^n */
}
break;
case COT:
if(equals(v,two) || iseven(v))
{ o[i] = cotsqtocscsq; ++i; /* cot^2 u = csc^2 u - 1 */
}
o[i] = cottotan; ++i; /* cot u = 1 / tan u */
o[i] = cottosincos; ++i; /* cot u = sin u / cos u */
o[i] = cottocscsec; ++i; /* cot = csc/sec */
if(isodd(v))
{ o[i] = cotoddpower; ++i; /* cot^(2n+1) u = cot u (csc^2 u-1)^n */
}
break;
case CSC:
if(equals(v,two) || iseven(v))
{ o[i] = cscsqtocotsq; ++i; /* csc^2 u = cot^2 u + 1 */
}
o[i] = cscrule; ++i; /* csc^2 u = 1/sin^2 u */
if(isodd(v))
{ o[i] = cscoddpower; ++i; /* csc^(2n+1) u = csc u (cot^2 u+1)^n */
}
break;
case SINH:
if(equals(v,two) || iseven(v))
{ o[i] = sinhsqtocoshsq; ++i;
}
break;
case COSH:
if(equals(v,two) || iseven(v))
{ o[i] = coshsqtosinhsq; ++i;
}
break;
}
if(iseven(v) && !equals(v,two))
{ o[i] = writeassquare; ++i; /* a^(2n)= (a^n)^2 */
}
if(!equals(v,three) && !cancel(v,three,&w,&q))
{ o[i] = writeascube; ++i; /* a^(3n) = (a^n)^3 */
}
if(FUNCTOR(v) == '*' || (ISINTEGER(v) && !small_prime(INTDATA(v))))
{ o[i] = writeaspower; ++i; /* a^(?n) = (a^n)^? */
}
if(equals(v,two))
{ o[i] = expandpower; ++i;
}
if(iseven(v))
{ o[i] = absevenpowerrev; ++i; /* u^(2n) = |u|^(2n) if u is real */
}
switch(h) /* functor of exponent */
{ case '+':
o[i] = reversecollectpowers; ++i; /* a^(b+c) = a^b a^c */
count = 0;
for(j=0;j<ARITY(v);j++)
{ if(NEGATIVE(ARG(j,v)))
++count;
}
if(count && count != ARITY(v))
{ o[i] = reversecollectpowers2; ++i; /* a^(b-c) = a^b/a^c */
}
break;
case '-':
if(ONE(ARG(0,v)))
{ o[i] = eliminatenegexp1; ++i;
}
else
{ o[i] = eliminatenegexp; ++i;
}
if(g == '/')
{ o[i] = negexpofquotient; ++i;
}
break;
case '/': /* fractional exponents */
if(equals(ARG(1,v),two))
{ o[i] = exponenttosqrt; ++i;
}
else if(isinteger(ARG(1,v)))
{ o[i] = exponenttoroot; ++i;
}
if(INTEGERP(u) && !(ISINTEGER(u) && small_prime(INTDATA(u))))
{ o[i] = factorbase; ++i;
}
break;
case '*':
if(ARITY(v) == 2)
{ o[i] = reversepowertopower1; ++i;
o[i] = reversepowertopower2; ++i;
}
else
{ o[i] = reversepowertopower3; ++i;
}
break;
}
if(equals(v,infinity))
{ o[i] = toinfinity1; ++i;
o[i] = toinfinity0; ++i;
}
if(equals(v,minusinfinity))
{ o[i] = tominusinfinity1; ++i;
o[i] = tominusinfinity0; ++i;
}
if(equals(u,infinity))
{ o[i] = powerofinfinity; ++i;
}
if(!ZERO(v) && !ONE(v) && !equals(v,two))
{ o[i] = breakpower; ++i;
}
*nops = i;
return;
}
/*__________________________________________________________________*/
static int ok_sumleadingterm( pathlist *p)
/* return 1 if there is a LIMIT on the path, and after the LIMIT
there is no occurrence of '+', indeed only unary functions and ROOT
and powers and quotients are allowed. In the case of powers the
path must go into the base and the exponent must be constant.
Return 0 not to show sumleadingterm on the menu.
You can't apply sumleadingterm inside a sum, as the example
x^2/(1-x) - x^2/(1+x) shows.
You also can't go into a variable
exponent, as the related example e^(x^2/(1-x)) e^-(x^2/(1+x)) shows.
Also you get the wrong answer in lim(h->0, ln((x+h)/x)/h) if
you replace x+h by x.
Also in lim(1 + h/x)^(x/h), if you drop the h/x. But since
this function only sees the path, it can't tell if the exponent
is variable or not. So the operation will be presented, even if
the exponent is variable. But it won't work.
The use of this function thus does NOT guarantee that Mathpert's term
selection menu shows 'sumleadingterm' only when it will be legal to use it.
However, in cases where it is illegal, the operation will fail.
*/
{ pathlist *marker;
int flag = 0;
unsigned short f;
term x;
term tmark = history(get_activeline());
for(marker=p;marker;tmark = ARG(marker->data,tmark),marker=marker->next)
{ f = marker->functor;
if(f != FUNCTOR(tmark))
return 0; /* this can happen if the selected term is a subrange. */
if(f== LIMIT)
{ flag = 1;
x = ARG(0,ARG(0,tmark));
continue;
}
if(!flag)
continue;
if(f == '+')
return 0;
if(f == '^' &&
(marker->data != 0 || /* reject path in exponent */
depends(ARG(1,tmark),x) /* reject nonconstant exponent */
)
)
return 0; /* reject path in exponent */
if(f == '/' || f == '^' || f == '*' || f == ROOT || f == SQRT)
continue;
return 0;
}
return flag;
}
/*_______________________________________________________________________*/
int select_arith_aux(term t)
/* If t contains a sum or product with more than one numerical arg,
return 1. Or, if it contains a numerical '^' term, or a numerical
fraction which is not a rational number or on which cancel will work,
return 1. Or, if it contains a summand that evaluates to zero or
a factor that evaluates to 1, return 1.
Otherwise, return 0.
*/
{ unsigned short n,f;
int i,count,flag;
double z;
term a,b;
if(ATOMIC(t))
return 0;
f = FUNCTOR(t);
n = ARITY(t);
if(f == '+' || f == '*')
{ count = 0;
for(i=0;i<n;i++)
{ if(numerical(ARG(i,t)))
{ ++count;
flag = i;
}
if(count > 1)
return 1;
}
if(count == 1 && !deval(ARG(flag,t),&z) && z != BADVAL)
{ if(f == '+' && fabs(z) < VERYSMALL)
return 1;
else if(f == '*' && fabs(z-1) < VERYSMALL)
return 1;
}
}
if(f == '^' && numerical(t))
return 1;
if(f == '/' && numerical(t) &&
(!RATIONALP(t) || !cancel(ARG(0,t),ARG(1,t),&a,&b)
)
)
return 1;
for(i=0;i<n;i++)
{ if(select_arith_aux(ARG(i,t)))
return 1;
}
return 0;
}
/*_______________________________________________________________________*/
static int contains_integer_factor(term u)
/* return 1 if u is a product, one of whose factors is an
integer other than a small prime. Return 0 otherwise.
*/
{ int i;
term v;
unsigned short n = ARITY(u);
if(FUNCTOR(u) != '*')
return 0;
for(i=0;i<n;i++)
{ v = ARG(i,u);
if(INTEGERP(v) && !ONE(v) && !small_prime(INTDATA(v)))
return 1;
}
return 0;
}
/*__________________________________________________________________*/
static void count_aux(int count[6], unsigned short hh)
/* increment one of the entries of count according
as the value of hh is '=', '<', LE, '>', or GE. If
it's none of these values increment count[5].
*/
{ ++count[hh == '=' ? 0 :
hh == '<' ? 1 :
hh == LE ? 2 :
hh == '>' ? 3 :
hh == GE ? 4 :
5
];
}
/*____________________________________________________________________*/
int test_invisiblesub(term t)
/* check if invisible sub can work with selected term t as the arg.
Return 1 if it can work, 0 if not.
*/
{ char buffer[DIMREASONBUFFER];
term w;
int err;
int savenextdefn = get_nextdefn();
int saveeigen = get_eigenindex();
short savenextassumption = get_nextassumption();
int savenvariables = get_nvariables();
void *savenode = heapmax();
term s = history(get_currentline());
err = invisiblesub(s,t,&w,buffer);
set_nextdefn(savenextdefn);
set_nvariables(savenvariables);
set_nextassumption(savenextassumption);
set_eigenvariable(saveeigen);
reset_heap(savenode);
return err ? 0 : 1;
}
/*___________________________________________________________________*/
static int inexact(term t)
/* return 1 if t contains a double other than 0.5 */
{ unsigned short n;
int i;
if(OBJECT(t) && TYPE(t) == DOUBLE)
return DOUBLEDATA(t) == 0.5 ? 0 : 1;
if(ATOMIC(t))
return 0;
n = ARITY(t);
for(i=0;i<n;i++)
{ if(inexact(ARG(i,t)))
return 1;
}
return 0;
}
/*______________________________________________________________________*/
static int contains_binders(term t)
/* return 1 if t contains a LIMIT, SUM, PRODUCT, or definite INTEGRAL;
return -1 if t contains a DIFF or an indefinite INTEGRAL. In case it
contains both, it returns from the first one encountered.
*/
{ unsigned short f,n;
int i,flag = 0,temp;
if(ATOMIC(t))
return 0;
f = FUNCTOR(t);
if(f == LIMIT || f == SUM || f == PRODUCT || (f == INTEGRAL && ARITY(t) == 4))
return 1;
if(f == DIFF || f == INTEGRAL)
flag = -1;
n = ARITY(t);
for(i=0;i<n;i++)
{ temp = contains_binders(ARG(i,t));
if(temp == 1)
return 1;
if(temp)
flag = temp;
}
return flag;
}
/*______________________________________________________________________*/
static int binders_on_path(lterm t, term a)
/* return the number of binding functors on the path t.path.
Does not count indefinite integrals and derivatives as binding functors.
Call fillbinders once for each binding functor, assuming the path
is the path to t from a (so t is a located subterm of a).
Sets the eigenvariable when passing a binding functor, and also
when passing an indefinite integral or derivative.
*/
{ unsigned short f;
pathlist *marker = t.path;
int ans = 0;
term *varlist = get_varlist();
int nvariables = get_nvariables();
int i;
term x;
start: /* label for tail recursion */
if(marker == NULL)
return ans;
if(marker->data < 0)
return ans;
/* a is a subrange term, e.g. a product formed from the last two
terms of a 3-ary product. */
f = marker->functor;
if(f == FRACT) /* term t contains a broken fraction */
f = '/';
if(f == VECTOR && FUNCTOR(a) == OR)
f = OR;
if(f == AND && FUNCTOR(a) == OR)
f = OR; /* happens in related_rates problems */
if(FUNCTOR(a) != f)
assert(0);
if(f == LIMIT || f== SUM || f == PRODUCT || (f == INTEGRAL && ARITY(a)== 4))
{ fillbinders(a);
++ans;
}
if(f == LIMIT || f == SUM || f == PRODUCT || f == INTEGRAL || f == DIFF)
{ /* set the eigenvariable */
if(f == LIMIT)
x = ARG(0,ARG(0,a));
else
x = ARG(1,a);
if(!ISATOM(x))
assert(0);
for(i=0;i<nvariables;i++)
{ if(equals(x,varlist[i]))
{ set_eigenvariable(i);
break;
}
}
}
if(ATOMIC(a))
return ans;
a = ARG(marker->data,a);
marker = marker->next;
goto start;
}
/*______________________________________________________________________*/
int contains_numerical_bernoulli(term t)
/* returns 1 if t contains a term with functor BERNOULLI and numerical argument. */
{ unsigned short f,n;
int i;
int rval = 0;
f = FUNCTOR(t);
n = ARITY(t);
if(f == BERNOULLI && numerical(ARG(0,t)))
return 1;
if(ATOMIC(t))
return 0;
for(i=0;i<n;i++)
rval = rval | contains_numerical_bernoulli(ARG(i,t));
return rval;
}
/*______________________________________________________________________*/
int contains_numerical_euler(term t)
/* returns 1 if t contains a term with functor EULERNUMBER and numerical argument. */
{ unsigned short f,n;
int i;
int rval = 0;
f = FUNCTOR(t);
n = ARITY(t);
if(f == EULERNUMBER && numerical(ARG(0,t)))
return 1;
if(ATOMIC(t))
return 0;
for(i=0;i<n;i++)
rval = rval | contains_numerical_euler(ARG(i,t));
return rval;
}
/*______________________________________________________________________*/
static int contains_numerical_subterm(term t)
/* return nonzero if t contains a numerical subterm;
return a value > 1 if it contains a subterm with two numerical args.
In the case of an OR or AND or MATRIX, more than one arg must contain a
numerical subterm. */
/* Examples: i^2 + i - 6, return 1
(-1)^2 x, return 2 (one arg with two numerical args)
2 + x -2, return 2 (two numerical args)
*/
{ unsigned short n;
int i,count;
if(POSNUMBER(t))
return 1;
if(ATOMIC(t))
return 0;
n = ARITY(t);
if(FUNCTOR(t) == OR || FUNCTOR(t) == AND || FUNCTOR(t) == MATRIX)
{ /* more than one arg must contain a numerical subterm */
count = 0;
for(i=0;i<n;i++)
{ if(contains_numerical_subterm(ARG(i,t)))
{ if(count)
return 2;
else
++count;
}
}
}
count = 0;
for(i=0;i<n;i++)
{ if(contains_numerical_subterm(ARG(i,t)) > 1)
return 2;
if(numerical(ARG(i,t)))
++count;
if(count > 1)
return count;
}
return count;
}
/*_________________________________________________________________________________*/
static int contains3(term t, unsigned short f)
/* return 1 if t contains f in a sum or product
or base of an exponent from the top, 0 otherwise
*/
{ unsigned short n,i,g;
if(ISATOM(t))
return f == FUNCTOR(t);
if(OBJECT(t))
return 0;
g = FUNCTOR(t);
if(g == f)
return 1;
if(g != '*' && g != '-' && g != '^' && g != '+')
return 0;
n = ARITY(t);
if(g == '^')
n = 1;
for(i=0;i<n;i++)
{ if(contains3(ARG(i,t),f))
return 1;
}
return 0;
}
/*______________________________________________________________*/
static void selectminmaxops(term t, actualop *o, int *nops)
/* called by selectops1; parameters are the same as for selectops1.
*/
{ int i=0;
if(constant(ARG(1,t)))
{ o[i] = functionisconstant; ++i;
}
if(!used2(ADDCRITICALPOINTS))
{ o[i] = addcriticalpoints; ++i;
}
if(!used2(ADDENDPOINTS))
{ o[i] = addendpoints; ++i;
}
if(!used2(ADDUNDEFINEDPOINTS))
{ o[i] = addundefinedpoints; ++i;
}
if(!used2(ADDLIMITS) &&
(used2(TABULATE) || used2(TABULATEEXACT))
)
{ o[i] = addlimits; ++i;
}
if(!used2(REJECTPOINT) &&
(used2(TABULATE) || used2(TABULATEEXACT))
)
{ o[i] = rejectpoint; ++i;
}
if(used2(ADDCRITICALPOINTS) || used2(ADDENDPOINTS) || used2(ADDUNDEFINEDPOINTS))
{ o[i] = tabulate; ++i;
o[i] = tabulateexact; ++i;
}
*nops = i;
}
/*______________________________________________________________*/
static void selectintervalops(term t, actualop *o, int *nops)
/* called by selectops1; parameters are the same as for selectops1.
*/
{ unsigned short ff = FUNCTOR(ARG(0,t));
unsigned short gg = FUNCTOR(ARG(1,t));
int i = 0;
term u,a,b,w;
char buffer[DIMREASONBUFFER];
u = ARG(1,ARG(0,t));
a = ARG(0,ARG(0,t));
b = ARG(1,ARG(1,t));
if(FRACTION(u) && !econstant(ARG(1,u)) &&
obviously_positive(ARG(0,u)) &&
econstant(a) && econstant(b)
)
{ /* show the appropriate operation for taking the reciprocal */
int signa, signb;
if(obviously_positive(a))
signa = 1;
else if(obviously_negative(a))
signa = -1;
else signa = 0;
if(obviously_positive(b))
signb = 1;
else if(obviously_negative(b))
signb = -1;
else
signb = 0;
if(signa == 1 && signb == 1)
{ o[i] = ff == '<' ?
(gg == '<' ? recipinterval11 : recipinterval21):
(gg == '<' ? recipinterval12 : recipinterval22);
++i;
}
if(signa == -1 && signb == -1)
{ o[i] = ff == '<' ?
(gg == '<' ? recipinterval31 : recipinterval41):
(gg == '<' ? recipinterval31 : recipinterval42);
++i;
}
if(signa == -1 && signb == 1)
{ o[i] = ff == '<' ?
(gg == '<' ? recipinterval51 : recipinterval61):
(gg == '<' ? recipinterval52 : recipinterval62);
++i;
}
}
if(obviously_nonnegative(u))
{ if(ff == '<' &&
obviously_positive(strongnegate(ARG(0,ARG(0,t))))
)
{ o[i] = gg == '<' ? sqrtinterval3 : sqrtinterval5; ++i;
}
else if(ff == LE &&
obviously_nonnegative(strongnegate(ARG(0,ARG(0,t))))
)
{ o[i] = gg == '<' ? sqrtinterval4 : sqrtinterval6; ++i;
}
}
if(FUNCTOR(u) == '^' && iseven(ARG(1,u)))
{ if(FUNCTOR(ARG(0,t)) == '<' && FUNCTOR(ARG(1,t)) == '<')
{ o[i] = sqrtinterval1; ++i; /* a<u^2<b => -sqrt b<u<-sqrt a or sqrt a<u<sqrt b */
if(!equals(ARG(1,u),two))
{ o[i] = rootinterval1; ++i; /* a<u^2^n <b => -sqrt(b)<u<-sqrt(a) or sqrt(a)<u<sqrt(b) */
}
}
else
{ o[i] = sqrtinterval2; ++i;
/* a<=u^2<=b => -sqrt b<=u<=-sqrt a or sqrt a<=u<=sqrt b */
/* also handles intervals with one < and one LE */
if(!equals(ARG(1,u),two))
{ o[i] = rootinterval2; ++i; /* a<=u^2^n <=b => -sqrt(b)<=u<=-sqrt(a) or sqrt(a)<=u<=sqrt(b) */
}
}
}
/* Don't put explicitdomain in because it already came in from the
call to selectops1(ARG(0,t)... if it's going to come it at all. */
if(!intervaltoabs1(t,zero,&w, buffer))
{ o[i] = intervaltoabs1; ++i; /* -a <= u <= a iff |u| <= a */
}
if(!intervaltoabs2(t,zero,&w,buffer))
{ o[i] = intervaltoabs2; ++i; /* -a < u <= a iff |u| < a */
}
*nops = i;
}
/*____________________________________________________________________*/
#define MAXOPS 80
static actualop selectedops[MAXOPS];
static char *stext[64];
static int scommands[64];
static void StoreSelectorMenuString(int i, char *text)
{ if(i < 64)
stext[i] = text;
}
static void StoreSelectorMenuCommand(int i, int id)
{ if(i < 64)
scommands[i] = id;
}
/*____________________________________________________________________*/
int MakeSelectorMenu(PDOCDATA pDocData,char **menuitems, operation *menuops)
/*
Create the selection menu, to contain a list of all operators applicable
to currently selected subterms of the active line, or which
could use those selected subterms as arguments.
The array menuitems will receive the menu items as strings
containing <svg> elements, of class "selectionmenuitem" and
with a property "commandID" containing an integer to be used
when that item is chosen for execution.
Return the number of operators in the menu
At entrance, menuitems should have space for 64 pointers to char,
but those pointers need not point anywhere; space will be
malloc'ed and then freed after use.
The ops corresponding to the menuitems are placed in the array menuops.
(They are needed by showStep.)
*/
{ int i,j,k,nops=0, nopswith=0;
int index;
term active = pDocData->DocProverData.history[get_activeline()];
int command_id;
int selected_eqn;
int orderflag;
const char *text;
short nextassumption = get_nextassumption();
ltermlist *q;
hashbucket *model;
SetCommentStop(1); // don't leave error messages in the document while testing operations
Papyrus *pPapyrus = pDocData->papyrus;
memset(selectedops,0,sizeof(selectedops));
if(pPapyrus->selected == NULL)
assert(0); // this shouldn't be called unless there is a selected term
selected_eqn = get_selected_equation();
if(selected_eqn < 0 && (FUNCTOR(active) == AND || FUNCTOR(active) == OR))
active = ARG(abs(selected_eqn)-1,active);
dowith(active,pPapyrus->selected,selectedops, &nopswith);
/* This gets operations to be applied with the selected term
as arg, or with an arg made from the selected terms. */
q = pPapyrus->selected;
if(ALREADY(q->data))
UNSET_ALREADY(q->data);
/* this allows polyvalop to show up on the Term Selection Menu. For example
if pPapyrus->selected is a sum in a numerator, it may be marked ALREADY,
but polyvalop may nevertheless work on the fraction and then set_pathtail
to the sum, since within polyval it will UNSET_ALREADY on num and denom.
*/
orderflag = get_orderflag();
if(orderflag == ASCENDING)
{ /* are we in an exponent? If so we must set orderflag to DESCENDING */
pathlist *marker;
for(marker = q->data.path; marker; marker = marker->next)
{ if(marker->data == 1 && marker->functor == '^')
{ set_orderflag(DESCENDING);
break;
}
}
}
nextassumption = get_nextassumption();
selectops(pPapyrus->selected,selectedops+nopswith,&nops);
/* This gets operations to be applied TO the selected term(s) */
set_nextassumption(nextassumption); /* make sure no assumptions made by
operations tried out in constructing
the menu survives */
if(orderflag== ASCENDING)
set_orderflag(ASCENDING);
nops += nopswith;
if(nops == 0)
{ // MessageBox((HWND)pDocData->hwnd, english(1328), english(1329), MB_OK | MB_ICONEXCLAMATION);
/* No applicable operations */ /* Try again, please */
// Some reviewers thought it was better without this.
return 0;
}
model = pDocData->DocControlData.model;
for(k=0;k<nops && k < 64;k++)
{ index = access_mod(selectedops[k]);
i = model[index].men; /* = op.men */
j = model[index].choice; /* = op.choice */
/* using the hash table this way is faster than
calling lookupop(selectedops[k],&op); */
assert(j >= 0);
assert(j < 16);
if(i==functions_menu)
text = get_defnstring(j);
else
text = cmdmenu(i)[j];
command_id = opcommand(i,j);
if(k >= nopswith)
command_id |= 0x10; /* set bit 4 to tell it should be
applied only to the selected term */
if(text == NULL)
assert(0);
StoreSelectorMenuCommand(k,command_id);
StoreSelectorMenuString(k, (char *) text); /* text is const char * */
menuops[k].men = i;
menuops[k].choice = j;
}
SetCommentStop(0); // stop inhibiting error messages and comments
// Now make the <svg> elements.
for(k=0;k<nops;k++)
{ int textlength = (int) strlen(stext[k]);
int avail = textlength +8192; // you can need a lot for termtoSVG
menuitems[k] = malloc(avail);
if(menuitems[k] == NULL)
assert(0);
char idbuf[32];
sprintf(idbuf,"%d",scommands[k]);
svgSymbolTextInvisibleElement(
stext[k], // menu string with TeX-like abbreviations
menuitems[k], // where to write the SVG code
idbuf, // id for the resulting SVG element,
"selectmenuitem", // the class of the SVG element
avail, // how much space is available
0, // the color to use
0, // x-coordinate to use
0 // y-coordinate to use
// zero because these menu items will be put in a wrapper div
// with absolute positioning, so they will be positioned relative to the wrapper.
);
}
return nops;
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists