Sindbad~EG File Manager

Current Path : /usr/home/beeson/MathXpert/algebra/
Upload File :
Current File : /usr/home/beeson/MathXpert/algebra/eqn.c

/* Algebraic operations on equations, including real logs and exponentials.
Operators for the solve_equations, quadratic_equations, and advanced_equations
menus, and 'sister' operators for auto mode */
/*
M. Beeson
Original date 12.27.90
3.13.99  modified
12.27.99 removed "pragma argsused" lines
12.30.99 modified cancelterm
3.2.01  modified diveqn_aux for TRIG_IDENTITY
3.8.01  modified select_divarg for TRIG_IDENTITY
7.20.05 changed buffer size in addeqn and subeqn to 128
1.21.06 modified the dated line
8.28.07 modified cancelterm to fail if arg contains MATRIX
12.8.14 changed allocation of buffer in diveqn_aux.
5.14.24 inserted   strcat(reason," ");   several places
*/

/* Re complex numbers:  functions in this file will find complex
roots of polynomials with real coefficients, but will not deal
with complex logs and exponentials.   These are the functions to
be included on MathXpert's algebra disk.  Complex log and exponentials
are dealt with in trigeqn.c */


/*  Equations have multiplicities; the multiplicity of a root is just the
multiplicity of a solved equation x=c.  An equation  u^n  = 0  will give
rise to an equation u=0 with multiplicity n.  An equation uv=0 will give
rise to a disjunction or(u=0,v=0).  Disjunctions of equations are printed
in column matrix form.  The so-called 'non-degeneracy conditions' of Wu
come out as assumptions (when they can't be inferred or refuted).  */

/* We represent an equation with multiplicity n as a term with the
#-defined functor MULTIPLICITY, which is declared infix so we get
a display like  x = 3  multiplicity 2  */

/* When working on equations, varlist[eigenvariable] is the
independent variable; there may be other variables introduced by substitutions,
and other variables present in the original problem.  We cannot require
that these all be parameters if these operators are to be used in calculus.
We can't require that varlist[0] always be the independent variable
either, as we may make a substitution for varlist[0], and it's too
complicated (because of dependency information in varinfo) to swap
positions of variables in varlist.  Therefore when a substitution is
made for varlist[0], we change 'eigenvariable' as well.  Now upon unwinding
that definition, how do we recover the old eigenvariable?  Instead of
trying to figure that out, we keep the information as we go, in
varinfo[i].oldeign (see defns.h).
*/


#include <string.h>
#include "globals.h"
#include <assert.h>
#include "ops.h"
#include "operator.h"
#include "probtype.h"
#include "dcomplex.h"
#include "order.h"
#include "cancel.h"
#include "factor.h"
#include "simpprod.h"
#include "simpsums.h"
#include "algaux.h"
#include "advfact.h"
#include "ceval.h"
#include "complex4.h"  /* rootofunity */
#include "eqn.h"
#include "prover.h"
#include "trigtran.h"
#include "trigpoly.h"
#include "solvelin.h"
#include "symbols.h"
#include "pvalaux.h"   /* noccurs    */
#include "errbuf.h"
#include "mstring.h"
#include "autosimp.h"
#include "nextline.h"
#include "cflags.h"
#include "tdefn.h"
#include "dispfunc.h"  /* functor_string  */
#include "deval.h"     /* seminumerical   */
#include "inveqn.h"    /* invert_equation */
#include "meromorp.h"  /* ok_to_cancel    */
#include "graphstr.h"
#include "mpdoc.h"
#include "checkarg.h"
#include "execute.h"   /* GetTransform    */

static int auto_cancelterm(term, term, term *, char *);
static int auto_transfer(int*, term, term *, term *, char *);
static int select_divarg(term, term *);
static term dmco(term, term);
static int simple(term);
static int one_sqrt(term t, term *arg);
/*__________________________________________________________________*/
/* in order that we can recover the exact menu text from the code,
we have to use different function names for the functions that apply
to (different kinds of) inequalities and equations.  These different
functions just pass the arguments on to the workhorse functions.
The endings 1,2 refer to strict and not-strict inequalities. */

int reverselessthan(term eqn, term arg, term *next, char *reason)
{ if(FUNCTOR(eqn)=='<')
     return switchsides(eqn,arg,next,reason);
  return 1;
}

int reversele(term eqn, term arg, term *next, char *reason)
{ if(FUNCTOR(eqn)==LE)
     return switchsides(eqn,arg,next,reason);
  return 1;
}

int reversegreaterthan(term eqn, term arg, term *next, char *reason)
{ if(FUNCTOR(eqn)=='>')
     return switchsides(eqn,arg,next,reason);
  return 1;
}

int reversege(term eqn, term arg, term *next, char *reason)
{ if(FUNCTOR(eqn)==GE)
     return switchsides(eqn,arg,next,reason);
  return 1;
}

int addeqn1(term eqn, term arg, term *next, char *reason)
{ if(FUNCTOR(eqn)=='<' || FUNCTOR(eqn) == '>' || interval_as_and(eqn))
     return addeqn(eqn,arg,next,reason);
  return 1;
}

int addeqn2(term eqn, term arg, term *next, char *reason)
{ if(FUNCTOR(eqn)==LE || FUNCTOR(eqn) == GE || interval_as_and(eqn))
     return addeqn(eqn,arg,next,reason);
  return 1;
}

int subeqn1(term eqn, term arg, term *next, char *reason)
{ if(FUNCTOR(eqn)=='<' || FUNCTOR(eqn) == '>' || interval_as_and(eqn))
     return subeqn(eqn,arg,next,reason);
  return 1;
}

int subeqn2(term eqn, term arg, term *next, char *reason)
{ if(FUNCTOR(eqn)==LE || FUNCTOR(eqn) == GE || interval_as_and(eqn))
     return subeqn(eqn,arg,next,reason);
  return 1;
}

int changesigns1(term eqn, term arg, term *next, char *reason)
{ if(FUNCTOR(eqn)=='<' || FUNCTOR(eqn) == '>')
     return changesigns(eqn,arg,next,reason);
  if(FUNCTOR(eqn)==AND && FUNCTOR(ARG(0,eqn)) == '<')
     return changesigns(eqn,arg,next,reason);
  return 1;
}

int changesigns1g(term eqn, term arg, term *next, char *reason)
{ return changesigns1(eqn,arg,next,reason);
}

int changesigns2(term eqn, term arg, term *next, char *reason)
{ if(FUNCTOR(eqn)==LE || FUNCTOR(eqn) == GE)
     return changesigns(eqn,arg,next,reason);
  if(FUNCTOR(eqn)==AND && FUNCTOR(ARG(0,eqn)) == LE)
     return changesigns(eqn,arg,next,reason);
  return 1;
}

int changesigns2g(term eqn, term arg, term *next, char *reason)
{ return changesigns2(eqn,arg,next,reason);
}


/*__________________________________________________________________*/
int switchsides(term eqn, term arg, term *next, char *reason)
{ term left,right;
  unsigned f = FUNCTOR(eqn);
  if(!INEQUALITY(f))   /* includes = and NE */
     return 1;
  left = ARG(0,eqn);
  right = ARG(1,eqn);
  if(get_mathmode() == AUTOMODE && f == '=')
     { /* get the variable on the left if it's only on the right */
        if( !econstant(left) || econstant(right))
            return 1;   /* operator fails */
        /* else continue, and swap left and right */
      }
  switch(f)
   { case '=' :  *next = equation(right,left);
                 strcpy(reason, english(201));  /* if a=b then b=a */
                 break;
     case '<' :  *next = greaterthan(right,left);
                 strcpy(reason, english(202));  /* if a<b then b>a */
                 break;
     case '>' :  *next = lessthan(right,left);
                 strcpy(reason, english(203)); /* if a>b then b<a  */
                 break;
     case LE  :  *next = ge(right,left);
                 strcpy(reason, english(204)); /* if a \ge b then b \ge a  */
                 break;
     case GE  :  *next = le(right,left);
                 strcpy(reason, english(205)); /* if a \le b then b \le a  */
                 break;
   }
  HIGHLIGHT(*next);
  return 0;
}
/*__________________________________________________________________*/
int changesigns(term eqn, term arg, term *next, char *reason)
/* -u=-v => u=v;   -u  \le  -v =>  v  \le  u; etc. */
/* If an appropriate arg is supplied, it can work on a system of
   equations, changing the signs of the specified equation.  It can
   also work on an interval_as_and.
*/
{ term left,right;
  int err;
  unsigned short f = FUNCTOR(eqn);
  if(f == AND  && interval_as_and(eqn))
     { err = changesigns(ARG(1,eqn),arg,&right,reason);
       if(err)
          return 1;
       err = changesigns(ARG(0,eqn),arg,&left,reason);
       if(err)
          return 1;
       *next = and(right,left);
       return 0;
     }
  if(f == AND && FUNCTOR(arg) != ILLEGAL && ISINTEGER(arg))
     { long n = INTDATA(arg);
       unsigned short m = ARITY(eqn);
       int i;
       term u;
       unsigned short path[5];
       if(n > m || n <= 0)
          return 1;
       err = changesigns(ARG((unsigned short)(n-1),eqn), arg,&u,reason);
       if(err)
          return 1;
       *next = make_term(AND,(unsigned short) m);
       for(i=0;i<m;i++)
          ARGREP(*next,i, i==n-1 ? u : ARG(i,eqn));
       path[0] = AND;
       path[1] = (unsigned short) n;
       path[2] = 0;
       set_pathtail(path);
       if(LINEUP(eqn))
          SET_LINEUP(*next);
       return 0;
     }
  if(!INEQUALITY(f))
     return 1;
  left = ARG(0,eqn);
  right = ARG(1,eqn);
  switch(f)
    { case '=' :
         if(status(pushminusin) <= LEARNING)
            *next = equation(tnegate(left),tnegate(right));
         else
            *next = equation(strongnegate(left),strongnegate(right));
         break;
      default  :
         *next = make_term(f,2);   /* must switch the sides */
          ARGREP(*next,0,strongnegate(right));
          ARGREP(*next,1,strongnegate(left));
      break;
    }
  strcpy(reason, english(168));   /* change signs */
  HIGHLIGHT(*next);
  return 0;
}
/*__________________________________________________________________*/
static term addeqn_aux(term right, term arg)
/* arg is one side of an equation to which are adding arg.  Return the
sum as required by addeqn. */
{ term ans;
  if(NEGATIVE(right) && equals(arg,ARG(0,right)))
     /* add_cancel_and_order would get this anyway, but
        we trap this common case for efficiency */
     { ans = zero;
       HIGHLIGHT(ans);
       return ans;
     }
  if(FUNCTOR(right) == '/' && NEGATIVE(ARG(0,right)) &&
     FUNCTOR(arg) == '/' && equals(ARG(1,arg),ARG(1,right)) &&
     equals(ARG(0,arg),ARG(0,ARG(0,right)))
    )  /* add_cancel_and_order wouldn't get this, because it
          doesn't pull '-' out of a fraction */
     { ans = zero;
       HIGHLIGHT(ans);
       return ans;
     }
  return add_cancel_and_order(right,arg);
}
/*__________________________________________________________________*/
int addeqn(term eqn, term arg, term *next, char *reason)
/* add arg to both sides, cancelling if its negation is already there,
and returning the new term in proper additive order. */
{ term left,right;
  int err;
  unsigned short f = FUNCTOR(eqn);
  char buffer[128];
  term u,v;
  if(interval_as_and(eqn))
     { err = addeqn(ARG(0,eqn),arg,&u,reason);
       if(err)
          return 1;
       err = addeqn(ARG(1,eqn),arg,&v,reason);
       if(err)
          return 1;
       *next = and(u,v);
       return 0;
     }
  if(!INEQUALITY(f))
      return 1;
  left = ARG(0,eqn);
  right = ARG(1,eqn);
  if(ZERO(arg)) /* go ahead and add it explicitly, since it otherwise
                   won't even show */
     { term newleft,newright,newleft1,newright1,temp;
       newleft1 = sum(left,var0);  /* add var0 instead of zero temporarily,
                                    because zero would be dropped */
       newright1 = sum(right,var0);
       temp = zero;
       HIGHLIGHT(temp);
       subst(temp,var0,newleft1,&newleft);
       subst(temp,var0,newright1,&newright);
       *next = make_term(f,2);
       ARGREP(*next,0,newleft);
       ARGREP(*next,1,newright);
       strcpy(reason, english(206));   /*  add 0 */
       arg = zero;
       goto out;
     }
  HIGHLIGHT(arg);
  *next = make_term(f,2);
  HIGHLIGHT(arg);
  ARGREP(*next,0,addeqn_aux(left,arg));
  ARGREP(*next,1,addeqn_aux(right,arg));
  err = mstring(arg,buffer);
  if(err || strlen(buffer) > MAXREASONSTRING-4)
     { strcpy(reason, english(189));  /* add to both sides */
       return 0;
     }
  strcpy(reason, english(190));   /*  add  */
  strcat(reason," ");
  strcat(reason,buffer);
  out:
  SetShowStepArg(arg);
  /* This is called by transfereqn, transferineq, so it has to tell
     ShowStep which operation should be selected in Term Selection mode. */
  SetShowStepOperation(f == '=' ? addeqn :
                       f == '<' ? addeqn1:
                       f == '>' ? addeqn1:
                       f == LE  ? addeqn2:
                                  addeqn2
                );
  return 0;
}
/*__________________________________________________________________*/
int subeqn(term eqn, term arg, term *next, char *reason)
/* subtract arg from both sides of eqn, if eqn is an equation
or an inequality.  Also works on interval_as_and terms, in which
case it subtracts arg from all three terms.
*/
{ term left,right;
  int err;
  unsigned short f = FUNCTOR(eqn);
  char buffer[128]; // not MAXREASONSTRING, as mstring's output goes in here temporarily
  term u,v;
  if(NEGATIVE(arg) && get_mathmode() == AUTOMODE)
     return addeqn(eqn,ARG(0,arg),next,reason);
  if(interval_as_and(eqn))
     { err = subeqn(ARG(0,eqn),arg,&u,reason);
       if(err)
          return 1;
       err = subeqn(ARG(1,eqn),arg,&v,reason);
       if(err)
          return 1;
       *next = and(u,v);
       return 0;
     }
  if(!INEQUALITY(f))
     return 1;
  left = ARG(0,eqn);
  right = ARG(1,eqn);
  if(ZERO(arg)) /* go ahead and add it explicitly, since it otherwise
                   won't even show */
     { term newleft,newright,newleft1,newright1,minuszero;
       newleft1 = sum(left,var0);  /* add var0 instead of zero temporarily,
                                    because zero would be dropped */
       newright1 = sum(right,var0);
       minuszero = make_term('-',1);
       ARGREP(minuszero,0,zero);
       HIGHLIGHT(minuszero);
       subst(minuszero,var0,newleft1,&newleft);
       subst(minuszero,var0,newright1,&newright);
       *next = make_term(f,2);
       ARGREP(*next,0,newleft);
       ARGREP(*next,1,newright);
       strcpy(reason,english(191));   /* subtract 0 */
       arg = zero;
       goto out;
      }
  HIGHLIGHT(arg);
  *next = make_term(f,2);
  HIGHLIGHT(arg);
  if(FUNCTOR(arg) == '+' && status(pushminusin) > LEARNING)
     { ARGREP(*next,0,add_cancel_and_order(left,strongnegate(arg)));
       ARGREP(*next,1,add_cancel_and_order(right,strongnegate(arg)));
     }
  else
     { ARGREP(*next,0,add_cancel_and_order(left,tnegate(arg)));
       ARGREP(*next,1,add_cancel_and_order(right,tnegate(arg)));
     }
  strcpy(reason, english(192));  /* subtract */
  strcat(reason," ");
  err = mstring(arg,buffer);
  if(err || strlen(buffer) > MAXREASONSTRING-9)
     ;  /* don't put anything more in the reason */
        /* 'subtract from both sides' is too long and
           'add to both sides' is misleading */
  else
     strcat(reason,buffer);
  out:
      /* This is called by transfereqn, transferineq, so it has to tell
         ShowStep which operation should be selected in Term Selection mode. */
  SetShowStepArg(arg);
  SetShowStepOperation(f == '=' ? subeqn:
                       f == '<' ? subeqn1:
                       f == '>' ? subeqn1:
                       f == LE  ? subeqn2:
                                  subeqn2
                );
  return 0;
}
/*__________________________________________________________________*/
int transfer_aux(int dir,term eqn, term arg, term *next, char *reason)
/* transfer 'arg' from left to right if dir == 0, or from
right to left if dir is nonzero.   arg must be a summand or a sum of summands
on the 'source' side of the equation.  */
{ term source, target;
  int i,err;
  unsigned short f = FUNCTOR(eqn);
  if(!INEQUALITY(f))
     return 1;
  if(dir)
     { source = ARG(1,eqn);
       target = ARG(0,eqn);
     }
  else
     { source = ARG(0,eqn);
       target = ARG(1,eqn);
     }
  if(equals(source,arg))
     { if(NEGATIVE(source))
          { err = addeqn(eqn,ARG(0,arg),next,reason);
            if(err)
               return 1;
            SetShowStepOperation(f == '=' ? addeqn : (f == '<' || f == '>') ? addeqn1 : addeqn2);
            SetShowStepArg(ARG(0,arg));
            return 0;
          }
       err = subeqn(eqn,arg,next,reason);
       if(err)
          return 1;
       SetShowStepOperation(f == '=' ? subeqn : (f == '<' || f == '>') ? subeqn1 : subeqn2);
       return 0;
     }
  if(NEGATIVE(source) && equals(ARG(0,source),arg))
     { err = addeqn(eqn,arg,next,reason);
       if(err)
          return 1;
       SetShowStepOperation(f == '=' ? addeqn : (f == '<' || f == '>') ? addeqn1 : addeqn2);
       return 0;
     }
  if(FUNCTOR(source) == '/' && NEGATIVE(ARG(0,source)) &&
     FUNCTOR(arg) == '/' && equals(ARG(1,arg),ARG(1,source)) &&
     equals(ARG(0,arg),ARG(0,ARG(0,source)))
    )
     { err = addeqn(eqn,arg,next,reason);  /*  left = (-a)/b, add a/b */
       if(err)
          return 1;
       SetShowStepOperation(f == '=' ? addeqn : (f == '<' || f == '>') ? addeqn1 : addeqn2);
       return 0;
     }
  if (FUNCTOR(source) == '+' && FUNCTOR(arg) != '+')
    { for(i=0;i<ARITY(source);i++)
         { if( equals(arg,ARG(i,source)))
              { if(FUNCTOR(arg)=='-')
                   { err = addeqn(eqn,ARG(0,arg),next,reason);
                     if(err)
                        return 1;
                     SetShowStepOperation(f == '=' ? addeqn : (f == '<' || f == '>') ? addeqn1 : addeqn2);
                     return 0;
                   }
                else
                   { err = subeqn(eqn,arg,next,reason);
                     if(err)
                        return 1;
                     SetShowStepOperation(f == '=' ? subeqn : (f == '<' || f == '>') ? subeqn1 : subeqn2);
                     return 0;
                   }
              }
           if( FUNCTOR(ARG(i,source))=='-' && equals(ARG(0,ARG(i,source)),arg))
              { err = addeqn(eqn,arg,next,reason);
                if(err)
                   return 1;
                SetShowStepOperation(f == '=' ? addeqn : (f == '<' || f == '>') ? addeqn1 : addeqn2);
                return 0;
              }
         }
    }
  if(FUNCTOR(arg)== '+' && FUNCTOR(source) == '+' && ARITY(arg) <= ARITY(source))
    /* source  \pm  arg must get rid of ARITY(arg) terms of source */
    { term temp,temp2;
      int err;
      char buffer[2048];  // long enough to hold anything mstring can produce.
      HIGHLIGHT(arg);
      temp = sum(source,arg);
      collect(temp,&temp2);
      if(FUNCTOR(temp2) != '+' || ARITY(temp2) <= ARITY(source) - ARITY(arg))
                 /* it worked */
         { if(dir)
              *next = equation(add_cancel_and_order(target,arg),temp2);
           else
              *next =  equation(temp2,add_cancel_and_order(target,arg));
           if(f != '=')
              SETFUNCTOR(*next,f,2);
           err = mstring(arg,buffer);
           if(err || strlen(buffer) > 18)
              { strcpy(reason,english(189));  /*  add to both sides */
                return 0;
              }
           strcpy(reason,english(190));  /* add  */
           strcat(reason,buffer);
           SetShowStepOperation(f == '=' ? addeqn : (f == '<' || f == '>') ? addeqn1 : addeqn2);
           return 0;
         }
           /* Then try it the other way */
      temp = sum(source,strongnegate(arg));
      collect(temp,&temp2);
      if(FUNCTOR(temp2) != '+' || ARITY(temp2) <= ARITY(source) - ARITY(arg))
         /* it worked */
         { if(dir)
              *next = equation(add_cancel_and_order(target,strongnegate(arg)),temp2);
           else
              *next =  equation(temp2,add_cancel_and_order(target,strongnegate(arg)));
           if(f != '=')
              SETFUNCTOR(*next,f,2);
           err = mstring(arg,buffer);
           if(err || strlen(buffer) > MAXREASONSTRING - 9)
              { strcpy(reason, english(193));  /* subtract term */
                SetShowStepOperation(f == '=' ? subeqn : (f == '<' || f == '>') ? subeqn1 : subeqn2);
                return 0;
              }
           strcpy(reason,english(192));  /*  subtract  */
           strcat(reason," ");
           strcat(reason,buffer);
           SetShowStepOperation(f == '=' ? subeqn : (f == '<' || f == '>') ? subeqn1 : subeqn2);
           return 0;
         }
    }
  return 1;   /* specified arg can't be found */
}
/*__________________________________________________________________*/
int transfer1(term eqn, term arg, term *next, char *reason)
/* transfer arg left to right if possible.  Works on equations
or inequalities.  Not called in auto mode. */
{ unsigned short f = FUNCTOR(eqn);
  if(!INEQUALITY(f))
     return 1;
  return transfer_aux(0,eqn,arg,next,reason);
}
/*__________________________________________________________________*/
int transfer2(term eqn, term arg, term *next, char *reason)
/* transfer arg right to left if possible.  Works on equations
or inequalities.  Not called in auto mode. */
{ unsigned short f = FUNCTOR(eqn);
  if(!INEQUALITY(f))
     return 1;
  return transfer_aux(1,eqn,arg,next,reason);
}
/*__________________________________________________________________*/
int muleqn(term eqn, term arg, term *next, char *reason)
/* multiply both sides by arg,  cancelling if its inverse is already there,
and returning the new term in proper multiplicative order. */
/* If either side of eqn is a sum containing fractions as summands,
distribute the product.  Otherwise spliteqn will be the next thing done,
which isn't what we want, e.g. in 13 - 6(x^2+1)/x = 0, we multiply by x */

{ int err;
  term x;
  int problemtype;
  if(FUNCTOR(eqn) != '=')
     return 1;
  if(FUNCTOR(arg) == ILLEGAL)
     { err = select_mularg(eqn,&arg);
       if(err)
          return 1;
       return muleqn(eqn,arg,next,reason);
     }
  problemtype = get_problemtype();
  if(
     (problemtype == LINEAR_EQUATION || problemtype == LINEAR_EQUATIONS) &&
     !constant(arg)
    )
     { errbuf(0, english(1837));
       /* When solving linear equations, you can multiply only by a constant. */
       /* Dowith prevents getting here in term selection mode, but you might
          get here from the Operations menu. */
       return 1;
     }
  x = get_eigenvariable();
  problemtype = get_problemtype();
  /* You are not allowed to multiply by zero */
  if(!contains(arg,FUNCTOR(x)))
     { err = check1(nonzero(arg));
       if(err)
          { errbuf(0,english(1726));
            if(get_problemtype() == LINEAR_EQUATIONS)
               errbuf(1,english(1731));
               /* You are not allowed to multiply by zero.  That would make the
                  equation true for all values of the unknowns. */

            else
               errbuf(1,english(1727));
               /* You are not allowed to multiply by zero.  That would make the
                  equation true for all values of the unknown. */
            return 1;
          }
     }
  else if(problemtype == LINEAR_EQUATIONS)
     { /* You can get here by selecting one equation for display */
       errbuf(0, english(1729));
       errbuf(1, english(1730));
       /* You are not allowed to multiply by an expression containing the unknown.
          The resulting equation would no longer be linear. */
       return 1;
     }
  else
     { int save_nextdefn = get_nextdefn();
       unsigned short saveit = get_nextassumption();
       if(save_nextdefn)
          assume(domain(eqn));
       err = infer(nonzero(arg));
       if(err)
          { err = infer(equation(arg,zero));
            if(!err)
               { errbuf(0,english(1726));
                 set_nextassumption(saveit);
                 set_nextdefn(save_nextdefn);
                 return 1;
               }
            set_checksolutionsflag(1);
          }
        set_nextassumption(saveit);
        set_nextdefn(save_nextdefn);
     }
  return muleqn_aux(eqn,arg,next,reason);
}
/*______________________________________________________________*/
static term dmco(term a, term b)
/* dmco for 'distribute,multiply,cancel,and order' */
{ unsigned short n;
  int i;
  term summand,ans;
  assert(FUNCTOR(a)=='+');
  n = ARITY(a);
  ans = make_term('+',n);
  for(i=0;i<n;i++)
     { summand = ARG(i,a);
       if(FUNCTOR(summand) == '-')
          ARGREP(ans,i,tnegate(multiply_cancel_and_order(b,ARG(0,summand))));
       else
          ARGREP(ans,i,multiply_cancel_and_order(b,summand));
     }
  return ans;
}
/*______________________________________________________________*/
int muleqn_aux(term eqn, term arg, term *next, char *reason)
/* do the work of muleqn and mulineq    */
/*  assumes arg is already instantiated */
{ term left,right,temp,temp2;
  int err;
  char buffer[81];
  unsigned short f = FUNCTOR(eqn);
  int problemtype = get_problemtype();
  if(FUNCTOR(arg)==ILLEGAL)
     return 1;
  left = ARG(0,eqn);
  right = ARG(1,eqn);
  *next = make_term(f,2);
   if(
      (problemtype == LINEAR_EQUATION || problemtype == LINEAR_EQUATIONS) &&
      !constant(arg)
    )
     { errbuf(0, english(1837));
       /* When solving linear equations, you can only multiply by a constant. */
       /* Dowith prevents getting here in term selection mode, but you might
          get here from the Operations menu. */
       return 1;
     }
  if(ONE(arg)) /* go ahead and multiply it explicitly, since it otherwise
                   won't even show */
     { term newleft,newright;
       if(FRACTION(left) && ONE(ARG(1,left)))
          newleft = ARG(0,left);
       else
          { temp2 = product(left,var0);  /* use var0 instead of one temporarily
                                           because one would be dropped */
            temp = one;
            HIGHLIGHT(temp);
            subst(temp,var0,temp2,&newleft);
            /* don't assume temp2 is a product; left may have been 'one',
               in which case temp2 is var0. */
          }
       if(FRACTION(right) && ONE(ARG(1,right)))
          newright = ARG(0,right);
       else
          { temp2 = product(right,var0);
            temp = one;
            HIGHLIGHT(temp);
            subst(temp,var0,temp2,&newright);
          }
       ARGREP(*next,0,newleft);
       ARGREP(*next,1,newright);
       strcpy(reason, english(194));  /*  multiply by 1 */
       SetShowStepArg(one);
       return 0;
      }
  HIGHLIGHT(arg);
  ARGREP(*next,0, niceproduct(arg,left));
  ARGREP(*next,1,niceproduct(arg,right));
  err = mstring(arg,buffer);
  if(
     (ZERO(right) || ZERO(left)) &&
     !econstant(arg) && !entire(arg)
    )
     { commentbuf(0, english(1561));
       commentbuf(1, english(1547));
       commentbuf(2, f == '=' ? english(665) : english(1812));
       /* You may gain unwanted solutions by eliminating a subexpression not everywhere defined.
          Remember to check your final solutions in the original equation.
          (or for inequalities:)
          Remember to use the assumptions to eliminate such unwanted solutions at the end.
       */
       set_checksolutionsflag(1);
     }
  if(err || strlen(buffer) > MAXREASONSTRING - 12)
     strcpy(reason, english(195));  /*  multiply both sides */
  else
     { strcpy(reason,english(196));  /*  multiply by  */
       strcat(reason," ");
       strcat(reason,buffer);
     }
  SetShowStepArg(arg);
  return 0;
}
/*__________________________________________________________________*/
int diveqn(term eqn, term arg, term *next, char *reason)
{ int err;
  if(FUNCTOR(eqn) != '=')
     return 1;
  if(FUNCTOR(arg)==ILLEGAL)
     { err = select_divarg(eqn,&arg);
       if(err)
          return 1;
     }
  return diveqn_aux(eqn,arg,next,reason);
}
/*_____________________________________________________________________*/
int diveqn_aux(term eqn, term arg, term *next, char *reason)
/* do the work of diveqn, also on inequalities */
{ term x,left,right,u,v,newleft,newright,temp,cancelled;
  unsigned short n;
  int i,err,err2;
  short nbefore, nafter, xflag;
  void  *savenode;
  char buffer[DIMREASONBUFFER];
  unsigned short f = FUNCTOR(eqn);  /* can be an inequality */
  unsigned short g;
  int sign=0;
  int problemtype = get_problemtype();
  left = ARG(0,eqn);
  right = ARG(1,eqn);
  *next = make_term(f,2);
  if(
      (problemtype == LINEAR_EQUATION || problemtype == LINEAR_EQUATIONS) &&
      !constant(arg)
    )
     { errbuf(0, english(1836));
       /* When solving linear equations, you can only divide by a constant. */
       /* Dowith prevents getting here in term selection mode, but you might
          get here from the Operations menu. */
       return 1;
     }
  if(ONE(arg)) /* go ahead and divide it explicitly, since it otherwise
                   won't even show */
     { err = err2 = 1;
       if(FUNCTOR(left) == '*')
          { err = multbyone(ARG(0,left),arg,&newleft,reason);
            if(err)
               newleft = left;
          }
       else
          newleft = left;
       if(FUNCTOR(right) == '*')
          { err2 = multbyone(ARG(0,right),arg,&newright,reason);
            if(err2)
               newright = right;
          }
       else
          newright = right;
       if(err && err2)
          { errbuf(0, english(1486));
            /* No use dividing by 1, nothing will change. */
            return 1;
          }
       *next = equation(newleft,newright);
       strcpy(reason, english(200));  /*divide by */
       strcat(reason, " 1");
       SetShowStepArg(one);
       return 0;
     }
  nbefore = get_nextassumption();  /* number of assumptions now in effect */
  savenode = heapmax();

  if(problemtype == TRIG_IDENTITY)
     { if(!ok_to_cancel(arg,left,right))
          return 1;
     }
  else
     {  /* Don't allow division by zero */
       x = get_eigenvariable();
       if(problemtype >= LIMITS && contains(eqn,DIFF) &&
          SOLVETYPE(problemtype) && !derivative_subterm(eqn,&x)
         )
           /* e.g. implicit differentiation, we're solving for a derivative subterm */
          { err = check1(nonzero(arg));
            if(err)
               { reset_heap(savenode);
                 return 1;
               }
          }
       else if(obviously_positive(arg))
          { sign = 1;
            err = 0;
          }
       else if(NEGATIVE(arg) && obviously_positive(ARG(0,arg)))
          { err = 0;
            sign = -1;
          }
       else if(contains(arg,FUNCTOR(x)))
          { xflag = 1;
            if(f == '=')
               { err = check1(nonzero(arg));
               }
            else
               { err = infer(lessthan(zero,arg));
                 if(!err)
                    sign = 1;
                 else
                    { err = infer(lessthan(arg,zero));
                      if(!err)
                         sign = -1;
                    }
               }
          }
       else if(f == '=')
          { err = check1(nonzero(arg));
            xflag = 0;
          }
       else /* inequality, arg doesn't contain x */
          { xflag = 0;
            err = check1(lessthan(zero,arg));
            if(!err)
               sign = 1;
            else
               { err = check1(lessthan(arg,zero));
                 if(!err)
                    sign = -1;
               }
          }
       reset_heap(savenode);    /* infer and check can use a lot of memory */
       nafter = get_nextassumption();
       if(err)
          { if(xflag && f == '=')
              { errbuf(0, english(1501));
                errbuf(1, english(1502));
                           /* Dividing by a quantity containing the variable
                             is allowed only if the quantity can never be zero;
                             otherwise you might lose some solutions. */
              }
            else if(f == '=')
               errbuf(0, english(198)); /*  Can't divide by zero */
            else if(obviously_nonnegative(arg))
                    { errbuf(0,english(1844));
                      errbuf(1,english(1845));
                      errbuf(2,english(1846));
                      /* You can't divide by that quantity, because MathXpert
                         can't verify that it is never zero.  In that case
                         you might lose some solutions.  */
                    }
            else
               errbuf(0, english(1804));
               /* Can't determine the sign of the expression you wanted to divide by. */
            if(nafter > nbefore)
               set_nextassumption(nbefore);
            return 1;
          }
     }
  if(INTEGERP(arg) && FUNCTOR(left) == '+')
     {  /* see next comment below for explanation--we have to do this
           on the left as well as the right in case of inequalities */
        n = ARITY(left);
        u = make_term('+',n);
        v = reciprocal(arg);
        for(i=0;i<n;i++)
           ARGREP(u,i,multiply_cancel_and_order(v,ARG(i,left)));
        ARGREP(*next,0, u);
     }
  else if(ZERO(left))
     ARGREP(*next,0,zero);
  else if(FUNCTOR(left) == '*' && !cancel(left,arg,&cancelled,&temp))
     ARGREP(*next,0,temp);
  else
     ARGREP(*next,0,multiply_cancel_and_order(reciprocal(arg),left));
  if(INTEGERP(arg) && FUNCTOR(right) == '+')
     {  /* when dividing by 2 for example, go ahead and distribute so
           we get e.g.  m + pi/2  instead of (2m+pi_term)/2.  This saves
           one 'simplify' step at the end, and in trig equations where
           you have four equations 2x =..., it saves four steps as each
           equation is separately divided by 2 and then simplified.
        */
        n = ARITY(right);
        u = make_term('+',n);
        v = reciprocal(arg);
        for(i=0;i<n;i++)
           ARGREP(u,i,multiply_cancel_and_order(v,ARG(i,right)));
        ARGREP(*next,1, u);
     }
  else if(ZERO(right))
     ARGREP(*next,1,zero);
  else if(FUNCTOR(right) == '*' && !cancel(right,arg,&cancelled,&temp))
     ARGREP(*next,1,temp);
  else
     ARGREP(*next,1,multiply_cancel_and_order(reciprocal(arg),right));
  err = mstring(arg,buffer);
  if(err || strlen(buffer) > MAXREASONSTRING-10)
     { strcpy(reason, english(199));  /*  divide both sides */
       HIGHLIGHT(*next);
       SetShowStepArg(arg);
       return 0;
     }
  strcpy(reason, english(200));  /*    divide by  */
  strcat(reason," ");
  strcat(reason,buffer);
  if(sign == -1 && f != '=' && f != NE)
     /* reverse the inequality sign when dividing by a negative quantity */
     { switch(f)
          { case '<' :
               g = '>';
               break;
            case LE:
               g = GE;
               break;
            case '>':
               g = '<';
               break;
            case GE:
               g = LE;
               break;
            default:
               assert(0);
          }
       SETFUNCTOR(*next, g, 2);
     }
  HIGHLIGHT(*next);   /* entire equation is affected by division */
  SetShowStepArg(arg);
  return 0;
}
/*__________________________________________________________________*/
static int select_divarg(term eqn, term *arg)
/* called from automode.  Select arg to be used by diveqn.  Return 0 
for success, 1 for failure.  Divide by
constant factors of a product on the left, but only when the right
side is already constant, and NOT if the left side is an mvpoly2
with more than one occurence of the eigenvariable and the right side
isn't zero (example, 4x(x+1)=3, we don't want to divide by 4).
*/

/*  If the left side is a product, one of whose factors is an integral,
and the problemtype is a calculus type (200 or more), then divide by
the factors which aren't integrals.  Similarly for DIFF in case the
problemtype is RELATED_RATES or IMPLICIT_DIFF>
*/

/* When verifying identities, as opposed to solving equations, just 
find a common factor of the two sides if there is one. */

{ term left,right,c;
  unsigned short n,k;
  unsigned short symbol = FUNCTOR(eqn);
  int i,count,flag;
  term x;
  int problemtype;
  if(symbol != '=')
     return 1;
  left = ARG(0,eqn);
  right = ARG(1,eqn);
  problemtype = get_problemtype();
  if(problemtype == TRIG_IDENTITY)
    { /* select a term to cancel from both sides */
      while(FRACTION(left) || NEGATIVE(left))
         left = ARG(0,left);
      while(FRACTION(right) || NEGATIVE(right))
         right = ARG(0,right);
      return cancel(left,right,arg,&c);
    }
  if(symbol != '=' && econstant(left) && !econstant(right) && !ATOMIC(right) && FUNCTOR(right) == '*')
    { left = ARG(1,eqn);
      right = ARG(0,eqn);
    }
  else if( !(econstant(right) && !econstant(left) && FUNCTOR(left) == '*') )
    { term cancelled;
      if(contains_sqrt(left) == ABSFUNCTOR && !contains(right,ABSFUNCTOR))
         { /* leave the factor(s) containing ABSFUNCTOR isolated */
           n = ARITY(left);
           count=0;
           for(i=0;i<n;i++)
              { if(contains(ARG(i,left),ABSFUNCTOR))
                   { ++count;
                     flag = i;
                   }
              }
           if(count == 1)
              { cancel(left,ARG(flag,left),&cancelled,arg);
                return 0;
              }
           assert(count > 1);
           if(count == n)
              return 1;  /* ABSFUNCTOR in every factor */
           if(count == n-1)
              /* only one factor without ABSFUNCTOR */
              { for(i=0;i<n;i++)
                   { if(!contains(ARG(i,left),ABSFUNCTOR))
                         { *arg = ARG(i,left);
                           return 0;
                         }
                   }
                 assert(0);
              }
           *arg = make_term('*',(unsigned short)(n-count));
           k=0;
           for(i=0;i<n;i++)
              { if(!contains(ARG(i,left),ABSFUNCTOR))
                   { ARGREP(*arg,k,ARG(i,left));
                     ++k;
                   }
              }
           assert(k==n-count);
           return 0;
         }
      if(problemtype < 200)
         return 1;  /* not doing calculus */
      n = ARITY(left);
      for(i=0;i<n;i++)
         { if(FUNCTOR(ARG(i,left))==INTEGRAL)
              break;  /* there is an integral on the left */
           if(FUNCTOR(ARG(i,left))==DIFF &&
              (problemtype == IMPLICIT_DIFF ||
               problemtype == RELATED_RATES
              )
             )
              break;
         }
      if(i==n)
         return 1;  /* no integral or derivative, forget it */
      cancel(left,ARG(i,left),&cancelled,arg);
      return 0;
    }
  if(FUNCTOR(left) != '*')
     return 1;
  x = get_eigenvariable();
  if(!ZERO(right) && mvpoly2(left) && noccurs(x,left) > 1)
     return 1;  /* to fulfill the specs of the function, above */
  n = ARITY(left);
  c= make_term('*',n);
  k=0;  /* mark place in c */
  for(i=0;i<n;i++)
    { if(econstant(ARG(i,left)))
         { ARGREP(c,k,ARG(i,left));
           ++k;
          }
    }
  if(k==0)
     { RELEASE(c);
       return 1;
     }
  if(k==1)
     { *arg = ARG(0,c);
       RELEASE(c);
       return 0;
     }
  SETFUNCTOR(c,'*',k);
  *arg = c;
  return 0;
}
/*__________________________________________________________________*/
int cancelterm(term eqn, term arg, term *next, char *reason)
/* cancel arg (specified by user) from both sides of an equation */
{ int err;
  int flag = -1;
  term left,right,newleft,newright;
  if(FUNCTOR(eqn) != '=')
     return 1;
  if(contains(arg,MATRIX))
     return 1;   // you can't cancel a matrix term
  if(FUNCTOR(arg)==ILLEGAL)
     return auto_cancelterm(eqn,arg,next,reason);
  left = sum(tnegate(arg),ARG(0,eqn));
     /* put -arg first, so it will be cancelled even if ARG(0,eqn) permits
        other cancellations. */
  right = sum(tnegate(arg),ARG(1,eqn));
  if(contains2(arg,INTEGRAL,2))
     { flag = get_cofi_index();
     }
  err = additivecancel(left,zero,&newleft,reason);
  if(err)
     { if(flag >= 0)
          set_cofi_index(flag);
       return 1;
     }
  err = additivecancel(right,zero,&newright,reason);
  if(err)
     { if(flag >= 0)
          set_cofi_index(flag);
       return 1;
     }
  *next = equation(newleft,newright);
  SetShowStepArg(arg);
  return 0;
}
/*__________________________________________________________________*/
static int auto_cancelterm(term eqn, term arg, term *next, char *reason)
/* used only in auto mode.  Choose the term arg to be cancelled
and call cancelterm */
{ int i,j,err;
  term left,right;
  if(FUNCTOR(eqn) != '=')
     return 1;
  left = ARG(0,eqn);
  right = ARG(1,eqn);
  if(FUNCTOR(left) != '+')
    { if(FUNCTOR(right) != '+')
         return 1;  /*  a=a is caught elsewhere */
      for(i=0;i<ARITY(right);i++)
         { if(equals(left,ARG(i,right)))
              { err = cancelterm(eqn,left,next,reason);
                if(err)
                   return 1;
                SetShowStepArg(left);
                goto out;
              }
         }
      if(i==ARITY(right))
         return 1;
    }
  /* Now left is a sum */
  if(FUNCTOR(right) != '+')
     { for(i=0;i<ARITY(left);i++)
          { if(equals(right,ARG(i,left)))
               { err = cancelterm(eqn,right,next,reason);
                 if(err)
                    return 1;
                 SetShowStepArg(right);
                 goto out;
               }
          }
       if(i==ARITY(left))
          return 1;
     }
   /* Now both left and right are sums */
   for(i=0;i<ARITY(left);i++)
      { for(j=0;j<ARITY(right);j++)
           { if(equals(ARG(i,left),ARG(j,right)))
                { err = cancelterm(eqn,ARG(i,left),next,reason);
                  if(err)
                     return 1;
                  SetShowStepArg(ARG(i,left));
                  goto out;
                }
           }
      }
   return 1;
   out:
   SetShowStepOperation(cancelterm);
   return 0;
}
/*__________________________________________________________________*/
int transfereqn(term eqn, term arg, term *next,char *reason)
/*  called in automode only, brother of transfer1 and transfer2.
Finds a term and transfers it. Works only on equations */
{ int dir,err;
  term u;
  if(FUNCTOR(eqn) != '=')
     return 1;
  err = auto_transfer(&dir,eqn,&u,next,reason);
  if(err)
     return 1;
  SetShowStepArg(NEGATIVE(u) ? ARG(0,u) : u);
  return 0;
}

/*__________________________________________________________________*/
int transferstrictineq(term eqn, term arg, term *next,char *reason)
/*  called in automode only, brother of transfer1 and transfer2.
Finds a term and transfers it. Works only on < and > */
{ int dir,err;
  term u;
  unsigned short f = FUNCTOR(eqn);
  if(f != '<' && f != '>')
     return 1;
  err = auto_transfer(&dir,eqn,&u,next,reason);
  if(err)
     return 1;
  SetShowStepArg(u);
  return 0;
}

/*__________________________________________________________________*/
int transferineq(term eqn, term arg, term *next,char *reason)
/*  called in automode only, brother of transfer1 and transfer2.
Finds a term and transfers it. Works only on LE and GE */
{ int dir,err;
  term u;
  unsigned short f = FUNCTOR(eqn);
  if(f != LE && f != GE)
     return 1;
  err = auto_transfer(&dir,eqn,&u,next,reason);
  if(err)
     return 1;
  SetShowStepArg(u);
  return 0;
}
/*__________________________________________________________________*/
int transfer(term eqn, term arg, term *next,char *reason)
/*  called in automode only, brother of transfer1 and transfer2.
Finds a term and transfers it.  Works only on intervals. */

{ unsigned short f = FUNCTOR(eqn);
  int dir,err;
  term temp,temp2,transferred;
  if(f == AND && interval_as_and(eqn))
     /* example   -2 < x-1 < 2, should add 1 getting -1 < x < 3  */
     /* This will be called by preops before working on the individual
        inequalities */

    { err = auto_transfer(&dir,ARG(1,eqn),&transferred, &temp,reason);
      if(err)
         return 1;
      err = transfer_aux(dir ? 0 : 1, ARG(0,eqn),transferred,&temp2,reason);
         /* make sure the second transfer goes the OTHER direction */
      if(err)
         return 1;
      *next = and(temp2,temp);
      assert(interval_as_and(*next));
      SetShowStepArg(transferred);
      return 0;
    }
  return 1;   /* works only on intervals */
}
/*__________________________________________________________________*/
static int contains_log2(term t)
/* return 1 if t contains LN or LOG or LOGB 'monomially', i.e.
so that it can be collected with other log terms.
This differs from contains_monomially(t,LN) in that it counts
constant LN terms too, but doesn't count logs raised to a power.
Return 0 otherwise. */

{ unsigned short f = FUNCTOR(t);
  unsigned short n;
  int i;
  if(f == LN || f == LOGB || f == LOG)
     return 1;
  if(f == '-')
     return contains_log2(ARG(0,t));
  if(f == '*')
     { n = ARITY(t);
       for(i=0;i<n;i++)
          { if(contains_log2(ARG(i,t)))
               return 1;
          }
       return 0;
     }
  return 0;
}
/*__________________________________________________________________*/
static int auto_transfer(int *dir, term eqn, term *arg, term *next,char *reason)
/* called in auto mode; choose a term and transfer it */
/* return 0 for success, 1 means we don't want to transfer anything */
/* works only on inequalities and equations           */
/* generally we want to transfer non-constant terms to the left side;
   if learning, one term at a time, otherwise transfer all the
   non-constant terms at once.  See exceptions below. */
/* On the other hand, if there are logs (or lns) on the left, we want
   to transfer constant terms to the right, so that a power can be
   taken to eliminate the log, as in log(x^2-x+1) - 1 = 0; UNLESS those
   constant terms contain_monomially logs themselves, in which case
   they will collect with the other logs, as in log 2 + log x = 10  */

/* Exception 1:  in an inequality with the variable only on the right,
     don't transfer non-constants to the left.  This rule simultaneously fixes
     three problems:
    (1)   0 < f(x) => -f(x) < 0 => 0 < f(x) (the last step due to changesigns)
    (2)   don't do anything to c < x, which is already solved,
          and often occurs as the first conjunct in an interval  and(c<x,x<d)
    (3)   c < f(x) =>  c-f(x) < 0 => -f(x) < -c => c < f(x)
          generalizes both the problems in (1) and (2).

   Exception 2:  In an equation or inequality with two or more radicals
   (but NOT absolute values, which are NOT treated this way)
   containing the unknown, we want to put the radicals on OPPOSITE sides
   of the equation, rather than the same side.  ( But this doesn't apply
   to fractional exponents:  e.g. u^(1/2) + 2u^(1/4) + 1 = 0.)
   The same for two powers with the unknown in the exponent, as in
   e^u(x) = 2e^v(x)  which should become u(x) = ln 2 +  v(x) directly
   In both these cases, 'contains' means in a top-level monomial or
   fraction of monomials;
   what if the containment is deeper, as in   sin (sqrt x) = cos(sqrt(x))?
   Chances are most such equations can't be solved anyway, but unless
   the transfer is blocked for another reason, we'd be better off to make it,
   I think.

   Exception to Exception 2:   when one side is a fraction with an
   denominator containing a sum containing a SQRT, go ahead and transfer it.
   "noxious" checks for such a term.

   Example:  sqrt x = 3/(6 sqrt x + sqrt(4x-2)).  After transfer and
   common denom, you can multiply by the obnoxious denom and get rid of
   it, avoiding a second squaring and a fourth-degree equation.

   Exception 3:  In equations involving two different trig functors,
   such as sin x - cos x = 0, we want to put sin x on the left and cos x
   on the right, and then square.  In general we want to isolate cos x
   on one side if there is no cos^2 already.  But watch out for the
   exceptions to the exception:

      (3a)  equations of the form A sin^2 x + B cos x = constant
            should use sin^2 x => 1-cos x
            rather than separate-and-square.

      (3b)  separate-and-square works fine for trig polynomials,
            but if the left side contains fractions, they should
            be worked on first with common denominators etc.

      (3c)  Don't do this on inequalities, because you can't
            square, e.g. tan x <= sec x can't just be squared.
*/

/* Returns indirectly in *arg the term chosen and transferred */
/* Returns indirectly in *dir the direction of transfer;
   0 is left-to-right, 1 is right-to-left */

{ term right,left,c,temp,x,u;
  int count;
  int absflag, sqrtflag, rootflag, powerflag;
  int abscount, sqrtcount, rootcount, powercount,fractcount;
  int logflag;
  unsigned short n,k,g;
  unsigned short f = FUNCTOR(eqn);
  int problemtype = get_problemtype();
  int currenttopic = get_currenttopic();
  int i,j,err;
  *dir = 0;
  assert(INEQUALITY(f));
  right = ARG(1,eqn);
  left = ARG(0,eqn);
  x = get_eigenvariable();
  g = FUNCTOR(left);
  if(!simple(left))  /* simple returns 0 if left needs immediate simplification */
     return 1;  /* don't do anything on examples like these:
                   (\sqrt n)^2 = n-3;  (\sqrt n)^2 = n-3;  |n|^2 = n-3  */
  if(g == ABSFUNCTOR)
     return 1;    /* don't transfer when we have ABSFUNCTOR on the left,
                    as in abs(x-1) = x.  Instead powereqn should be used */
  if(FUNCTOR(right) == ABSFUNCTOR)
     return 1;    /* use lessthanineq or leineq  or a=abs(z) iff a=z or a=-z */
  if(econstant(left) && f != '=')  /* see 'Exception 1' noted above */
     /* then the variable will remain on the right */
     { temp = left;
       left = right;
       right = temp;  /* swap left and right */
       g = FUNCTOR(left);
       *dir = 1;      /* signal that this has been done */
     }
  /* Now for the exception to Exception 2: */
  if(FUNCTOR(right) == '/' && noxious(ARG(1,right)) &&
     !CALCULUS_TOPIC(currenttopic)
    )
     { *arg = right;
       *dir = 1;
       return transfer_aux(*dir,eqn,*arg,next,reason);
     }
  if(FUNCTOR(left) == '/' && noxious(ARG(1,left)) &&
     problemtype != IMPLICIT_DIFF && problemtype != MINMAX &&
     currenttopic != _int_by_parts1 &&
     currenttopic != _int_by_parts2
    )
     { *arg = right;
       *dir = 1;
       return transfer_aux(*dir,eqn,*arg,next,reason);
     }
  if(is_linear_in(left,x) && is_linear_in(right,x))
      { if(collected(right) > 1 || collected(left) > 1)
           return 1;  /* collect terms before transferring */
        if(collected(left) && g != '+' && econstant(right))
           return 1;  /* e.g. 3x = 5  */
      }
  else if(mvpoly(left) && !one_sqrt(right,arg))
      /* example:  x <= 7 sqrt(x) + 30,
         transfer the 30 but not the sqrt(x) term.
         Without this clause, transfer does nothing and
         then alltoleft moves everything to the left, after
         which the 30 is moved back to the right, which
         looks awkward.
      */
      { *dir = 1;
        if(ZERO(*arg))
           return 1;
        return transfer_aux(*dir,eqn,*arg,next,reason);
      }
  else if(g != '+' && collected(left)==1 && !contains(eqn,DIFF))
     /* for nonlinear equations; but not when solving for DIFF during
        implicit differentiation
      */
     return 1;    /* fail e.g. on e^x = 5 */
  count = logflag = 0;  /* count the nonconstant terms on the left */
  if(g == '+')
     { n = ARITY(left);
       for(i=0;i<n;i++)
          { u = ARG(i,left);
            if(!econstant(u))
               ++count;
            if(contains_log2(u))
               ++logflag;
          }
     }
  else
     { if(!econstant(left))
          ++count;
       if(contains_log2(left))
          ++logflag;
       n = 1;
     }
  if(n == 1 && count == 1 && econstant(right))
     { if(f == '=')
          return 1;  /* don't transfer the only nonconstant term */
       /* Example, (2x-3)/(x-3) < 1, it's better to transfer and use common denoms,
          when the alternative is to multiply by the square of the denom */
       if(FRACTION(left) && !econstant(ARG(1,left)) && !ZERO(right))
          { *arg = NEGATIVE(right) ? ARG(0,right) : right;
            *dir = 1;
            if(ZERO(*arg))
               return 1;
            return transfer_aux(*dir, eqn, right, next,reason);
          }
       if(FRACTION(right) && !econstant(ARG(1,right)) && !ZERO(left))
          { *arg = NEGATIVE(left) ? ARG(0,left) : left;
            *dir = 0;
            if(ZERO(*arg))
               return 1;
            return transfer_aux(*dir, eqn, left, next,reason);
          }
       return 1;  /* maybe we should also check for sums containing a fraction with
                     nonzero denominator.  */
     }
  if(logflag > 1)
     return 1;   /* wait till the logs are collected before transferring */
     /* example:  log 2 + log x = 1 => log 2x = 1 => 2x =10 => x=5
       rather than log 2 + log x = 1 => log x = 1-log 2 => etc */
  if(count == 1 && econstant(right))
     { /* don't transfer right to left; transfer any constant terms on
          the left to the right */
       if(n==1)
          { /* the left side is constant */
            *arg = NEGATIVE(left)? ARG(0,left): left;
            if(ZERO(*arg))
               return 1;
            return transfer_aux(*dir, eqn, left, next,reason);
          }
       if(n==2)
          { if(!econstant(ARG(0,left)))
               { *arg = NEGATIVE(ARG(1,left)) ? ARG(0,ARG(1,left)) : ARG(1,left);
                 if(ZERO(*arg))
                    return 1;
                 return transfer_aux(*dir,eqn,ARG(1,left),next,reason);
               }
            else
               { *arg = NEGATIVE(ARG(0,left)) ? ARG(0,ARG(0,left)): ARG(0,left);
                 if(ZERO(*arg))
                    return 1;
                 return transfer_aux(*dir,eqn,ARG(0,left),next,reason);
               }
          }
          /* now n > 2, so a sum must be transferred */
       k=0;
       *arg = make_term('+',(unsigned short)(n-1));
       for(i=0;i<n;i++)
          { if(ZERO(ARG(i,left)))
               { RELEASE(*arg);
                 return 1;
               }
            if(econstant(ARG(i,left)))
               { ARGREP(*arg,k,ARG(i,left));
                 ++k;
               }
          }
       assert(k==n-1);   /* because there's only one nonconstant term */
       return transfer_aux(*dir,eqn,*arg,next,reason);
     }
  /* Now right isn't constant, or there is more than one nonconstant
  term on the left. First check for SQRT, ROOT, ABSFUNCTOR, and nonconstant powers,
  as in Exception 2 in the specs. */

  if(g == '+')
     { n = ARITY(left);
       rootcount = sqrtcount = abscount = powercount = fractcount = 0;
       for(i=0;i<n;i++)
          { temp = ARG(i,left);
            if(NEGATIVE(temp))
               temp = ARG(0,temp);
            if(FRACTION(temp) && !econstant(temp))
               ++fractcount;
            if(FRACTION(temp) && econstant(right) && fractcount==2)
               return 1;
               /* use common denoms first, example:
                  sqrt(x-1)/sqrt(x+1) - sqrt(x+1)/sqrt(x-1) = 1
               */
            if(econstant(temp))
               continue;
            if(contains_monomially(temp,SQRT))
               { ++sqrtcount;
                 sqrtflag = i;
               }
            if(contains_monomially(temp,ROOT))
               { ++rootcount;
                 rootflag = i;
               }
            if(contains_monomially(temp,ABSFUNCTOR))
               { ++abscount;
                 absflag = i;
               }
            if(FUNCTOR(temp) == '^' && SIGNEDFRACTION(ARG(1,temp)))
              /* example, x + (x^2 + 1)^(1/2) = 0.  Move the two terms to
                 opposite sides. */
               { ++powercount;
                 powerflag = i;
               }
            if(FUNCTOR(temp) == '*')
               { for(j=0;j<ARITY(temp);j++)
                    { if(FUNCTOR(ARG(j,temp)) == '^' && SIGNEDFRACTION(ARG(1,ARG(j,temp))))
                         { ++powercount;
                           powerflag = i;
                           break;
                         }
                    }
               }
            if(contains_monomially(temp,'^'))
               /* This only finds nonconstant exponents */
               { ++powercount;
                 powerflag = i;
               }
          }
       if(abscount == 1)
          i = absflag;
       else if(rootcount)
          i = rootflag;
       else if(sqrtcount)
          i = sqrtflag;
       else if(powercount && !fractcount)
          /* example: (5x-6)^(1/5) + x/(5x-6)^4/5 = 0 should be done by
             common denoms, not by separating the terms */
          i = powerflag;
       else
          i = -1;
       if(i >= 0 && !(ZERO(right) && NEGATIVE(ARG(i,left))))
          { /* then transfer all the other terms to the right */
            if(n==2)
               *arg = ARG(i ? 0 : 1, left);
            else
               { *arg = make_term('+',(unsigned short)(n-1));
                 for(j=0;j<i;j++)
                    ARGREP(*arg,j,ARG(j,left));
                 for(j=i+1;j<n;j++)
                    ARGREP(*arg,j-1,ARG(j,left));
               }
            return transfer_aux(*dir,eqn,*arg,next,reason);
         }
       else if(i >= 0) /* && ZERO(right) && NEGATIVE(ARG(i,left)) */
         { /* then transfer ARG(0,ARG(i,left)) to the right */
           *arg = ARG(0,ARG(i,left));
           if(ZERO(*arg))
              return 1;
           return transfer_aux(*dir,eqn,*arg,next,reason);
         }
     }
  else if( (g == '*' || g == '/' || g == SQRT || g == ROOT || g == '-' ) &&
           ( contains_monomially(left,SQRT) ||
             contains_monomially(left,ROOT) ||
             contains_monomially(left,ABSFUNCTOR) ||
             contains_monomially(left,'^') ||
             (contains_monomially(right,SIN) && contains(left,COS) && !contains(left,SIN)) ||
             (contains_monomially(right,COS) && contains(left,SIN) && !contains(left,COS)) ||
             (contains_monomially(right,TAN) && contains(left,SEC) && !contains(left,TAN)) ||
             (contains_monomially(right,SEC) && contains(left,TAN) && !contains(left,SEC)) ||
             (contains_monomially(right,COT) && contains(left,CSC) && !contains(left,COT)) ||
             (contains_monomially(right,CSC) && contains(left,COT) && !contains(left,CSC))
           )
         )
     /* then don't transfer anything, we're going to square, or
        take a power, or take a log, or use alltoleft */
     /*  example  \sqrt (x-1) = f(x), no matter what f(x) is we should square */
      return 1;

  /* Now we've taken care of transfers that should be made or blocked
  to help attract SQRT, ROOT, or fractional exponents. */

  if(FUNCTOR(eqn) == '=')
     { err = trig_transfer(dir,eqn,arg,next,reason);
       /* Handle equations as in Exception 3 */
       if(!err)
          return 0;
     }
  if((g == '+' || g == '*') &&
     !(collected(left) < 2 && econstant(right)) &&
     ((eqpoly(left,x) && eqpoly(right,x)) || (trigpoly2(left,x) && trigpoly2(right,x))) &&
     !(is_linear_in(left,x) && is_linear_in(right,x))
    ) /*  Don't just move some terms, because alltoleft is going to
          work when you get to it in post_ops.
          If you change these condition, change the
          conditions in alltoleft in eqn1.c to match */
     return 1;

  if(f != '=' && is_linear_in(left,x) && is_linear_in(right,x) &&
     !econstant(left) && FUNCTOR(left) == '+' &&
     !econstant(right) && !contains(right,'+')
    )
    /* example, x+1 < 4x */
    /* Move nonconstant terms from left to right */
     { n = ARITY(left);
       k=0;
       c = make_term('+',n);
       for(i=0;i<n;i++)
          { if(!econstant(ARG(i,left)))
               { ARGREP(c,k,ARG(i,left));
                 ++k;
               }
          }
     }
  else
     { /* Now move any non-constant terms from right to left.
          Leave constant terms where they are for now; some collection or
          attraction operator will have to do the work. */

       *dir = *dir ? 0 : 1;
       if(FUNCTOR(right) != '+' && econstant(right))
          return 1;
       if(FUNCTOR(right) != '+')
          { if(NEGATIVE(right))
               *arg = ARG(0,right);
            else if(FUNCTOR(right) == '/' && NEGATIVE(ARG(0,right)))
               *arg = make_fraction(ARG(0,ARG(0,right)),ARG(1,right));
            else
              *arg = right;
            return transfer_aux(*dir,eqn,*arg,next,reason);  /* right isn't constant */
          }
       /* now right is a sum */
       n = ARITY(right);
        /* collect all non-constant terms on the right and transfer them
           to the left all at once */
       k=0;
       c = make_term('+',n);
       for(i=0;i<n;i++)
          { if(!econstant(ARG(i,right)))
               { ARGREP(c,k,ARG(i,right));
                 ++k;
               }
          }
     }
  if(k==0)
     { RELEASE(c);
       return 1;
     }
  if(k==1)
     { *arg = NEGATIVE(ARG(0,c)) ? ARG(0,ARG(0,c)) : ARG(0,c);
       RELEASE(c);
       return transfer_aux(*dir,eqn,*arg,next,reason);
     }
  SETFUNCTOR(c,'+',k);
  *arg = c;
  return transfer_aux(*dir,eqn,c,next,reason);
}
/*___________________________________________________________*/
static int simple(term t)
/* return 0 if term can obviously be simplified further, and
should be, before any 'transfer' of terms from one side of
an equation to the other (when t is the left side) should be done */

{ unsigned short f = FUNCTOR(t);
  unsigned short n = ARITY(t);
  unsigned short h;
  int i;
  if(ATOMIC(t))
     return 1;
  if(PROTECTED(t))
     return 1;;
  if(f == '^' && isinteger(ARG(1,t)))
     /* Example:  x - (x(x-1))^(1/2), it's not necessary to push the
        exponent in before transferring; therefore the 'isinteger' clause */
     { h = FUNCTOR(ARG(0,t));
       if( h=='*' || h==SQRT || h==ROOT || h==ABSFUNCTOR || h=='^' || h=='/')
          return 0;   /* first simplify the power */
     }
  for(i=0;i<n;i++)
     { if(!simple(ARG(i,t)))
           return 0;
     }
  return 1;
}

/*__________________________________________________________________*/
int solvelinear(term t, term arg, term *next, char *reason)
/* solve linear equation at once*/
{ int err;
  if(FUNCTOR(t) != '=')
     return 1;
  err = solve_linear_ineq(t,next);
  if(err)
     return 1;
  strcpy(reason, english(246));  /* solve linear equation */
  HIGHLIGHT(*next);
  return 0;
}

/*__________________________________________________________________*/
int functioneqn(term eqn, term arg, term *next, char *reason)
/* arg is an artificial term f(0) where f is the function the
   user wants to apply.
*/

{ term left,right;
  unsigned short f,g;
  int err;
  if(FUNCTOR(eqn) != '=')
     return 1;
  left = ARG(0,eqn);
  right = ARG(1,eqn);
  if(FUNCTOR(arg) == ILLEGAL)
     { /* in auto mode, must choose the function to be applied */
        g = FUNCTOR(left);
        if(g == '^')
           { if(equals(ARG(0,left),eulere))
                 arg = ln1(zero);
             else if(equals(ARG(0,left),ten))
                 arg = log1(zero);
           }
        else
           { err = invert_function(g,zero,&arg);
             if(err)
                return 1;
           }
     }
  f = FUNCTOR(arg);
  assert(f != ILLEGAL);
  if(ARITY(arg) != 1 || !ZERO(ARG(0,arg)))
     return 1;
  if(f == LN)
     return lneqn(eqn,arg,next,reason);
     /* so we don't generate ln(e^u) but just u */
  if(f == LOG)
     return logeqn(eqn,arg,next,reason);
  if(f == SQRT)
     return sqrteqn(eqn,arg,next,reason);
  /* Now it's some other function for which we don't have an
     explicit operator */
  left = make_term(f,1);
  right = make_term(f,1);
  ARGREP(left,0,ARG(0,eqn));
  ARGREP(right,0,ARG(1,eqn));
  err = check1(domain(left));
  if(err)
     { errbuf(0,english(167)); /* Left side would not be defined. */
       return 1;
     }
  err = check1(domain(right));
  if(err)
     { errbuf(0,english(168)); /* Right side would not be defined. */
       return 1;
     }
  *next = equation(left,right);
  HIGHLIGHT(*next);
  if(numerical(arg))
     { strcpy(reason, english(170));   /* apply  */
       reason[5] = 32;
       functor_string(f,SCREEN,reason+6);
     }
  else
     strcpy(reason, english(1721));  /* apply function */
  set_checksolutionsflag(1);
  return 0;
}
/*_____________________________________________________________*/
int econstant(term t)
/* does term t contain the variable being solved for, or
any variable on which that variable depends?  In case of
implicit diff or related rate problems, instead of a 'variable' we are
sometimes solving for a derivative term; but from ssolve,
a new variable has been substituted, and the problemtype but
not the topic has been changed.
   Using econstant instead of 'constant'  should
result in all letters but varlist[eigenvariable] (and things
it depends on) being regarded as constant
by the equation-solving operators.
    But note that ncs() still regards
only parameters as constant.  The part about IMPLICIT_DIFF should result in
all terms not containing derivatives being regarded as constant, so we
solve for dy/dx when doing implicit differentiation.
*/

{ int problemtype = get_problemtype();
  term x = get_eigenvariable();
  term y;
  int nvariables;
  term *varlist;
  int i;
  int currenttopic;
  if(seminumerical(t))
     return 1;
  if(problemtype == IMPLICIT_DIFF || problemtype == RELATED_RATES)
     return !contains(t,DIFF);
  if(problemtype == SOLVE_EQUATION &&
     (currenttopic = get_currenttopic()) != 0 &&
     CALCULUS_TOPIC(currenttopic)
    )
      { if (currenttopic == _implicit_diff ||
            currenttopic == _related_rates ||
            currenttopic == _minmax
           )
           return !contains(t,FUNCTOR(x));
        else
           return !contains(t,INTEGRAL);
      }
  if(contains(t,FUNCTOR(x)))
     return 0;
  if(!DEPENDENT(x))
     return 1;
  /* Now the eigenvariable depends on some other variable(s);
     check if t contains any such variable. */
  nvariables = get_nvariables();
  varlist = get_varlist();
  for(i=0;i<nvariables;++i)
     { y = varlist[i];
       if(equals(y,x))
          continue;
       if(depends(x,y) && contains(t,FUNCTOR(y)))
          return 0;
     }
  return 1;
}

/*____________________________________________________________*/
int trueeqn(term t, term arg, term *next, char *reason)
/* 'equation is identically true'.  Reduce an identity
to 'true'.  Only used when solving equations.
   If checksolutionsflag is nonzero,  *next should not be
'true'  The operation will succeed only if the original
equation can be inferred (using current assumptions).
*/
{ int i;
  unsigned short n;
  unsigned short f = FUNCTOR(t);
  unsigned short path[5];
  term u,x;
  int nextassumption;
  int err,flag, count;
  if(f == OR)
     { n = ARITY(t);
       for(i=0;i<n;i++)
          { u = ARG(i,t);
            if(equals(u,trueterm))
               { *next = trueterm;
                 HIGHLIGHT(*next);
                 strcpy(reason,english(1522)); /* eqn identically true */
                 return 0;
               }
            err = trueeqn(u,arg,next,reason);
            if(!err)
               { path[0] = OR;
                 path[1] = i+1;
                 path[2] = 0;
                 set_pathtail(path);
                 return 0;
               }
          }
       return 1;
     }
  if(f != '=')
     return 1;
  if(!equals(ARG(0,t),ARG(1,t)))
     return 1;
  flag = get_checksolutionsflag();
  if(!flag)
     { *next = trueterm;
       HIGHLIGHT(*next);
       strcpy(reason,english(1522)); /* eqn identically true */
       return 0;
     }
  err = infer(domain(history(0)));  /* changed 1.21.06 */
  if(err)
     { errbuf(0, english(1554));
       errbuf(1, english(1555));
       errbuf(2, english(1556));
       /* Steps have been taken that would require checking the solution.
          That would mean verifying the original equation as an identity.
          MathXpert is unable to do that, so the operation fails.
       */
       return 1;
     }
  /* Now, don't just make the next line 'true' when there are assumptions. */
  nextassumption = get_nextassumption();
  x = get_varlist()[0]; /* the original eigenvariable */
  count = 0;
  for(i=0;i<nextassumption;i++)
     { u = get_assumption(i);
       if(depends(u,x))
          { ++count;
            flag = i;
          }
     }
  if(count == 0)
     { *next = trueterm;
       HIGHLIGHT(*next);
       strcpy(reason,english(1522)); /* eqn identically true */
       return 0;
     }
  copy(get_assumption(flag),next);
  HIGHLIGHT(*next);
  strcpy(reason,english(1522)); /* eqn identically true */
  if(count == 1)
     commentbuf(0,english(1557));  /* !This assumption was made earlier. */
  else
     commentbuf(0,english(1558));
     /* !There are more assumptions that may restrict the solution further. */
  HIGHLIGHT(*next);
  return 0;
}

/*___________________________________________________________________*/
int noxious(term t)
/* return 1 if t contains a sum containing a sqrt */
{ unsigned short n;
  int i;
  if(ATOMIC(t))
     return 0;
  n = ARITY(t);
  if(FUNCTOR(t) == '+')
     { for(i=0;i<n;i++)
          { if(contains_sqrt2(ARG(i,t)))
               return 1;
          }
       return 0;
     }
  for(i=0;i<n;i++)
     { if(noxious(ARG(i,t)))
          return 1;
     }
  return 0;
}
/*___________________________________________________________________*/
unsigned short contains_sqrt2(term a)
/* return ABSFUNCTOR or SQRT or ROOT if a contains an ABSFUNCTOR or SQRT or ROOT
term as a factor or as a factor of num or denom
*/
{ unsigned short n,f;
  unsigned short ans;
  int i;
  f = FUNCTOR(a);
  if(INEQUALITY(f))
     { i = contains_sqrt2(ARG(0,a));
       if(i)
          return (unsigned short) i;
       return contains_sqrt2(ARG(1,a));
     }
  if(f==SQRT || f == ABSFUNCTOR || f == ROOT)
     { if(contains(a, FUNCTOR(get_eigenvariable())))
          return f;
       else
          return 0;
     }
  if(f == '-')
     return contains_sqrt2(ARG(0,a));
  n = ARITY(a);
  if(f == '/')
     { for(i=0;i<n;i++)
          { ans = contains_sqrt2(ARG(i,a));
            if(ans)
               return ans;
          }
     }
  if(f != '*')
     return 0;
  for(i=0;i<n;i++)
     { f = FUNCTOR(ARG(i,a));
       if((f == SQRT || f == ABSFUNCTOR || f==ROOT) &&
          contains(ARG(i,a),FUNCTOR(get_eigenvariable()))
         )
          return f;
     }
  return 0;
}
/*______________________________________________________________________*/
term niceproduct(term arg, term left)
/* distribute the product if arg is a monomial and left is a sum;
otherwise cancel and order, unless both are sums, in which case
just return the product.  
   in ogarithmic differentiation there is special treatment because
we don't want to distribute the variable 'y' which is next going to 
be expanded by definition.
*/
{ term ans;
  if(FUNCTOR(left) == '+' && FUNCTOR(arg) == '+')
     { ans = product(arg,left);
       sortargs(ans);
       return ans;
     }
  else if(FUNCTOR(left) == '+' && monomial(arg) &&
          get_currenttopic() != _logarithmic_differentiation
         )
     return dmco(left,arg);
  else
     return multiply_cancel_and_order(arg,left);
}

/*__________________________________________________________________________*/
static int one_sqrt(term t, term *ans)
/* if t is a sum, one of whose summands contains a sqrt of the eigenvariable
as a factor, and if all the other summands are constant,
return in *ans the sum of the OTHER summands of t.
   Return zero for success, 1 for failure.
   In case of failure, *ans will be garbage.
*/
{ unsigned short n;
  int i,flag=0,count = 0;
  term x;
  if(FUNCTOR(t) != '+')
     return 1;
  x = get_eigenvariable();
  n = ARITY(t);
  for(i=0;i<n;i++)
     { if(contains(ARG(i,t),FUNCTOR(x)))
          { if(count)
               return 1;
            ++count;
          }
       if(contains_sqrt2(ARG(i,t)))
          flag = i+1;
     }
  if(!flag)
     return 1;
  --flag;
  if(n == 2)
     { *ans = ARG(flag ? 0 : 1, t);
       return 0;
     }
  *ans = make_term('+',(unsigned short)(n-1));
  for(i=0;i<n-1;i++)
     ARGREP(*ans,i, i < flag ? ARG(i,t) : ARG(i+1,t));
  return 0;
}


/*_____________________________________________________________________________*/
int preparetocancel(term t, term arg, term *next, char *reason)
/* t is an identity to be verified. Look for a pair of factors on the two 
sides which are equal but not literally identical, and perform a transformation 
that brings them closer together.  The factors must be meromorphic
(see meromorp.c) and the test for equality can therefore be done by 
numerical testing, using somewhere_nonzero (see meromorp.c)
 */
{ term u,v,a,b,temp;
  int i,j,k1,k2,err;
  actualop op;
  unsigned short n,m;
  unsigned short path1[32], path2[32];
  if(FUNCTOR(t) != '=')
    return 1; 
  path1[0] = path2[0] = '=';
  path1[1] = 1;
  path2[1] = 2;
  k1=k2=2;
  u = ARG(0,t);
  v = ARG(1,t);
  while(FRACTION(u) || NEGATIVE(u))
     { path1[k1] = FUNCTOR(u);
       path1[k1+1] = 1;
       k1+=2;
       u = ARG(0,u);
     }
  while(FRACTION(v) || NEGATIVE(v))
     { path2[k2] = FUNCTOR(v);
       path2[k2+1] = 1;
       k2 += 2;
       v = ARG(0,v);
     }
  if(FUNCTOR(u) != '*' && FUNCTOR(v)== '*')
     { m = ARITY(v);
       if(!meromorph(u))
          return 1;
       if(!somewhere_nonzero(u))
          return 1;  /* one side is really zero */
       for(j=0;j<m;j++)
          { if(meromorph(ARG(j,v)) && !somewhere_nonzero(sum(u,tnegate(ARG(j,v)))))
               break;
          }
       if(j==m)
          return 1;
       path2[k2] = '*';
       path2[k2+1] = j+1;
       k2 += 2;
       a = u;
       b = ARG(j,v);
     }
  else if(FUNCTOR(v) != '*' && FUNCTOR(u) == '*')
     { n = ARITY(u);
       if(!meromorph(v))
          return 1;
       if(!somewhere_nonzero(u))
          return 1;
       for(j=0;j<n;j++)
          { if(meromorph(ARG(j,u)) && !somewhere_nonzero(sum(v,tnegate(ARG(j,u)))))
               break;
          }
       if(j==n)
          return 1;
       path1[k1] = '*';
       path1[k1+1] = j+1;
       k1 += 2;
       a = ARG(j,u);
       b  = v;
     }
  else if(FUNCTOR(u) == '*' && FUNCTOR(v) == '*')
     { n = ARITY(u);
       m = ARITY(v);
       for(i=0;i<n;i++)
          { for(j=0;j<m;j++)
              { if(meromorph(ARG(i,u)) && meromorph(ARG(j,v)) &&
                   somewhere_nonzero(ARG(i,u)) &&
                   !somewhere_nonzero(sum(ARG(i,u),tnegate(ARG(j,v))))
                  )
                   break;
              }
            if(j<m)
               break;
          }
       if(i==n)
          return 1;
       path1[k1] = path2[k2] = '*';
       path1[k1+1] = i+1;
       path2[k2+1] = j+1;
       k1 += 2;
       k2 += 2;
       a = ARG(i,u);
       b = ARG(j,v);
     }
  else
     return 1;
  path1[k1] = 0;
  path2[k2] = 0;
  /* Now, find an operation that transforms a into b or vice-versa, or at least 
     gets closer. */
  err = GetTransform(a,b,&op,&temp,reason);
  if(!err)
     { set_pathtail(path1);
       SetShowStepOperation(op);
       pathsub(path1,temp,t,next);
       return 0;
     }
   err = GetTransform(b,a,&op,&temp,reason);
   if(!err)
      { set_pathtail(path2);
        SetShowStepOperation(op);
        pathsub(path2,temp,t,next);
        return 0;
      }
   return 1;
}



     
  

   
  

       

       

Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists