Sindbad~EG File Manager
/* M. Beeson, for MathXpert
7.6.94 appx extracted from simpsums.c
6.14.98 last modified
5.6.13 added include trig.h, calc.h
*/
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include "globals.h"
#include "ops.h" /* prototypes of operators */
#include "prover.h"
#include "order.h"
#include "cancel.h"
#include "integral.h"
#include "factor.h" /* ratpart2 */
#include "deval.h"
#include "pvalaux.h" /* summands */
#include "simpsums.h" /* additivecancel_aux */
#include "symbols.h"
#include "polynoms.h" /* partialfractions */
#include "mathmode.h" /* get_mathmode */
#include "errbuf.h"
#include "mstring.h"
#include "pathtail.h"
#include "trig.h" /* univariatepoly */
#include "calc.h" /* partialfractionsop */
static void collect_aux(term, term *, int *, int *,char *);
/*_____________________________________________________________________*/
/* collect all possible groups of like terms */
/* return 1 for failure, 0 for success */
/* in the event of failure, *next = t */
/* capable of working on both sides of an equation or all equations in
an AND, but in practice won't because of the leftmost-focus rule; however,
it's called by eqnscollectall (file eqns.c) which rejects input with
functor other than AND, so that capability gets used. */
int collectall(term t, term arg, term *next, char *reason)
{ int cancelflag,collectflag;
collect_aux(t,next,&cancelflag,&collectflag,reason);
if(!cancelflag && !collectflag) /* nothing done */
return 1;
if(cancelflag==1 && !collectflag) /* only one cancellation happened */
return 0; /* leaving reason as generated by collect_aux */
else if(cancelflag && !collectflag) /* several cancellations, no collection */
strcpy(reason, english(439)); /* cancel terms */
else if(cancelflag) /* both cancellations and collection took place */
strcpy(reason, english(437)); /* collect and cancel */
else /* no cancellations, only collections */
strcpy(reason, english(438)); /* collect like terms */
return 0;
}
/*_____________________________________________________________________*/
static void collect_aux(term t, term *next, int *cancelflag, int *collectflag, char *reason)
/* perform all possible collections and cancellations on t,
returning in cancelflag and collectflag how many of each were done,
and in case only one cancellation and no collection was done, returning
a reason string mentioning the term cancelled */
/* Works on sums, or on both sides of an equation or inequality, or on
all args of an AND */
{ int err,cancel_occurred;
term temp,new;
unsigned short f = FUNCTOR(t);
unsigned short n = ARITY(t);
int i,pp,qq,mathmode,changedarith;
aflag arithflag;
temp = t;
err=0;
mathmode = get_mathmode();
changedarith = 0;
if(mathmode != AUTOMODE)
{ arithflag = get_arithflag();
if(arithflag.comdenom == 0)
{ arithflag.comdenom = 1;
set_arithflag(arithflag);
changedarith = 1;
}
}
*cancelflag=*collectflag=0;
if(f == AND || f == '=' || f == '<' || f == '>' || f == LE || f == GE)
{ *next = make_term(f,n);
for(i=0;i<n;i++)
{ collect_aux(ARG(i,t),ARGPTR(*next)+i,&pp,&qq,reason);
*cancelflag +=pp;
*collectflag +=qq;
}
if(collectflag ==0 && cancelflag==0)
{ RELEASE(*next);
*next = t;
}
if(changedarith)
{ arithflag.comdenom = 0;
set_arithflag(arithflag);
}
return;
}
if(f != '+')
{ *next = t;
if(changedarith)
{ arithflag.comdenom = 0;
set_arithflag(arithflag);
}
return;
}
while(err==0)
{ err = collect1(0,temp,&new,&cancel_occurred);
if(cancel_occurred == 2)
err = additivecancel(temp,zero,next,reason);
/* get the right reason string */
if(err == 0)
{ temp = new;
*cancelflag += cancel_occurred;
++*collectflag;
}
}
if( !*cancelflag && !*collectflag) /* nothing done */
*next = t;
else
{ *next = temp;
additive_sortargs(*next); /* put the args in order if something else is done */
}
if(changedarith)
{ arithflag.comdenom = 0;
set_arithflag(arithflag);
}
return;
}
/*_________________________________________________________________*/
static int additivecancel3(term t, term *cancelled, term *next)
/* cancel once additively in t, returning the cancelled term and the
result of the cancellation indirectly; return 0 for success */
{ unsigned short k,m;
int i;
int buf1=0; /* set if buffer1 is dynamically allocated, so it can be freed */
term *buffer1, *buffer2;
if(FUNCTOR(t)!= '+')
return 1; /* inapplicable */
buffer1 = ARGPTR(t);
k = ARITY(t);
buffer2 = callocate(k,sizeof(term));
if(buffer2==NULL)
{ nospace();
return 1;
}
additivecancel_aux(buffer1,k,buffer2,&m,cancelled);
if(m==k)
{ /* no cancellation took place; try harder: */
k = count_summands(t);
assert(k>0);
buffer1 = callocate(k,sizeof(term));
buf1=1;
if(buffer1 == NULL)
{ nospace();
return 1;
}
sum_aux(t,buffer1,&m,0);
buffer2 = callocate(k,sizeof(term));
if(buffer2==NULL)
{ nospace();
return 1;
}
additivecancel_aux(buffer1,k,buffer2,&m,cancelled);
if(m==k) /* no cancellation took place; clean up and fail */
{ if(buf1)
free2(buffer1);
free2(buffer2);
return 1;
}
}
if(m==1)
*next = buffer2[0];
else if(m==0)
*next = zero;
else
{ *next = make_term('+',m);
for(i=0;i<m;i++)
ARGREP(*next,i,buffer2[i]);
}
HIGHLIGHT(*next);
if(buf1)
free2(buffer1);
free2(buffer2);
return 0;
}
/*______________________________________________________________________*/
int additivecancel(term t, term arg, term *next, char *reason)
/* Cancel additively in t (once) getting *next */
{ term cancelled;
char str[80]; /* for the string form of the cancelled term */
int err = additivecancel3(t,&cancelled,next);
if(err)
return 1;
err = mstring(cancelled,str);
if(err || strlen(str) > 15)
strcpy(reason, english(435)); /* cancel \pm terms */
else
{ strcpy(reason, english(436)); /* cancel */
strcat(reason,str);
}
return 0;
}
/*_________________________________________________________________*/
int partialfractionsop(term t , term arg, term *next, char *reason)
/* the operator: arg is the variable x (of integration for example)
in which t is a rational function */
/* arg is selected automatically as varlist[eigenvariable]. When
integrating, eigenvariable always indexes the variable of integration,
so this will be correct. If we later allow nested integrals, we must
make sure to change eigenvariable when we go from one integral to
another and back out. */
{ int err;
term denom;
if(FUNCTOR(arg)==ILLEGAL) /* in menu mode from integration menu */
arg = get_eigenvariable();
if(FUNCTOR(t) != '/')
return 1;
denom = ARG(1,t);
if(FUNCTOR(denom) == '+' && contains(denom,'^'))
{ errbuf(0, english(431));
/* You must factor the denominator first. */
return 1;
}
if(FUNCTOR(denom) != '*' && FUNCTOR(denom) != '^')
return 1; /* example, (x-1)^2/x */
if(FUNCTOR(denom) == '*' && ARITY(denom) > 4)
{ errbuf(0,english(1989));
/* Denominator too complicated to expand in partial fractions */
return 1;
}
err = partialfractions(t,arg,next);
if(err || equals(t,*next))
return 1;
strcpy(reason, english(432)); /* partial fractions */
HIGHLIGHT(*next);
return 0;
}
/*________________________________________________________________*/
int univariatepoly(term t, term arg, term *next, char *reason)
/* t must contain exactly one variable. Find a polynomial
equivalent to t and return it in *next */
{ int err;
term *atomlist;
term temp,x;
int nvariables;
if(seminumerical(t))
return 1; /* there must be a variable */
nvariables = variablesin(t,&atomlist);
if(nvariables > 1)
{ free2(atomlist);
errbuf(0, english(1385));
/* Only one variable is allowed. Consider using... */
return 1;
}
assert(nvariables == 1);
x = atomlist[0];
free2(atomlist);
err = makepoly(t,x,&temp);
if(!err)
{ *next = poly_term(temp,x);
if(equals(*next,t))
{ errbuf(0, english(1386));
/* That expression is already in polynomial form. */
RELEASE(temp);
return 1;
}
strcpy(reason,english(1384)); /* polynomial form */
return 0;
}
err = polyform(t,x,&temp); /* temp will be a POLYnomial */
if(err)
return 1;
*next = poly_term(temp,x);
strcpy(reason,english(1384)); /* polynomial form */
return 0;
}
/*________________________________________________________________*/
int monicpoly(term t, term arg, term *next, char *reason)
/* t must contain exactly one variable and be a polynomial
in that variable. Factor out the leading coefficient.
Example: 3x^2 + 2x + 1 becomes 3(x^2 + 2/3 x + 1/3)
*/
{ int i,err;
term *atomlist;
term temp,x,a,anspoly;
int nvariables;
unsigned short n;
if(get_mathmode() != SELECTIONMODE)
{ errbuf(0, english(1488));
/* You must select the expression to be worked on. */
return 1;
}
if(seminumerical(t))
return 1; /* there must be a variable */
nvariables = variablesin(t,&atomlist);
if(nvariables > 1)
{ free2(atomlist);
errbuf(0, english(1385));
/* Only one variable is allowed. Consider using... */
return 1;
}
assert(nvariables == 1);
x = atomlist[0];
free2(atomlist);
err = makepoly(t,x,&temp);
if(err)
{ errbuf(0, english(1487));
/* Expression must be in polynomial form. */
return 1;
}
n = ARITY(temp);
a = ARG(n-1,temp); /* the leading coefficient */
if(ONE(a))
{ errbuf(0, english(1489));
/* Leading coefficient is already 1 */
return 1;
}
anspoly = make_term(POLY,n);
/* Now divide each coefficient by a */
for(i=0;i<n-1;i++)
polyval(make_fraction(ARG(i,temp),a),ARGPTR(anspoly)+i);
ARGREP(anspoly,n-1,one);
*next = product(a,poly_term(anspoly,x));
HIGHLIGHT(*next);
strcpy(reason,english(1490)); /* make leading coef 1 */
return 0;
}
/*________________________________________________________*/
int pullminusout(term t, term arg, term *next, char *reason)
/* -a-b = -(a+b) */
{ int i;
unsigned short n;
term u;
if(FUNCTOR(t) != '+')
return 1;
n = ARITY(t);
for(i=0;i<n;i++)
{ if(!NEGATIVE(ARG(i,t)))
return 1;
}
u = make_term('+',n);
for(i=0;i<n;i++)
ARGREP(u,i,ARG(0,ARG(i,t)));
tneg(u,next);
HIGHLIGHT(*next);
strcpy(reason,"-a-b = -(a+b)");
return 0;
}
/*________________________________________________________*/
int pullminusout2(term t, term arg, term *next, char *reason)
/* a(b-c) = -a(c-b) */
{ int i,j,k,err;
unsigned short n;
unsigned short path[3];
term u,v,cancelled;
if(FUNCTOR(arg) == ILLEGAL && FUNCTOR(t) == '+' && ARITY(t) == 2 &&
FUNCTOR(ARG(0,t)) == '*' && FUNCTOR(ARG(1,t)) == '*'
)
{ /* called from automode on a sum containing a product */
/* a(c-b) + d(b-c) = a(c-b) - d(c-b) */
for(i=0;i<ARITY(ARG(0,t));i++)
{ if(FUNCTOR(ARG(i,ARG(0,t))) == '+')
break;
}
if(i==ARITY(ARG(0,t)))
return 1;
for(j=0;j<ARITY(ARG(1,t));j++)
{ if(FUNCTOR(ARG(j,ARG(1,t))) == '+' &&
eqtest(ARG(i,ARG(0,t)), ARG(j,ARG(1,t))) == -1
)
break;
}
if(j==ARITY(ARG(1,t)))
return 1;
err = pullminusout2(ARG(1,t),arg,&u,reason);
if(!err)
{ *next = sum(ARG(0,t),u);
path[0] = '+';
path[1] = 2;
path[2] = 0;
set_pathtail(path);
return 0;
}
err = pullminusout2(ARG(0,t),arg,&u,reason);
if(!err)
{ *next = sum(u,ARG(1,t));
path[0] = '+';
path[1] = 1;
path[2] = 0;
set_pathtail(path);
return 0;
}
return 1;
}
if(FUNCTOR(arg) == ILLEGAL && FUNCTOR(t) == '+' && ARITY(t) == 3)
{ /* a - b + c(b - a) = a - b - c(b-a) */
for(k=0;k<3;k++)
{ u = k == 0 ? sum(ARG(1,t),ARG(2,t)) :
k == 1 ? sum(ARG(0,t),ARG(3,t)) :
sum(ARG(0,t),ARG(1,t));
if(!cancel(ARG(k,t),u,&cancelled,&v))
{ err = pullminusout2(ARG(k,t),arg,&u,reason);
if(!err)
{ *next = make_term('+',3);
for(i=0;i<3;++i)
ARGREP(*next,i, i==k ? u : ARG(i,t));
path[0] = '+';
path[1] = k+1;
path[2] = 0;
set_pathtail(path);
return 0;
}
}
}
return 1;
}
if(FUNCTOR(t) != '*')
return 1;
n = ARITY(t);
/* look for a factor which is a sum with a negative summand */
for(i=0;i<n;i++)
{ u = ARG(i,t);
if(FUNCTOR(u) == '+')
/* look for a negative summand */
{ for(j=0;j<ARITY(u);j++)
{ if(NEGATIVE(ARG(j,u)))
break; /* found one */
}
if(j < ARITY(u))
break;
}
}
if(i==n)
return 1; /* no sum with a negative summand is a factor */
u = make_term('*',n);
for(k=0;k<n;k++)
{ if(k != i)
{ ARGREP(u,k,ARG(k,t));
continue;
}
v = strongnegate(ARG(k,t));
if(FUNCTOR(v) == '+' && ARITY(v) == 2 &&
NEGATIVE(ARG(0,v)) && !NEGATIVE(ARG(1,v))
)
v = sum(ARG(1,v),ARG(0,v));
else if(FUNCTOR(v) == '+' && ARITY(v) > 2 && NEGATIVE(ARG(0,v)))
additive_sortargs(v);
ARGREP(u,k,v);
}
tneg(u,next);
HIGHLIGHT(*next);
strcpy(reason,"a(b-c) = -a(c-b)");
return 0;
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists