Sindbad~EG File Manager
/* M. Beeson, for Mathpert */
/* final_remark */
/*
10.5.90 original date
1.14.99 modified
1.15.00 added 2396
2.28.00 added series_fremark call
1.25.06 modified for MINMAX
9.2.07 added a case for complex_trig
5.6.13 added a call to get_minmaxflag and include stddef.h
6.17.13 removed mention of dead topic alg1_quadratic_formula
3.19.23 initialized err in final_remark
12.31.23 replaced get_controldata with get_problemtype()
1.1.25 modified final_remark under ADDSERIES
1.22.25 if 0'd out some code involving q.opseq
2.16.25 removed unused 'flag' in final_remark
3.3.25 added clear_comment_buffer(); in final_remark
*/
#include <assert.h>
#include <stddef.h>
#include "globals.h"
#include "graphstr.h"
#include "display.h"
#include "mpdoc.h"
#include "tdefn.h"
#include "probtype.h"
#include "cflags.h"
#include "trig.h" /* checknumerically */
#include "eqn.h" /* solved */
#include "order.h" /* mvpoly */
#include "factor.h" /* factored */
#include "automode.h" /* lookupop */
#include "algaux.h" /* contains_monomially */
#include "ops.h"
#include "calc.h"
#include "optable.h" /* access_optable */
#include "prover.h" /* interval_as_and */
#include "symbols.h" /* get_checksolutionsflag */
#include "pvalaux.h" /* complex_number */
#include "fremark.h"
#include "cancel.h"
#include "complex4.h" /* is_polar_complex */
#include "autosum.h" /* contains_arctrigs */
#include "deval.h" /* seminumerical */
#include "mpminmax.h" /* used2, TABULATE, etc. */
#include "series.h" /* used4, DIVERGENCETEST, etc. */
#include "domain.h" /* contains_defined_variables */
#include "polynoms.h" /* ispolyin */
#include "scontrol.h" /* series_fremark */
#include "operator.h" /* complex_numbers */
static int solved_system(term p);
static int needs_commondenom(term t);
static int contains_cf(term t);
static int contains_ratexp(term t);
static int needs_expand(term t);
static int noderivs(term t);
static int needs_negexp(term t);
static int contains_negexp(term t);
static int trivial_op(operation);
static int contains_trigproduct(term t);
/*___________________________________________________________________*/
const char * final_remark(int *flag, operation *plan)
/* get the string to write after Finished is pressed,
or after AutoFinish completes. At entrance, *flag is
0, 1, or 2. It is 0 if automode will do nothing more,
1 if automode will take 1 step more, 2 if automode will
take 2 or more additional steps.
Determine whether the problem is acceptably finished,
and if so put *flag = 1 (if you want it to say "Finished),
or *flag = 3 (it will say "OK"), or put *flag = 0 (it will
say "Not yet". In any
case return the remark that the user should see.
Note that the answer may be acceptable even if automode
would do more; and conversely, automode may find nothing
to do even if it hasn't yet solved the problem.
If *flag is nonzero at input, then plan must contain *flag
valid entries indicating what automode would do next.
This will be the case when the user has pressed the
Finished button. When automode itself completes a problem,
*flag is zero at input.
*/
{
int currentline = get_currentline();
int currenttopic = get_currenttopic();
term p = history(currentline);
int problemtype,nsteps,i,err=1;
unsigned short f = FUNCTOR(p);
const char *ans;
term x = get_eigenvariable();
term u,v;
problemtype = get_problemtype();
nsteps = *flag; /* number of additional steps needed (0,1,2) */
*flag = 0; /* set it to 1 if the problem is solved */
/* First we look for a reason to reject the proposed answer. */
switch(problemtype)
{ case LINEAR_EQUATION: /* fall through */
case SOLVE_EQUATION:
if(
( FUNCTOR(p) == '=' && equals(ARG(0,p),ARG(1,p))) ||
equals(p,trueterm)
)
{ *flag = 1;
if(get_checksolutionsflag())
return english(1728);
/* The equation appears to be an identity, but you have
taken some steps that could have introduced new solutions,
so you haven't proved that the original equation was an
identity. This work is inconclusive.";
*/
return english(1688);
/* Equation is an identity, true whenever both sides are defined." */
}
if(!solved(p,x))
{ if(nsteps == 1)
{ /* check for the case 3 = x */
if(solved(equation(ARG(1,p),ARG(0,p)),x))
{ *flag = 1; /* accept this as finished */
return english(2224);
/* That equation is solved, but it is customary to put the
variable on the left side, so Mathpert would take one more step. */
}
}
return nsteps ? english(1668): english(5);
/* Not yet solved. */
/* Too difficult. You can still try numerical or graphical solution. */
}
if(
(currenttopic == _complex_cubics || currenttopic == _cubic_one_root) &&
FUNCTOR(p) == OR && ARITY(p) > 3
)
{ return english(1879);
/* There can be at most three roots of a cubic equation.
Simplify the expressions for the roots until some of them are identical. */
}
if(
(currenttopic == _complex_cubics || currenttopic == _cubic_one_root) &&
equals(p,falseterm)
)
{ return english(1878);
/* The original cubic must have more than one real root. In that case
complex solutions of the intermediate equations will be involved,
which is beyond the scope of this topic. Your intermediate equation
has no real solutions, but of course the original cubic does have solutions. */
}
if(COMPLEXTOPIC(currenttopic))
{ /* answer must be in the form a + bi */
if(FUNCTOR(p) == '=')
{ if( iscomplex(ARG(1,p)) && !complexexpression(ARG(1,p)))
return english(2396);
/* Solution(s) should be reduced to the form a + bi */
}
if(FUNCTOR(p) == OR)
{ for(i=0;i<ARITY(p);i++)
{ if(iscomplex(ARG(1,ARG(i,p))) && !complexexpression(ARG(1,ARG(i,p))))
return english(2396);
/* Solution(s) should be reduced to the form a + bi */
}
}
if(currenttopic == _complex_trig)
{ if(iscomplex(p) && !complexexpression(p))
{ return english(2402);
/* The answer should be reduced to the form a + bi */
}
}
}
if(nsteps == 0)
{ *flag = 1;
return english(7); /* That's the answer */
}
if(contains(p,FALSEFUNCTOR) || contains(p,TRUEFUNCTOR))
return nsteps == 1 ? english(1869) : english(1870);
/* You have solved the equation, but Mathpert would take one more step. */
/* You have solved the equation, but perhaps the answer should be simplified. */
if(nsteps)
{ if(currenttopic == _solve_linear_eqn ||
currenttopic == _quadratic_formula ||
currenttopic == _solve_quadratic_equation
)
return english(1882);
/* You have solved the equation, but the answer should be simplified. */
else
return english(1870);
/* You have solved the equation, but perhaps the answer should be simplified. */
}
break;
case ABSOLUTE_VALUE: /* fall through */
case INEQUALITIES:
if(interval_as_and(p) || INEQUALITY(f) || f == OR)
err = !solved(p,x);
else if(equals(p,trueterm) || equals(p,falseterm))
err = 0;
if(err)
return nsteps? english(1668) : english(1551);
/* Not yet solved. */
/* Too difficult. You can still try graphical solution. */
if(nsteps)
{ if(DEPENDENT(x))
return english(1724);
/* This looks solved, but the variable is not the original one */
else if( (void *) access_optable(plan[0].men)[plan[0].choice-1] == (void *) explicitdomain)
return english(1685);
/* Although this looks solved, you have included solutions
that are ruled out by current assumptions. Use the
assumptions to eliminate or correct the solution. */
else
return english(1725);
/* This is solved, but Mathpert would simplify the answer further. */
}
break;
case INTEGRATION:
if (FUNCTOR(p)== '=' ?
contains(ARG(1,p),INTEGRAL) :
contains(p,INTEGRAL)
)
{ if(nsteps)
return english(1668); /* Not yet solved. */
if(currenttopic == _simple_int)
return english(1398);
/* Mathpert can't solve that problem without more advanced
techniques. You entered it under Simple Integration, which
means Mathpert won't try integration by substitution or parts. */
if(currenttopic == _int_by_substitution)
return english(1491);
/* Mathpert can't solve that problem without more advanced
techniques. You entered it under integration by substitution,
which means Mathpert won't try integration by parts. */
return english(17); /* That's the best Mathpert can do. */
}
/* Now the integral sign has been eliminated */
if(contains(p,LIMIT))
{ /* an improper integral was reduced to a limit but the
limit hasn't been evaluated. */
return english(17);
}
/* Don't okay it until all defined variables have been eliminated. */
if(contains_defined_variables(p))
return english(2314);
break;
case FACTOR:
if(factored(p)) /* no non-irreducible sums */
{ if(mvpoly(p))
return english(16); /* That polynomial is irreducible */
if(nsteps == 0)
{ *flag = 1;
return english(7); /* That's the answer */
}
break;
}
if(nsteps == 0)
{ if(is_complex(p))
*flag = 1; /* it may contain e.g. (x^2-i) which we don't try to factor. */
return english(17);
}
/* That's the best Mathpert can do */
/* Mathpert would take one more step, and
the "answer" doesn't pass factored */
return nsteps == 1 ? english(1449) : english(1450);
/* Mathpert would take one more step. */
/* Mathpert would take two or more additional steps. */
case SIGMA_NOTATION:
if(contains(p,SUM))
return nsteps ? english(1670): english(1060);
/* There is an unevaluated indexed sum. */
/* Mathpert can't evaluate the sum. Evaluate it numerically. */
break;
case ADDSERIES:
if(contains(p,SUM) && contains(p, INFINITYFUNCTOR))
return nsteps ? english(2234): english(2235);
/* There is an unevaluated infinite series. */
/* Mathpert can't find a closed form for this series. */
break;
case POWERSERIES:
if(FUNCTOR(p) == SUM)
break;
if(contains(p,SUM) && linear_in_series(p))
break;
if(nsteps == 0)
{ // but it's not solved
return english(17); /* That's the best Mathpert can do */
}
return nsteps == 1 ? english(1449) : english(1450);
/* Mathpert would take one more step. */
/* Mathpert would take two or more additional steps. */
case TESTCONVERGENCE:
ans = series_fremark(p,*flag);
if(ans)
return ans;
break;
case TRIG_IDENTITY:
if(FUNCTOR(p) != '=')
assert(0);
if(equals(ARG(0,p),ARG(1,p)))
{ *flag = 1;
return english(1673);
/* That's the answer: the identity is verified. */
}
if(nsteps)
return english(1671);
/* Identity not yet verified. The two sides are not identical. */
/* Now, it's not verified, and MathXpert won't go further. */
#if 0
/* is this an identity that flunked checknumerically ? */
code_to_op(checknumerically, &op);
if(q.opseq[currentline].men == op.men && q.opseq[currentline].choice == op.choice)
return english(1097); /* Identity is not valid. */
#endif
return english(1096);
/* Identity not verified. Test it numerically. */
case LINEAR_EQUATIONS:
if(solved_system(p))
{ *flag = 1;
if(nsteps == 0)
return english(7); /* That's the answer. */
else
return english(1868);
/* You have solved the equations, but perhaps the answers should be simplified. */
}
if(currenttopic == _cramers_rule)
{ return nsteps ? english(1668) : english(1344);
/* Not yet solved. */
/* Cramer's rule only works when the equations
have a unique solution, which these don't.
*/
}
if(currenttopic == _eqns_by_matrix_inverse)
{ return nsteps ? english(1668) : english(1350);
/* Not yet solved. */
/* Taking the matrix inverse only works when the equations
have a unique solution, which these don't.
*/
}
if(nsteps)
return english(1668); /* Not solved yet */
else
{ *flag = 1;
return english(7); /* That's the answer */
}
case DIFFERENTIATE:
if(currenttopic == _higher_order_diff)
{ if(nsteps == 0 || noderivs(p))
{ *flag = 1;
return english(612);
/* That's a good answer. Use u=v => du/dx = dv/dx for more derivatives. */
}
return nsteps == 1 ? english(1449) : english(1450);
/* Mathpert would take one more step. */
/* Mathpert would take two or more additional steps. */
}
if(nsteps == 0)
{ *flag = 1;
return english(1672); /* That's a good answer. */
}
if(!noderivs(p))
return english(1674);
/* There is still an unevaluated derivative */
break;
case IMPLICIT_DIFF:
if(FUNCTOR(p) == '='
&& (contains(ARG(1,p),DIFF) ||
!(
(FUNCTOR(ARG(0,p)) == DIFF || FUNCTOR(ARG(0,p)) == PR)
&& ATOMIC(ARG(0,ARG(0,p)))
)
)
)
return nsteps ? english(1668) : english(17);
/* Not solved yet. */
/* That's the best Mathpert can do. */
if(nsteps == 0)
{ *flag = 1;
return english(1672); /* That's a good answer. */
}
if(!noderivs(p))
return english(1674);
/* There is still an unevaluated derivative */
break;
case DIFFERENTIATE_FROM_DEFN:
if(contains(p,DIFF))
return english(1674);
/* There is still an unevaluated derivative */
/* fall through to LIMITS */
case LIMITS:
case LHOPITAL:
if (contains(p,LIMIT)) /* an unevaluated limit remains */
return english(1669);
/* "There is an unevaluated limit term" */
/* (even if MathXpert can't evaluate it) */
if(contains_zero_denom(p))
return english(1966);
/* Eliminate zero denominators */
if(ISATOM(p) || equals(p,minusinfinity))
break;
if(contains_undefined(p))
return nsteps ? english(1988) : english(17);
/* There is an unevaluated infinite or undefined subterm. */
/* That's the best Mathpert can do. */
break;
case SIMPLIFY:
if(currenttopic == _evaluate_numerically)
{ if(seminumerical(p))
{ *flag = 1;
return english(7); /* That's the answer */
}
else
return english(1523);
/* To get a meaningful answer, you must specify values of the variables. Mathpert can't do this for you. */
}
if(currenttopic == _inverse_trig_functions)
{ if(contains_arctrigs(p))
return english(1832);
/* That expression still contains an inverse trig function. */
}
if(currenttopic == _trig_product)
{ if(contains_trigproduct(p))
return english(1952);
/* That expression still contains a product of trig functions. */
}
if(currenttopic == _complex_trig)
{ /* the solution should be in the form a + bi */
unsigned short f = FUNCTOR(p);
if(FRACTION(p))
{ if(iscomplex(ARG(1,p)))
return hints(complex_numbers,11); /* Express complex numbers in the form u + iv */
else
{ p = ARG(0,p);
f = FUNCTOR(p);
}
}
if(f == '-')
{ p = ARG(0,p);
f = FUNCTOR(p);
}
if(f == '+')
{ /* there should be just one complex summand */
int i;
unsigned short n = ARITY(p);
int count = 0;
int flag = -1;
for(i=0;i<n;i++)
{ if(iscomplex(ARG(i,p)))
{ ++count;
flag = i;
}
if(count == 2)
return hints(complex_numbers,11);
}
if(flag > -1)
{ p = ARG(i,p);
f = FUNCTOR(p);
}
}
if(FRACTION(p)) /* It could be a fraction now, e.g. on (1 + (i/2)) it will now be i/2 */
{ if(iscomplex(ARG(1,p)))
return hints(complex_numbers,11); /* Express complex numbers in the form u + iv */
else
{ p = ARG(0,p);
f = FUNCTOR(p);
}
}
if(f == '*')
{ /* there should be at most one complex factor and it should be i */
int i;
unsigned short n = ARITY(p);
int count = 0;
for(i=0;i<n;i++)
{ if(iscomplex(ARG(i,p)))
{ ++count;
if(!equals(ARG(i,p),complexi))
return hints(complex_numbers,11);
}
if(count == 2)
return hints(complex_numbers,11);
}
}
if(iscomplex(p) && f != '/' && f != '*' && f != '-' && f != '+')
return hints(complex_numbers,11);
}
break;
case COMMON_DENOMINATOR:
/* Reject it if it contains a sum that contains
a fraction. */
if(nsteps == 0)
{ *flag = 1;
return english(1672); /* That's a good answer */
}
if(needs_commondenom(p))
return english(1675);
/* There are still fractions to put over a common denominator. */
break;
case COMPOUND_FRACTIONS:
/* Reject it if it contains a fraction whose
numerator or denominator contains a fraction. */
if(nsteps == 0)
{ *flag = 1;
return english(1672); /* That's a good answer */
}
if(contains_cf(p))
return english(1676);
/* There is still a compound fraction to be eliminated. */
break;
case NEGATIVE_EXPONENTS:
/* Reject it if it contains a fraction which
contains_monomially an exponent in the denominator */
if(nsteps == 0)
{ *flag = 1;
return english(1672); /* That's a good answer */
}
if(needs_negexp(p))
return english(1676);
/* There is still a compound fraction to be eliminated. */
break;
case ELIMINATE_NEGATIVE_EXPONENTS:
/* Reject it if if contains a negative exponent */
if(nsteps == 0)
{ *flag = 1;
return english(1672); /* That's a good answer */
}
if(contains_negexp(p))
return english(1677);
/* There is still a negative exponent to be eliminated. */
break;
case FRACTIONAL_EXPONENTS:
if(nsteps == 0)
{ *flag = 1;
return english(1672); /* That's a good answer */
}
/* Reject if it contains a root or sqrt */
if(contains(p,ROOT) || contains(p,SQRT))
return english(1678);
/* There is still a root to be eliminated. */
break;
case ROOTS:
if(nsteps == 0)
{ *flag = 1;
return english(1672); /* That's a good answer */
}
/* Reject if it contains a fractional exponent */
if(contains_ratexp(p))
return english(1678);
/* There is a fractional exponent to be eliminated. */
break;
case BINOMIAL_THEOREM: /* fall through */
case EXPAND:
if(nsteps == 0)
{ *flag = 1;
return english(1672); /* That's a good answer */
}
/* Reject it if it contains an expandable
product */
err = needs_expand(p);
if(err == 2)
return english(1679);
/* There is a product of sums to multiply out. */
if(err == 1)
return english(1680);
/* There is a power of a sum to expand. */
break;
case COMPLEX_NUMBERS:
/* if you change this, changed SolvedDoc in getprob\autolog.c too */
switch(currenttopic)
{ case _complex_arithmetic: /* fall-through */
case _de_moivre:
if(!iscomplex(p))
break; /* i was eliminated, answer is real */
if(complex_number(p))
break; /* answer has the form a + bi */
return english(1822);
/* Reduce complex numbers to the form $a + bi$. */
case _polar_form:
if(is_polar_complex(p))
break;
if(!complexparts(p,&u,&v) && ZERO(v))
{ /* lastline has no imaginary part, but we still
have to watch out for negative real part. */
if(NEGATIVE(u) && obviously_positive(ARG(0,u)))
return english(1951);
/* That expression is real, but it is negative, so
it's not in polar form, which is $re^(it)$ with
$r$ positive. */
if(obviously_positive(u))
break; /* acceptable */
err = check1(le(zero,u));
if(!err)
break;
return english(1951);
/* That expression is real, but it is negative, so
it's not in polar form, which is $re^(it)$ with
$r$ positive. */
}
return english(1821);
/* That expression is not in polar form. Polar form is re^(iT). */
default:
assert(0); /* no more topics for COMPLEX_NUMBERS */
}
break;
case TRIG_EXPAND:
break;
case INDUCTION:
/* It's acceptable if 'thereforeasdesired' has been used
and the current line is an identity. */
break;
case MINMAX:
if(equals(p,falseterm))
break;
if(used2(FUNCTIONISCONSTANT))
{ clear_comment_buffer();
break;\
}
if(!used2(TABULATE) && !used2(TABULATEEXACT))
{ clear_comment_buffer();
return english(2228);
/* You must compute a table of function values and select the maximum or minimum. */
}
if(!used2(SELECTMIN) && !used2(SELECTMAX) && !get_minflag() && !get_maxflag() && !get_minmaxflag())
{ clear_comment_buffer();
return english(2229);
/* You should select the maximum and/or the minimum to complete the problem. */
}
break;
case RELATED_RATES:
if(solved_related_rates(p))
return english(1923);
/* You must solve each equation, either for a derivative or a variable. */
break;
}
/* If we get here, then the answer hasn't been rejected. This means
the main goal of the problemtype has been met: an equation is
solved, an integral sign or limit is eliminated, etc. The question
is whether the answer is sufficiently simplified.
*/
clear_comment_buffer();
clear_error_buffer();
if(nsteps == 0)
{ *flag = 1;
return english(1672); /* That's a good answer */
}
/* Now check for the case in which plan contains only
trivial operations. */
for(i=0;i<nsteps;i++)
{ if(!trivial_op(plan[i]))
break;
}
if(i == nsteps)
/* The next steps are trivial and after all the
main goal has already been achieved. */
{ *flag = 1;
return english(1672); /* That's a good answer */
}
if(nsteps == 1)
{ *flag = 3;
return english(1682);
/* Your answer is acceptable, but perhaps it could be
simplified. Mathpert would take one more step. */
}
if(nsteps == 2)
return english(1683);
/* Your answer is acceptable, but perhaps it could be
simplified. Mathpert would take two more steps. */
return english(1684);
/* Your answer is acceptable, but perhaps it could be
simplified. Mathpert would take at least three more steps. */
}
/*______________________________________________________________________*/
static int solved_system(term p)
/* return 1 if the equation or system of equations p is solved */
{ unsigned n,f,i;
term lhs, rhs, swap;
n = ARITY(p);
f = FUNCTOR(p);
if(equals(p,falseterm))
return 1; /* No solution; 'That's the answer' is appropriate */
if(INEQUALITY(f))
{ lhs = ARG(0,p);
rhs = ARG(1,p);
if(!ATOMIC(lhs) && f != '=' && ATOMIC(rhs))
{ swap = lhs;
lhs = rhs;
rhs = swap;
}
if(!ATOMIC(lhs))
return 0;
if(!constant(rhs))
return 0;
if(contains(rhs,DET) && seminumerical(rhs))
return 0; /* in Cramer's rule, you must evaluate numerical determinants */
return 1; /* This still counts x = 2/2 as solved. */
}
if(f == AND || f == OR)
{ for(i=0;i<n;i++)
{ if(!solved_system(ARG(i,p)))
return 0;
}
return 1;
}
/* Note the above code also works on interval_as_and terms */
return 0;
}
/*________________________________________________________________*/
static int needs_commondenom(term t)
/* return 1 if t contains a sum, one of whose summands is a
fraction or power of a fraction, or a product which has a fraction
or power of a fraction as a factor. */
{ unsigned short f,n;
int i,j;
term u,v;
if(ATOMIC(t))
return 0;
f = FUNCTOR(t);
n = ARITY(t);
if(f != '+')
{ for(i=0;i<n;i++)
{ if(needs_commondenom(ARG(i,t)))
return 1;
}
return 0;
}
for(i=0;i<n;i++)
{ u = ARG(i,t);
while(NEGATIVE(u) || FUNCTOR(u) == '^')
u = ARG(0,u);
if(FRACTION(u))
return 1;
if(FUNCTOR(u) == '*')
{ for(j=0;j<ARITY(u);j++)
{ v = ARG(j,u);
while(NEGATIVE(v) || FUNCTOR(v) == '^')
v = ARG(0,v);
if(FRACTION(v))
return 1;
}
}
}
return 0;
}
/*_________________________________________________________________________*/
static int contains_cf(term t)
/* return 1 if t contains a compound fraction */
{ unsigned short f,n;
int i;
term u;
if(ATOMIC(t))
return 0;
n = ARITY(t);
f = FUNCTOR(t);
if(f == '/')
{ for(i=0;i<2;i++)
{ u = ARG(i,t);
if(contains_monomially(u,'/'))
return 1; /* example: e^(1/2)/2 is ok */
}
}
for(i=0;i<n;i++)
{ if(contains_cf(ARG(i,t)))
return 1;
}
return 0;
}
/*____________________________________________________________________*/
static int needs_negexp(term t)
/* return 1 if t contains an exponent as a denominator or a factor
of a denominator. */
{ int i,j;
unsigned short f,n;
term u,v;
if(ATOMIC(t))
return 0;
f = FUNCTOR(t);
n = ARITY(t);
if(f == '/')
{ if(needs_negexp(ARG(0,t)))
return 1;
u = ARG(1,t);
while(NEGATIVE(u))
u = ARG(0,u);
if(FUNCTOR(u) == '^')
return 1;
if(FUNCTOR(u) == '*')
{ for(j=0;j<ARITY(u);j++)
{ v = ARG(j,u);
while(NEGATIVE(v))
v = ARG(0,v);
if(FUNCTOR(v) == '^')
return 1;
}
}
return needs_negexp(u);
}
for(i=0;i<n;i++)
{ if(needs_negexp(ARG(i,t)))
return 1;
}
return 0;
}
/*________________________________________________________*/
static int contains_ratexp(term t)
/* return 1 if t contains a rational exponent */
{ int i,j;
unsigned short n,f;
term u,v;
if(ATOMIC(t))
return 0;
f = FUNCTOR(t);
n = ARITY(t);
if(f == '^')
{ u = ARG(1,t);
while(NEGATIVE(u))
u = ARG(0,u);
if(RATIONALP(u))
return 1;
if(FUNCTOR(u) == '*')
{ for(j=0;j<ARITY(u);j++)
{ v = ARG(j,u);
while(NEGATIVE(v))
v = ARG(0,v);
if(RATIONALP(v))
return 1;
}
}
}
for(i=0;i<n;i++)
{ if(contains_ratexp(ARG(i,t)))
return 1;
}
return 0;
}
/*________________________________________________________*/
static int contains_negexp(term t)
/* return 1 if t contains a negative exponent, also
if the minus sign is in num or denom of a fractional exponent.
*/
{ int i,j;
unsigned short n,f;
term u,v;
if(ATOMIC(t))
return 0;
f = FUNCTOR(t);
n = ARITY(t);
if(f == '^')
{ u = ARG(1,t);
if(NEGATIVE(u))
return 1;
if(FRACTION(u))
{ if(NEGATIVE(ARG(0,u)) || NEGATIVE(ARG(1,u)))
return 1;
for(i=0;i<2;i++)
{ v = ARG(i,u);
if(FUNCTOR(v) == '*')
{ for(j=0;j<ARITY(v);j++)
{ if(NEGATIVE(ARG(j,v)))
return 1;
}
}
}
}
}
for(i=0;i<n;i++)
{ if(contains_negexp(ARG(i,t)))
return 1;
}
return 0;
}
/*________________________________________________________________________*/
static int needs_expand(term t)
/* return 1 or 2 if t contains a power or product or sums
that can be expanded, 0 if not. */
{ unsigned f = FUNCTOR(t);
unsigned n = ARITY(t);
int count,err;
unsigned i;
if(ATOMIC(t))
return 0;
if (f == '^' && FUNCTOR(ARG(0,t)) == '+')
{ term power = ARG(1,t);
if(!ISINTEGER(power))
return 0; /* You can't expand a bignum power anyway */
return 1;
}
if (f == '*')
{ count = 0; /* count the sums */
for(i=0;i<n;i++)
{ if(FUNCTOR(ARG(i,t))== '+')
++count;
}
if(count > 1)
return 2;
}
for(i=0;i<n;i++)
{ err = needs_expand(ARG(i,t));
if(err)
return err;
}
return 0;
}
/*___________________________________________________________*/
static int noderivs(term t)
/* return 1 if t has no evaluable derivatives. It may
contain a derivative like df/dx where f is neither
pre-defined nor user-defined */
{ unsigned short f,n;
int i;
if(ATOMIC(t))
return 1;
n = ARITY(t);
f = FUNCTOR(t);
if(f == DIFF)
{ f = FUNCTOR(ARG(0,t));
if(PREDEFINED_ATOM(f))
return 1;
if( ('a' <= f && f <= 'z') || ('A' <= f && f <= 'Z'))
return 1;
return 0;
}
for(i=0;i<n;i++)
{ if(!noderivs(ARG(i,t)))
return 0;
}
return 1;
}
/*________________________________________________*/
static actualop trivialops[] =
{ orderterms,
orderfactors,
apart,
factoroutnumber,
contentfactor,
cleardenoms,
pulloutrational,
polyvalop,
switchsides
};
static int trivial_op(operation op)
/* return 1 if o is 'trivial', i.e. an answer is
acceptable if all Mathpert wants to do to it is
apply trivial ops.
*/
{ unsigned n = sizeof(trivialops)/sizeof(actualop);
unsigned i;
actualop o = access_optable(op.men)[op.choice-1];
for(i=0;i<n;i++)
{ if((void *) o == (void *) trivialops[i])
return 1;
}
return 0;
}
/*__________________________________________________________________*/
int solved_related_rates(term t)
/* return 0 if t counts as a solution to a related rates problem, 1 if not.
A good solution should be an equation with an atom on the left
and that atom not occurring on the right, or a derivative
of an atom on the left, and no DIFF on the right,
or an AND of such expressions. A PROTECTED equation can still
remain in the answer; that's just the original equation.
*/
{ unsigned short n;
int i;
term u;
if(FUNCTOR(t) == AND)
{ n = ARITY(t);
for(i=0;i<n;i++)
{ u = ARG(i,t);
if(FUNCTOR(u) != '=')
return 1;
if(PROTECTED(u))
continue; /* ignore original equation */
if(FUNCTOR(ARG(0,u)) == DIFF)
{ if(!ISATOM(ARG(0,ARG(0,u))))
return 1;
if(contains(ARG(1,u),DIFF))
return 1;
}
else if(!ISATOM(ARG(0,u)) || contains(ARG(1,u),FUNCTOR(ARG(0,u))))
return 1;
}
return 0;
}
if(FUNCTOR(t) == '=' && FUNCTOR(ARG(0,t)) == DIFF &&
ISATOM(ARG(0,ARG(0,t))) && !contains(ARG(1,t),DIFF)
)
return 0;
if(FUNCTOR(t) == '=' && ISATOM(ARG(0,t)) &&
!contains(ARG(1,t),FUNCTOR(ARG(0,t)))
)
return 0;
return 1;
}
/*________________________________________________________________________*/
static int contains_trigproduct(term t)
/* return 1 if t contains f(x)g(y) where f and
g are trig functions.
*/
{ unsigned short n,f;
int i,j,count;
if(ATOMIC(t))
return 0;
n = ARITY(t);
if(FUNCTOR(t) == '*')
{ count = 0;
for(j=0;j<ARITY(t);j++)
{ f = FUNCTOR(ARG(j,t));
if(TRIGFUNCTOR(f))
++count;
}
return count > 1 ? 1 : 0;
}
for(i=0;i<n;i++)
{ if(contains_trigproduct(ARG(i,t)))
return 1;
}
return 0;
}
/*_____________________________________________________*/
int contains_zero_denom(term t)
/* return 1 if t contains a zero in a denominator, or
a power of zero in a denominator. */
{ unsigned short n,i;
term denom;
if(ATOMIC(t))
return 0;
if(FRACTION(t))
{ denom = ARG(1,t);
if(ZERO(denom))
return 1;
if(FUNCTOR(denom) == '^' && ZERO(ARG(0,denom)))
return 1;
if(contains_zero_denom(ARG(0,t)))
return 1;
return 0;
}
n = ARITY(t);
for(i=0;i<n;i++)
{ if(contains_zero_denom(ARG(i,t)))
return 1;
}
return 0;
}
/*__________________________________________________________*/
int linear_in_series(term t)
/* return 1 if t is a linear combination of infinite series or polynomials or reciprocals of monomials */
{ unsigned short n,f,i;
int count,flag;
int nvariables = get_nvariables();
term a,u,c,s;
term x = get_eigenvariable();
if(mvpoly(t))
return 1;
f = FUNCTOR(t);
if(f == '-')
{ t = ARG(0,t);
f = FUNCTOR(t);
}
if(f == SUM)
return nvariables > 1 ? is_power_series(t,x,&a) : 1;
n = ARITY(t);
if(f == '*')
{ count = 0;
for(i=0;i<n;i++)
{ if(constant(ARG(i,t)) && !contains(ARG(i,t),SUM))
continue;
if(FUNCTOR(ARG(i,t)) == SUM)
{ if(count)
return 0;
++count;
flag = i;
}
}
if(count == 0)
{ if(constant(t))
return 1;
if(nvariables && ispolyin(t,x))
return 1;
return 0;
}
return linear_in_series(ARG(flag,t));
}
if(f == '+')
{ for(i=0;i<n;i++)
{ u = ARG(i,t);
if(NEGATIVE(u))
u = ARG(0,u);
if(constant(u))
continue;
if(FUNCTOR(u) == '*' || FUNCTOR(u) == SUM)
{ if(!linear_in_series(u))
return 0;
continue;
}
if(nvariables > 1 && ispolyin(u,x))
continue;
if(FRACTION(u) && numerical(ARG(0,u)) && ismonomial(ARG(1,u),x,&c,&s))
continue;
return 0;
}
return 1;
}
return 0;
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists