Sindbad~EG File Manager
/* functions to help with solving sin x + cos x = 1 and similar
equations containing two different trig functors */
/* M. Beeson, for MathXpert */
/*
Original date 8.18.93
Last modified 3.1.98
*/
#define ALGEBRA_DLL
#include <assert.h>
#include "globals.h"
#include "probtype.h"
#include "eqn.h"
#include "trigtran.h"
#include "prover.h"
#include "trigpoly.h"
static int contains_nonconstant(term, unsigned short );
static int trig_transfer_aux(unsigned short ,int *,term,term *);
static int algebraic_fractions(term);
static int trig_transfer2(int *dir,term eqn,term *arg);
#define COMPLEMENT(g) ((unsigned short)(g==SIN ? COS : g==COS ? SIN : g==TAN ? SEC : g==SEC ? TAN : g==COT ? CSC : COT))
/*__________________________________________________________________*/
int trig_transfer(int *dir, term eqn, term *arg, term *next,char *reason)
/* called in auto mode; detect equations containing odd powers of sin AND
odd powers of cosine. Transfer terms to put the odd powers of cos on the
right and the odd powers of sin on the left. (Later the equation will be squared and
then sin^2 + cos^2 = 1 will be used to eliminate one of cos or sin. In some
cases a second squaring is necessary.) */
/* Return 1 if there are fractions outside the trig functions; in that
case automode should use common denoms etc. rather than separate-and-square.
This function also returns 1 if a single term contains both SIN and COS,
e.g. on 5 sin x cos x - 5 cos^2 x -1 = 0 nothing will be done.
See eqn.c for more comments. */
/* return 0 for success, 1 means we don't want to transfer anything */
{ int err;
int ncos,nsin,nsec,ntan,ncsc,ncot;
term x;
err = assess_trig(eqn,&nsin,&ncos,&ntan,&nsec,&ncot,&ncsc,0,&x);
if(err)
return 1;
/* Now we found at least some odd-powered trig functions, and no two with
different arguments. */
if(algebraic_fractions(eqn))
return 1;
if(nsin && ncos && !ntan && !ncsc && !nsec && !ncot)
/* sin and cosine only */
/* put SIN on the left and isolate COS on the right */
{ err = trig_transfer_aux(COS,dir,eqn,arg);
if(err)
{ /* example, 5 sin x cos x - 5 cos^2 x - 1 = 0.
If the expression is linear in sin x then transfer
terms not containing sin x to the right, etc. */
err = trig_transfer2(dir,eqn,arg);
if(err)
return 1;
}
return transfer_aux(*dir,eqn,*arg,next,reason);
}
if(ntan && nsec && !nsin && !ncsc && !ncos && !ncot)
/* sec and tan only */
/* put TAN on the left and isolate SEC on the right */
{ trig_transfer_aux(SEC,dir,eqn,arg);
return transfer_aux(*dir,eqn,*arg,next,reason);
}
if(ncot && ncsc && !nsin && !nsec && !ncos && !ntan)
/* csc and cot only */
/* put COT on the left and isolate CSC on the right */
{ trig_transfer_aux(CSC,dir,eqn,arg);
return transfer_aux(*dir,eqn,*arg,next,reason);
}
return 1;
}
/*____________________________________________________________*/
MEXPORT_ALGEBRA int assess_trig( term t,
int *nsin, int *ncos,
int *ntan, int *nsec,
int *ncot, int *ncsc,
int flag, term *x
)
/*
if t contains trig functions all with the same non-constant argument,
occurring to an odd power, tell how many of each kind, and the
common argument of all these trig functions comes back in *x. Don't
count constants like sin(1). Ignore (specific)
even powers of trig functions. Powers which are not specific integers
are counted, even powers like sin^2� x.
If there are two trig functions with
different non-constant arguments, fail (return 2).
Return 0 if there are some trig functions occurring to non-even-integer powers.
Return 1 if all trig functions occur to even integer powers.
If flag is nonzero,
then a meaningful value is passed in *x, and all the trig functions
must have this value for argument, or the function must fail. */
{ int ns,nc,nt,ncs,nse,nco;
int i,err;
unsigned short n = ARITY(t);
unsigned short f = FUNCTOR(t);
int localflag, success = 0;
term u;
*nsin = *ncos = *nsec = *ntan = *ncsc = *ncot = 0;
if(ATOMIC(t))
return 1;
if(f==SIN || f==COS || f==TAN || f==SEC || f==CSC || f==COT)
{ if(flag == 0)
{ if(econstant(ARG(0,t)))
return 1;
else
*x = ARG(0,t);
}
else
if(!equals(*x,ARG(0,t)))
return 2;
switch(f)
{ case SIN: *nsin = 1; return 0;
case COS: *ncos = 1; return 0;
case TAN: *ntan = 1; return 0;
case COT: *ncot = 1; return 0;
case CSC: *ncsc = 1; return 0;
case SEC: *nsec = 1; return 0;
}
}
localflag = flag;
if(f == '^')
{ /* ignore the exponent entirely. Ignore the base too if the
exponent is a specific even integer and the base doesn't
contain a sum. */
u = ARG(1,t);
if(INTEGERP(u) && ISEVEN(u) && !contains(ARG(0,t),'+'))
return 1;
return assess_trig(ARG(0,t),nsin,ncos,ntan,nsec,ncot,ncsc,flag,x);
}
for(i=0;i<n;i++)
{ u = ARG(i,t);
err = assess_trig(u,&ns,&nc,&nt,&nse,&nco,&ncs,localflag,x);
if(!err)
{ localflag = 1; /* Now *x is determined */
success = 1; /* found some non-even-powered trig functions */
*nsin += ns;
*ncos += nc;
*ntan += nt;
*ncsc += ncs;
*nsec += nse;
*ncot += nco;
}
else if(err == 2)
return 2; /* trig functions with different arguments */
}
if(success)
return 0;
return 1;
}
/*______________________________________________________________*/
static int trig_transfer_aux(unsigned short g,int *dir,term eqn,term *arg)
/* g is a trig function, eqn is an equation or inequality, known to contain
g with a nonconstant argument. We want to put nonconstant terms containing
odd powers of g on the right, and all other terms on the left, including
constant terms and those containing only even powers of g. Specifically:
Return in *arg a term to be transferred: viz.,
all terms on the left containing g with nonconstant arg and odd power,
if there are any, or if not, all terms on the right not containing g with
nonconstant arg and odd power, if there are any. Return 0 for success.
Return *dir =0 if the transfer
is to be left-to-right, or 1 if it is to be right-to-left.
If all terms with f are already on the left and all
terms with g are already on the right, return 1.
If any of the terms to be transferred contain BOTH f and g,
fail. Example: 3 sin x - sin x cos x = 0. Factoring should take
place, but it won't be tried till alltoleft turns it on in post_ops,
while transfereqn is called in pre_ops. */
{ int i;
unsigned short k, n;
term left = ARG(0,eqn);
term right = ARG(1,eqn);
term u,temp;
if(FUNCTOR(left) != '+' && contains_nonconstant(left,g))
return 1; /* No point transferring the whole left side */
if(FUNCTOR(left) == '+' && contains_nonconstant(left,g))
{ n = ARITY(left);
*dir = 0; /* left-to-right transfer */
temp = make_term('+',n);
k=0;
for(i=0;i<n;i++)
{ u = ARG(i,left);
if(contains_nonconstant(u,g))
{ if(contains_nonconstant(u,COMPLEMENT(g)))
return 1; /* factoring should be tried first */
ARGREP(temp,k,u);
++k;
}
}
assert(k>0);
if(k==1)
{ *arg = ARG(0,temp);
RELEASE(temp);
return 0;
}
if(k==n)
{ RELEASE(temp);
return 1; /* don't move the whole left side */
}
SETFUNCTOR(temp,'+',k);
*arg = temp;
return 0;
}
if(ZERO(right))
return 1; /* otherwise we wind up transferring zero */
*dir = 1; /* Now we're going to move all terms NOT containing g to the left */
if(FUNCTOR(right) != '+' && !contains_nonconstant(right,g))
{ *arg = right;
return 0;
}
if(FUNCTOR(right) == '+')
{ n = ARITY(right);
temp = make_term('+',n);
k=0;
for(i=0;i<n;i++)
{ u = ARG(i,right);
if(!contains_nonconstant(u,g))
{ ARGREP(temp,k,u);
++k;
}
}
if(k==0)
return 1;
if(k==1)
{ *arg = ARG(0,temp);
RELEASE(temp);
return 0;
}
SETFUNCTOR(temp,'+',k);
*arg = temp;
return 0;
}
return 1;
}
/*_____________________________________________________________________*/
static int contains_nonconstant(term t, unsigned short g)
/* return 1 if t contains unary functor g with a non-econstant argument */
/* Don't count EVEN POWERS of such functors */
{ unsigned short n;
int i,err;
if(ATOMIC(t))
return 0;
if(FUNCTOR(t) == '^' && FUNCTOR(ARG(0,t)) == g)
{ err = infer(even(ARG(1,t)));
if(!err)
return 0; /* don't count even powers */
}
if(g == FUNCTOR(t))
return econstant(ARG(0,t)) ? 0 : 1;
n = ARITY(t);
if(FUNCTOR(t) == '^')
n = 1; /* don't go into exponents here */
for(i=0;i<n;i++)
{ if(contains_nonconstant(ARG(i,t),g))
return 1;
}
return 0;
}
/*_______________________________________________________*/
MEXPORT_ALGEBRA int trig_suppress_factoring(int tcount,term t)
/* Return 1 when solving equations, if we don't want to factor t in
pre_ops. Specifically, if any of the following conditions holds:
-- t contains trig functions with different non-econstant arguments,
-- or t contains at least two distinct trig functors, but all occurring
to even integer powers. (Some such will get factored in post_ops,
if the factors will still contain only even powers.)
-- The specific case 1 - (sin x - cos x)^2 should not be factored.
It is assumed that 'tcount' is the number of distinct trig functors
in t. If this number is zero, return 0. If count == 1, the
only reason not to factor is trig functions with different args.
*/
{ int ntrig[6];
term x;
int err;
int problemtype = get_problemtype();
if(tcount == 0)
return 0; /* no trig functions present */
assert(FUNCTOR(t) == '+');
if(!SOLVETYPE(problemtype))
return 0; /* this is only used when solving equations */
err = assess_trig(t,ntrig,ntrig+1,ntrig+2,ntrig+3,ntrig+4,ntrig+5,0,&x);
/* so ntrig[i] has the number of occurrences of non-even-integer powers
the ith trig functor */
if(err==2)
return 1; /* trig functions with different args */
/* don't rely on trigexpandflag as that will count
cos x and cos(1) as different args, but assess_trig
requires different non-econstant args */
if(tcount == 1)
return 0;
/* Now there are at least two trig functions */
if(err == 1)
return 1; /* all trig functions to an even power */
/* Now trap the special case mentioned above */
if(ARITY(t) == 2 && NEGATIVE(ARG(1,t)))
{ term u = ARG(0,t);
term v = ARG(0,ARG(1,t));
if(ONE(v))
{ term temp = u;
u = v;
v = temp;
}
if(!ONE(u))
return 0; /* ok to factor */
if(FUNCTOR(v) == '^' && equals(ARG(1,v),two))
{ v = ARG(0,v);
if(FUNCTOR(v) == '+' && ARITY(v) == 2 && NEGATIVE(ARG(1,v)))
{ u = ARG(0,v);
v = ARG(0,ARG(1,v));
if((FUNCTOR(u) == SIN && FUNCTOR(v) == COS) ||
(FUNCTOR(v) == SIN && FUNCTOR(u) == COS)
)
return 1;
}
}
}
return 0;
}
/*_______________________________________________________________*/
static int algebraic_fractions(term t)
/* return 1 if t contains fractions algebraically, i.e.
down a path containing only =, inequalities, +, -, *, +, left of ^. */
{ unsigned short f,n;
int i;
if(ATOMIC(t))
return 0;
f = FUNCTOR(t);
if(f == '/')
return 1;
if(f != '*' && f != '+' && f != '-' && f != '^' && !INEQUALITY(f))
return 0;
if(f == '^' || f == '-')
return algebraic_fractions(ARG(0,t));
n = ARITY(t);
for(i=0;i<n;i++)
{ if(algebraic_fractions(ARG(i,t)))
return 1;
}
return 0;
}
/*___________________________________________________________*/
static int contains_powerof(unsigned short f, term t)
/* return 1 if t contains a power of a term with functor f,
0 if not. */
{ unsigned short i,n;
if(ATOMIC(t))
return 0;
if(FUNCTOR(t) == '^' && FUNCTOR(ARG(0,t)) == f)
return 1;
n = ARITY(t);
for(i=0;i<n;i++)
{ if(contains_powerof(f,ARG(i,t)))
return 1;
}
return 0;
}
/*___________________________________________________________*/
static int trig_transfer2(int *dir,term eqn,term *arg)
/* if eqn has the form polynomial(sin x, cos x), and is linear
in sin x, transfer terms not containing sin x to the right and
terms containing sin x to the left. Similarly if it's linear in
cos x.
Return 0 for success, 1 for failure.
Example: -5 sin x cos x + 5 cos^2 x + 1 = 0
*/
{ unsigned short i,n,g,k;
int side;
term left = ARG(0,eqn);
term right = ARG(1,eqn);
int sinflag, cosflag;
term temp;
term x = get_eigenvariable();
if(!ZERO(left) && !stricttrigpoly(left,x))
return 1;
if(!ZERO(right) && !stricttrigpoly(right,x))
return 1;
sinflag = contains_powerof(SIN,eqn);
cosflag = contains_powerof(COS,eqn);
if(sinflag && cosflag)
return 1;
if(!sinflag && !cosflag)
return 1;
g = sinflag ? COS : SIN;
for(side = 0;side < 2; side++)
{ left = side == 0 ? ARG(0,eqn) : ARG(1,eqn);
right = side == 0 ? ARG(1,eqn) : ARG(0,eqn);
if(FUNCTOR(left) == '+')
{ n = ARITY(left);
k = 0;
*arg = make_term('+',n);
for(i=0;i<n;i++)
{ if(!contains(ARG(i,left),g))
{ ARGREP(*arg,k,ARG(i,left));
++k;
}
}
if(k == 1)
{ temp = ARG(0,*arg);
RELEASE(*arg);
*arg = temp;
*dir = side;
return 0;
}
if(k > 1 && k < n)
{ *dir = side;
SETFUNCTOR(*arg,'+',k);
return 0;
}
RELEASE(*arg);
}
}
return 1;
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists