Sindbad~EG File Manager

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

/* operators from simplify products menu, plus collectterms
and additivecommute, except orderfactors, which is in order.c
M. Beeson, for MathXpert
11.21.90 original date
3.12.99 last modified
1.4.00 modified multiplyout to check return value of recursive calls
       and NOT to call expandable before beginning, so as not to
       automatically refuse to multiply out powers higher than the third.
1.14.00 modified multiplyout on (a+b)i
2.25.00 modified distriblaw
4.21.00 corrected bringminusout
6.21.04 added minusintoproduct1, minusintoproduct2, minusintoproduct3.
8.27.04 modified multiplyout to take account of possible failure of tmult.
7.20.05 made multiplyout not call binomialtheorem on exponent 0.
8.24.07 Put dollar signs on reason strings throughout.
5.6.13  added stdlib.h;  made mifc_aux static
3.18.23  corrected 'acube' to 'bcube'  at line 909
3.19.23  corrected mifc_aux, whose logic was botched.
1.30.25 made multiplyoutandsimp work on an '=' 
*/


#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <math.h>    /* fabs */
#include <stdlib.h>  /* abs  */
#include "globals.h"
#include "errbuf.h"
#include "tdefn.h"
#include "ops.h"
#include "operator.h"
#include "checkarg.h"
#include "prover.h"
#include "algaux.h"
#include "cancel.h"
#include "order.h"
#include "simpsums.h"
#include "simpprod.h"
#include "mpmem.h"
#include "probtype.h"
#include "calc.h"
#include "trig.h"      /* sigmatosum */
#include "pvalaux.h"   /* contains_sqrt, factors, etc. */
#include "symbols.h"
#include "mathmode.h"  /* domainflag, get_currenttopic */
#include "factor.h"    /* sqrt_aux   */
#include "errbuf.h"
#include "polynoms.h"  /* cancelgcd_aux */
#include "autosimp.h"  /* set_pathtail etc. */

static int get_binomial_exponent(void);
static int two_sums(term t);
static int power_of_sum(term u, term *power);
static int distribute(term a, term b, term *ans);
static void monomult(term a, term b, term *ans);
/*_____________________________________________________________________*/
int multbyzero(term t, term arg, term *next, char *reason)
{ int i,err,intflag;
  unsigned short n = ARITY(t);
  term *atomlist;
  int natoms;
  int foundone = -1;
  if(FUNCTOR(t) != '*')
     return 1;  /* inapplicable */
  if(contains(t,LIMIT))
     { errbuf(0, english(412));
          /* You must first evaluate the limit(s) */
       return 1;
     }
  intflag = contains(t,INTEGRAL);
  atomsin(t,&atomlist);
  for(i=0;i<n;i++)
     { if (ISZERO(ARG(i,t)))
          { foundone  = i;
            *next = zero;
            HIGHLIGHT(*next);
            if(i==0)
               strcpy(reason,"$0\\times x = 0$");
            else
               strcpy(reason, "$x\\times 0 = 0$");
            if(!intflag)
               goto out;  /* without checking other factors */
          }
     }
  if(!intflag || foundone < 0)
     return 1;  /* no factor was zero */
  /* if there were integrals among the factors, then */
  for(i=0;i<n;i++)
     { if(ZERO(ARG(i,t)))
          continue;
       err = infer(domain(ARG(i,t)));
       if(err && intflag)
          { errbuf(0, english(413));
             /* Can't verify that the integral is defined and finite */
            return 1;
          }
     }
  out:
  if(domainflag(get_problemtype(), get_currenttopic()))
     { /* topic is one for which MathXpert does not list
          the assumption(s) that the problem is defined.  This
          includes limits, dif from defn, evaluate trig function, etc. */
       natoms = atomsin(t,&atomlist);
       for(i=0;i<natoms;i++)
          { if(NOTDEFINED(atomlist[i]))
               break;
          }
       free2(atomlist);
       if(i < natoms)
          {  /* If we get here, there was an undefined atom in the product, e.g. 'infinity' */
             errbuf(0, english(414));
            /* Can't apply x \cdot 0 = 0 when x is not defined. */
            return 1;
          }
       for(i=0;i<n;i++)
          { if(!ATOMIC(ARG(i,t)))
               { err = infer(domain(ARG(i,t)));
                 if(err)
                    { errbuf(0,english(414));  /* see above */
                      return 1;
                    }
                }
           }
     }
  return 0;
}
/*_____________________________________________________________________*/
int multbyone(term t, term arg, term *next, char *reason)
{ int i,j;
  unsigned short count = 0;
  unsigned short n = ARITY(t);
  int flag; /* keep track of which arg was 1 */
  if(FUNCTOR(t) != '*')
     return 1;  /* inapplicable */
    /* count how many 1's are in the product first */
  for(i = 0;i<n;i++)
     { if(ISONE(ARG(i,t)))
          { flag = i;
            ++count;
          }
     }
  if(count == 0)
     return 1;   /* no ones */
  if(count == n)
    /* all factors were 1, as in 1*1  */
     { *next = one;
       strcpy(reason,"$1\\times x = x$");
       HIGHLIGHT(*next);
       return 0;
     }
  /* so if there were some ones */
  if(count == n-1) /* all but one factor was one */
     { i= 0;
       while(ISONE(ARG(i,t)) )
          ++i;
       *next = ARG(i,t);
       HIGHLIGHT(*next);
       if(flag==0)
          strcpy(reason,"$1\\times x = x$");
       else
          strcpy(reason,"$x\\times 1 = x$");
       return 0;
     }
  /* so there was more than one non-one factor, so we have to return a product */
  *next = make_term('*', (unsigned short)(n-count));
  j=0;
  for(i=0;i<n;i++)
     { if(! ISONE(ARG(i,t)))
          { ARGREP(*next,j,ARG(i,t));
            ++j;
          }
     }
  HIGHLIGHT(*next);
  if(flag==0)
     strcpy(reason,"$1\\times x = x$");
  else
     strcpy(reason,"$x\\times 1 = 1$");
  return 0;
}
/*___________________________________________________________________*/
int bringminusout(term t, term arg, term *next, char *reason)
/* An operator, leading from ab(-c)d to -abcd.  Does not cancel
double minuses nor bring out more than one minus.
  In case of  input like  a(-(bc))d  it can produce either -abcd (the default)
 or  -a(bc)d  if user is still learning regroupfactors. */
/* If doubleminus is well-known, it can apply to  -(a(-b)) to give ab */
/* does not bring out a deeply embedded minus, e.g.  a(b(-c)d) will not be
touched.  This requires TWO applications of bringminusout, or else one
application of regroupterms and then an application of bringminusout */

{ int i,j,m,err;
  term temp,s;
  unsigned short n = ARITY(t);
  if(FUNCTOR(t)== '-' && status(doubleminus)==WELLKNOWN)
     { err = bringminusout(ARG(0,t),arg,&temp,reason);
       if(err)
          return 1;  /* nothing to do */
       tneg(temp,next);
       HIGHLIGHT(*next);
       if(FUNCTOR(ARG(0,ARG(0,t))) == '-')
          strcpy(reason,"$-(-(a)b) = ab$");
       else
          strcpy(reason,"$-(a(-b)) = ab$");
       return 0;
     }
  if(ATOMIC(t) || FUNCTOR(t) != '*')
     return 1; /* inapplicable */
  for(i=0;i<n;i++)
     { s = ARG(i,t);
       if(FUNCTOR(s) == '-')
          { if(equals(ARG(0,s),infinity))  /* don't touch -infinity */
              continue;
            break;
          }
       if(FUNCTOR(s) == '+')
          { for(j=0;j<ARITY(s);j++)
               { if(FUNCTOR(ARG(j,s)) != '-')
                    break;
               }
            if(j < ARITY(s))
               continue;
            else  /* we have a sum with a - sign on each term */
               break;
          }
     }
  if(i==n)
     return 1;  /* no minuses among the factors */
    /* now ARG(i,t) = - X for some X, or else (-A-B...) */
  if(FUNCTOR(ARG(i,t)) == '+')
     { s = make_term('*',n);
       for(j=0;j<i;j++)
          ARGREP(s,j,ARG(j,t));
       ARGREP(s,i,strongnegate(ARG(i,t)));
       for(j=i+1;j<n;j++)
          ARGREP(s,j,ARG(j,t));
       tneg(s,next);
     }
  else if(equals(ARG(i,t),minusone) && status(bringminusout) > LEARNING)
     {  /* don't leave a factor of 1 inside unless learning this operator */
       if(n==2)
          tneg(ARG( i ? 0 : 1, t),next);
       else
          { temp = make_term('*',(unsigned short)(n-1));
            for(j=0;j<i;j++) ARGREP(temp,j,ARG(j,t));
            for(j=i+1;j<n;j++) ARGREP(temp,j-1,ARG(j,t));
            tneg(temp,next);
          }
     }
  else if(FUNCTOR(ARG(0,ARG(i,t))) != '*' || status(regroupfactors) <= LEARNING)
     { temp = make_term('*',n);
       for(j=0;j<i;j++)
          ARGREP(temp,j,ARG(j,t));
       ARGREP(temp,i,ARG(0,ARG(i,t)));
       for(j=i+1;j<n;j++)
          ARGREP(temp,j,ARG(j,t));
       tneg(temp,next);
     }
  else   /* a(-(bc)) = -(abc) */
     { m = ARITY(ARG(0,ARG(i,t)));
       temp = make_term('*',(unsigned short)(n+m-1));
       for(j=0;j<i;j++)
          ARGREP(temp,j,ARG(j,t));
       for(j=i;j<i+m;j++)
          ARGREP(temp,j,ARG(j-i,ARG(0,ARG(i,t))));
       for(j=i+m;j<n+m-1;j++)
          ARGREP(temp,j,ARG(j-m+1,t));
       tneg(temp,next);
     }
  HIGHLIGHT(*next);
  if(FUNCTOR(ARG(i,t))=='+')
     strcpy(reason,"$a(-b-c)= -a(b+c)$");
  else if(i==0)
     strcpy(reason,"$(-a)b = -ab$");
  else if(n==2)
     strcpy(reason,"$a(-b) = -ab$");
  else
     strcpy(reason,"a(-b)c = -abc");
  return 0;
}
/*___________________________________________________________________*/
int bringminusout2(term t, term arg, term *next, char *reason)
/* a(-b-c) = -a(b+c) */
{ unsigned short n;
  term u;
  int i,j,err;
  if(FUNCTOR(t) != '*')
     return 1;
  n = ARITY(t);
  for(i=0;i<n;i++)
     { u = ARG(i,t);
       if(FUNCTOR(u) != '+')
          continue;
       /* are all the args of u negative? */
       for(j=0;j<ARITY(u);j++)
          { if(!NEGATIVE(ARG(j,u)))
               break;
          }
       if(j == ARITY(u))
          break;
     }
  if(i==n)
     return 1;
  err = bringminusout(t,arg,next,reason);
  if(err)
     return 1;
  strcpy(reason,"a(-b-c) = -a(b+c)");
  return 0;
}
/*___________________________________________________________________*/
int bringminusout3(term t, term arg, term *next, char *reason)
/* (-a-b)c = -(a+b)c */
{ term u;
  int j,err;
  if(FUNCTOR(t) != '*')
     return 1;
  u = ARG(0,t);
  if(FUNCTOR(u) != '+')
     return 1;
  /* are all the args of u negative? */
  for(j=0;j<ARITY(u);j++)
     { if(!NEGATIVE(ARG(j,u)))
          return 1;
     }
  err = bringminusout(t,arg,next,reason);
  if(err)
     return 1;
  strcpy(reason,"$(-a-b)c = -(a+b)c$");
  return 0;
}
/*_______________________________________*/
int regroupfactors(term t, term arg, term *next, char *reason)
/* An operator.  It eliminates parentheses using associativity.
Example:   *(a,b,(cd),(ef)) produces *(a,b,c,d,e,f)
Does NOT take out minus signs.   */
{ int err;
  err = factors(t,next);
  if(err)
     return 1;
  strcpy(reason, english(415));  /* regroup factors */
  HIGHLIGHT(*next);
  return 0;
}
/*___________________________________________________________________*/
int collectnumbers(term t, term arg, term *next, char *reason)

/* Collect all numerical factors of t together (if there are two or more)
and bring them to the front.  (A single numerical factor is just brought
to the front, and arithmetic done on it if possible.)
If still learning collectnumbers, don't multiply the
numerical terms together; but otherwise, do multiply them. */
/* If the product of the numerical factors is 1:  then if collectnumbers
is WELLKNOWN, delete the 1 and just show the symbolic factors; but if
not, go ahead and show the 1 */

{ unsigned short count;
  unsigned short n = ARITY(t);
  unsigned short path[5];
  term tempnum,tempsym,num;
  int i,j,k,err;
  if(FUNCTOR(t) != '*')
     return 1;  /* inapplicable */
  /* first count the numerical factors */
  count = 0;
  for(i=0;i < n; i++)
     { if(numerical(ARG(i,t)))
          ++count;
     }
  if(count <= 1)
     return 1;  /* fail */
  if(status(collectnumbers) <= LEARNING)
     { *next = make_term('*',n);
       k=0;  /* put i-th arg of t in position k or j according as it's numerical */
       j= count;
       for(i=0;i<n;i++)
          { if(numerical(ARG(i,t)))
               { ARGREP(*next,k,ARG(i,t));
                 ++k;
               }
            else
               { ARGREP(*next,j,ARG(i,t));
                 ++j;
               }
          }
       strcpy(reason, english(419)); /* collect numbers */
       return 0;
     }
  else if(count == n)  /* all factors were numerical */
     {  err = arithmetic(t,arg,next,reason);
        if(!err)
           { SetShowStepOperation(arithmetic);
             return 0;
           }
        return err;
     }
  else if(count == 1)
     { if(numerical(ARG(0,t)))  /* one numerical arg in first position */
          { *next = make_term('*',n);
             err = arithmetic(ARG(0,t),arg,ARGPTR(*next),reason);
             if(err==0)
                { for(i=1;i<n;i++)
                     ARGREP(*next,i,ARG(i,t));
                  SetShowStepOperation(arithmetic);
                  path[0] = '*';
                  path[1] = 1;
                  path[2] = 0;
                  set_pathtail(path);
                }
             return err;
          }
       /* now there's one numerical term but not in first position */
       i=0;
       while(i<n && !numerical(ARG(i,t)))
           { ARGREP(*next,i+1,ARG(i,t));
             ++i;
           }
       value(ARG(i,t),ARGPTR(*next));
       if(equals(ARG(i,t),ARG(0,*next)))
          strcpy(reason, english(416));  /* bring number to front */
       else
          { strcpy(reason,english(417)); /* arithmetic */
            SetShowStepOperation(arithmetic);
          }
       HIGHLIGHT(ARG(0,*next));
       i++;
       while(i<n)
          { ARGREP(*next,i,ARG(i,t));
            ++i;
          }
       return 0;
     }
  /* at least two factors in the answer, unless the numerical part
     turns out to be 1 and there's only one symbolic factor */
  *next = make_term('*',n);  /* not n-count+1, see below */
  tempnum = make_term('*',count);
  tempsym = make_term('*',(unsigned short)(n-count));
  /* conceivably arity of tempsym is 1 */
  k=j=0;  /* put i-th arg of t in position j of tempsym if not numerical
           position k of tempnum if it's numerical */
  for(i=0;i<n;i++)
     { if(numerical(ARG(i,t)))
          { ARGREP(tempnum,k,ARG(i,t));
            ++k;
          }
       else
          { ARGREP(tempsym,j,ARG(i,t));
            ++j;
          }
     }
  err = value(tempnum,&num);
   /* tempnum can have e.g. some sqrts, so err can be nonzero */
  if(err==2)
     num = tempnum;
  if(err > 2)
     { errbuf(0,aem(err));
       return 1;
     }
  if(err == 1)
     return 1;
  if(ONE(num) && status(collectnumbers)==WELLKNOWN)
     { if(ARITY(tempsym) > 1)
          { RELEASE(*next); /*allocated above */
            *next = tempsym;
          }
       else
          { RELEASE(*next);
            *next = ARG(0,tempsym);
            RELEASE(tempsym);
          }
       HIGHLIGHT(*next);
       strcpy(reason, english(418));
           /* numbers multiply to 1 */
       return 0;
     }
  else
     { HIGHLIGHT(num);
       if(ATOMIC(num) || FUNCTOR(num) != '*' )
          { ARGREP(*next,0,num);
            for(i=1;i<n-count+1;i++)
            ARGREP(*next,i,ARG(i-1,tempsym));
            SETFUNCTOR(*next, '*', (unsigned short)(n-count + 1));
          }
       else 
          { k=0;
            for(i=0;i<ARITY(num); i++)
               { ARGREP(*next,k,ARG(i,num));
                 ++k;
               }
            for(i=1;i<n-count+1;i++)
               { ARGREP(*next,k,ARG(i-1,tempsym));
                 ++k;
               }
          }
       strcpy(reason, english(419)); /* collect numbers */
       RELEASE(tempnum);
       RELEASE(tempsym);
       return 0;
     }
}
/*___________________________________________________________________*/
int collectpowers(term t, term arg, term *next, char *reason)
/* examples: x^n x^2 = x^(n+2); x^2 x^3 = ^5 */
/* if learning collectpowers, does not add the exponents together,
   but otherwise, it does if it can */
/* does NOT regroup terms */
{  int ans;
   int flag = (status(collectpowers) >= KNOWN);
   unsigned short n;
   int i,j;
   term u,w,v;
   unsigned short path[11];
   if(FUNCTOR(t) != '*')
     return 1;
   if(get_mathmode() == AUTOMODE)
      { n = ARITY(t);
        /* first check for a factor of the form (a^b)^c or (ab)^c, and
           imitate the operators that should simplify those terms, if
           in automode.  This is too hard to control from outside. */

       for(i=0;i<n;i++)
          { u = ARG(i,t);
            if(FUNCTOR(u) == '^' && FUNCTOR(ARG(0,u)) == '^' &&
               !NEGATIVE(ARG(1,u)) &&
               !(ISINTEGER(ARG(0,ARG(0,u))) && ISINTEGER(ARG(1,ARG(0,u)))) &&
               /* don't touch (2^2)^n for example, see comments in
                  postops and preops about using powertopower. */
               !powertopower(u,arg,&w,reason)
              )
               { *next = make_term('*',n);
                 for(j=0;j<n;j++)
                    ARGREP(*next,j, j == i ? w : ARG(j,t));
                 path[0] = '*';
                 path[1] = i+1;
                 path[2] = 0;
                 set_pathtail(path);
                 SetShowStepOperation(powertopower);
                 return 0;
               }
            if(FUNCTOR(u) == '^' && FUNCTOR(ARG(0,u)) == '*' &&
               !NEGATIVE(ARG(1,u)) &&
               !producttopower(u,arg,&w,reason) &&
               FUNCTOR(w) == '*'   /* as it must */
              )
               { for(j=0;j<ARITY(w);j++)
                    HIGHLIGHT(ARG(j,w));  /* so the highlights won't get lost
                                             in flattening */
                 v = make_term('*',n);
                 for(j=0;j<n;j++)
                    ARGREP(v,j, j==i ? w : ARG(j,t));
                 *next = topflatten(v);
                 path[0] = '*';
                 path[1] = i+1;
                 path[2] = 0;
                 set_pathtail(path);
                 SetShowStepOperation(producttopower);
                 return 0;
               }
          }
      }
   ans = rawcollectpowers(t,next,flag);
   if(ans)
      return 1;
   strcpy(reason, english(420)); /* collect powers */
   if(flag)
      { if(FUNCTOR(*next) == '*' && ARITY(*next) <= 3)
           sortargs(*next);  /* e.g.  x^2\cdot 2x^2 = 2x^4, not x^4\cdot 2  */
        if(FUNCTOR(*next) == '^' && !numerical(ARG(1,*next)))
           { term temp = *next;
             polyval1(temp,next);  /* e.g.  2\cdot 2^(n+1) = 2^(n+2), not 2^(1+(n+1)) */
           }
      }
   return 0;
}
/*___________________________________________________________________*/
int difofsquares(term t, term arg, term *next, char *reason)
/*  (a-b)(a+b) = a^2-b^2 and variations with different orders and signs */
/* specifically  (a-b)(b+a); (-b+a)(a+b); (-b+a)(b+a); and these same
four with the two factors reversed */
/*  must also catch    u(a-b)v(a+b)w   etc.  */
/* must also catch u v^q, u^p v, and u^p v^q where u = a-b, v= a+b in
the various orders */

{ term a,b,c,u,v,r,s;
  unsigned short n = ARITY(t);
  int err,k;
  int i,j,swap;   /* the two factors are the i-th and j-th args of t */
  long p,q;   /* as in comments above */
  if(FUNCTOR(t) != '*' )
     return 1; /* inapplicable */
  err = getuv(t,&i,&j,&p,&q,&a,&b);   /* find a match for (a-b)^p and (a+b)^q
                                   among the factors of t */
  if(err)
     return 1;
  strcpy(reason,"$(a-b)(a+b)=a^2-b^2$");
/* create the term c = a^2-b^2 */
  r = square(a);
  s = square(b);
  c = sum(r,tnegate(s));
  if(!contains_sqrt(a) && !contains_sqrt(b) &&
     FUNCTOR(r) == '^' && FUNCTOR(s) == '^' &&  /* example, a = z^(1/2), r = z,
                                                no need to protect c, and
                                                indeed it prevents collecting
                                                terms in c, so better not */
     FUNCTOR(a) != '^' && FUNCTOR(b) != '^'     /* example, (e^x-1)(e^x+1),
                                                if we PROTECT it, we block
                                                (e^x)^2 = e^(2x) */
    )
      PROTECT(c);   /* prevent it being factored by differenceofsquares and
                   going into an infinite regress */
  HIGHLIGHT(c);
     /* Now c is a^2-b^2  */
  if(n==2 && p==1 && q==1) /* answer is c */
     { *next = c;
       HIGHLIGHT(*next);
       goto out;
     }
  if(n==2 && p==q)  /* answer is c^p */
     { *next = make_power(c,make_int(p));
       HIGHLIGHT(*next);
       goto out;
     }
     /* now the answer will be a product */
  if(p==q)      /* answer has one less factor than t */
     { *next = make_term('*',(unsigned short)(n-1));
       if(j < i)
          { swap = j;
            j = i;
            i = swap;
          }
          /* put (a^2-b^2)^p  in the i-th argument place */
       for(k=0;k<i;k++)
          ARGREP(*next,k,ARG(k,t));
       if(p==1)
          ARGREP(*next,i,c);
       else
          ARGREP(*next,i,make_power(c,make_int(p)));
       for(k=i+1;k<n-1;k++)
          ARGREP(*next,k,ARG( ((k<j) ? k : k+1), t));
       HIGHLIGHT(ARG(i,*next));
       goto out;
     }
  /* Now the answer will be a product in which there are as many factors
     as in t */
  if(p < q)      /* i-th arg gets c^p; j-th arg gets v^(q-p) */
     { *next = make_term('*',n);
       for(k=0;k<n;k++)
          if(k!= i && k != j) ARGREP(*next,k,ARG(k,t));
       if(p==1)
          ARGREP(*next,i,c);
       else
           ARGREP(*next,i,make_power(c,make_int(p)));
       v= ((FUNCTOR(ARG(j,t))=='^') ? ARG(0,ARG(j,t)) : ARG(j,t));
       if(q-p == 1) ARGREP(*next,j,v);
       else
           ARGREP(*next,j,make_power(v,make_int(q-p)));
     }
  else if(q < p)      /* j-th arg gets c^q; i-th arg gets u^(q-p) */
     { *next = make_term('*',n);
       for(k=0;k<n;k++)
          if(k!= i && k != j) ARGREP(*next,k,ARG(k,t));
       if(q==1) ARGREP(*next,j,c);
       else
          ARGREP(*next,j,make_power(c,make_int(q)));
       u= ((FUNCTOR(ARG(i,t))=='^') ? ARG(0,ARG(i,t)) : ARG(i,t));
       if(p-q == 1)
          ARGREP(*next,i,u);
       else
          ARGREP(*next,i,make_power(u,make_int(p-q)));
     }
  HIGHLIGHT(ARG(i,*next));
  HIGHLIGHT(ARG(j,*next));
  out:
     release(cancelop);     /* in case inhibited by rationalizenum or rationalizedenom */
     release(polyvalop);
     release(cancelgcd);
     release(apart);      /* in case inhibited by trigrationalize1 etc. */
     release(apartandcancel);
     return 0;
}
/*_______________________________________________________________________*/
int makedifofcubes(term t, term arg, term *next, char *reason)
/*  (a-b)(a^2+ab+b^2) = a^3-b^3 */
/*  Unlike difofsquares this works only on products of arity 2 */
{ term u,v,a,b,acube,bcube,p;
  int err;
  int sign = 1;
  if(FUNCTOR(t) != '*' || ARITY(t) != 2)
     return 1;
  u = ARG(0,t);
  v = ARG(1,t);
  if(FUNCTOR(u) != '+' || FUNCTOR(v) != '+')
     return 1;
  if(ARITY(u) == 3 && ARITY(v)== 2)
     { /* switch u and v */
       term temp = u;
       u = v;
       v = temp;
     }
  if(ARITY(u) != 2 || ARITY(v) != 3)
     return 1;
  a = ARG(0,u);
  b = ARG(1,u);
  if(NEGATIVE(b))
     { b = ARG(0,b);
       sign = -1;
     }
  if(sign != -1)
     return 1;
  if(FUNCTOR(ARG(0,v)) != '^' && !OBJECT(ARG(0,v)))
     /* The reason for allowing objects is so it can work on a^2+a+1 etc. */
     return 1;
  if(FUNCTOR(ARG(2,v)) != '^' && !OBJECT(ARG(2,v)))
     return 1;
  if(FUNCTOR(ARG(0,v)) != '^' && FUNCTOR(ARG(2,v)) != '^')
     return 1;
  err = sqrt_aux(ARG(0,v),&p);
  if(err || !equals(p,a))
     return 1;
  err = sqrt_aux(ARG(2,v),&p);
  if(err || !equals(p,b))
     return 1;
  /* cubic case, negative sign */
  if((FUNCTOR(ARG(1,v)) == '*' &&
      (
        (equals(ARG(0,ARG(1,v)),a) && equals(ARG(1,ARG(1,v)),b)) ||
        (equals(ARG(1,ARG(1,v)),a) && equals(ARG(0,ARG(1,v)),b))
      )
     ) ||
     (FUNCTOR(ARG(0,v)) == '^' &&
      ONE(ARG(2,v)) && ONE(b) &&
      equals(ARG(1,v),a)
     ) ||
     (FUNCTOR(ARG(2,v)) == '^' &&
      ONE(ARG(0,v)) && ONE(a) &&
      equals(ARG(1,v),b)
     )
    )
     { if(FUNCTOR(a) == ROOT && equals(ARG(0,a),three))
          acube = ARG(1,a);
       else if(FUNCTOR(a) == '^')
          acube = ARG(0,a);
       else if(ONE(a))
          acube = one;
       else
          acube = make_power(a,three);
       if(FUNCTOR(b) == ROOT && equals(ARG(0,b),three))
          bcube = ARG(1,b);
       else if(FUNCTOR(b) == '^')
          bcube = ARG(0,b);
       else if(ONE(b))
          bcube = one;
       else
          bcube = make_power(b,three);
       *next = sum(acube,tnegate(bcube));
       strcpy(reason,"$(a-b)(a^2+ab+b^2)=a^3-b^3$");
       HIGHLIGHT(*next);
       HIGHLIGHT(*next);
       release(cancelop);     /* in case it was inhibited by rationalizenum or rationalizedenom */
       release(polyvalop);
       return 0;
     }
  return 1;
 }

/*_______________________________________________________________________*/
int makesumofcubes(term t, term arg, term *next, char *reason)
/*  (a+b)(a^2-ab+b^2) = a^3+b^3 */
/*  Unlike difofsquares this works only on products of arity 2 */
{ term u,v,w,a,b,acube,bcube,p;
  int err;
  if(FUNCTOR(t) != '*' || ARITY(t) != 2)
     return 1;
  u = ARG(0,t);
  v = ARG(1,t);
  if(FUNCTOR(u) != '+' || FUNCTOR(v) != '+')
     return 1;
  if(ARITY(u) == 3 && ARITY(v)== 2)
     { /* switch u and v */
       term temp = u;
       u = v;
       v = temp;
     }
  if(ARITY(u) != 2 || ARITY(v) != 3)
     return 1;
  a = ARG(0,u);
  b = ARG(1,u);
  if(NEGATIVE(b))
     return 1;
  if(ARITY(v) != 3)
     return 1;
  if(FUNCTOR(ARG(0,v)) != '^' && !OBJECT(ARG(0,v)))
     /* The reason for allowing objects is so it can work on a^2+a+1 etc. */
     return 1;
  if(FUNCTOR(ARG(2,v)) != '^' && !OBJECT(ARG(2,v)))
     return 1;
  if(FUNCTOR(ARG(0,v)) != '^' && FUNCTOR(ARG(2,v)) != '^')
     return 1;
  if(!NEGATIVE(ARG(1,v)))
     return 1;
  err = sqrt_aux(ARG(0,v),&p);
  if(err || !equals(p,a))
     return 1;
  err = sqrt_aux(ARG(2,v),&p);
  if(err || !equals(p,b))
     return 1;
  w = ARG(0,ARG(1,v));
  if(
     (FUNCTOR(w) == '*' &&
      (
        (equals(ARG(0,w),a) && equals(ARG(1,w),b)) ||
        (equals(ARG(1,w),a) && equals(ARG(0,w),b))
      )
     ) ||
     (ONE(b) && equals(w,a) && ONE(ARG(2,v))) ||
     (ONE(a) && equals(w,b) && ONE(ARG(0,v)))
    )
     { if(FUNCTOR(a) == ROOT && equals(ARG(0,a),three))
          acube = ARG(1,a);
       else if(FUNCTOR(a) == '^')
          acube = ARG(0,a);
       else if(ONE(a))
          acube = one;
       else
          acube = make_power(a,three);
       if(FUNCTOR(b) == ROOT && equals(ARG(0,b),three))
          bcube = ARG(1,b);
       else if(FUNCTOR(b) == '^')
          bcube = ARG(0,b);
       else if(ONE(b))
          bcube = one;
       else
          bcube = make_power(b,three);
       *next = sum(acube,bcube);
       strcpy(reason,"$(a+b)(a^2-ab+b^2)=a^3+b^3$");
       HIGHLIGHT(*next);
       release(cancelop);     /* in case it was inhibited by rationalizenum or rationalizedenom */
       release(polyvalop);
       return 0;
     }
  return 1;
}
/*_______________________________________________________________________*/
int difofpowers(term t, term arg, term *next, char *reason)
/*  (a-b)(a^2+ab+b^2) = a^3-b^3 and similarly for higher powers */
/*  Unlike difofsquares this works only on products of arity 2 */

{ unsigned short n;
  term u,v,w,a,b,acube,bcube;
  int sign = 1;
  int i;
  if(FUNCTOR(t) != '*' || ARITY(t) != 2)
     return 1;
  u = ARG(0,t);
  v = ARG(1,t);
  if(FUNCTOR(u) != '+' || FUNCTOR(v) != '+')
     return 1;
  if(ARITY(u) >= 3 && ARITY(v)== 2)
     { /* switch u and v */
       term temp = u;
       u = v;
       v = temp;
     }
  if(ARITY(u) != 2 || ARITY(v) < 3)
     return 1;
  a = ARG(0,u);
  b = ARG(1,u);
  if(NEGATIVE(b))
     { b = ARG(0,b);
       sign = -1;
     }
  n = ARITY(v);
  if(FUNCTOR(ARG(0,v)) != '^' && !OBJECT(ARG(0,v)))
     /* The reason for allowing objects is so it can work on a^2+a+1 etc. */
     return 1;
  if(FUNCTOR(ARG(n-1,v)) != '^' && !OBJECT(ARG(n-1,v)))
     return 1;
  if(FUNCTOR(ARG(0,v)) != '^' && FUNCTOR(ARG(n-1,v)) != '^')
     return 1;
  if(FUNCTOR(ARG(0,v)) == '^')
     { if(!equals(ARG(0,ARG(0,v)),a))
          return 1;
       if(!equals(ARG(0,ARG(n-1,v)),b))
          return 1;
       if(!ISINTEGER(ARG(1,ARG(0,v))))
          return 1;
       if(INTDATA(ARG(1,ARG(0,v))) != n-1)
          return 1;
     }
  if(FUNCTOR(ARG(n-1,v)) == '^')
     { if(!ISINTEGER(ARG(1,ARG(n-1,v))))
          return 1;
       if(INTDATA(ARG(1,ARG(n-1,v))) != n-1)
          return 1;
     }
  if(n==3 && sign == -1)  /* cubic case, negative sign */
     { if(
          (FUNCTOR(ARG(1,v)) == '*' &&
           (
            (equals(ARG(0,ARG(1,v)),a) && equals(ARG(1,ARG(1,v)),b)) ||
            (equals(ARG(0,ARG(1,v)),b) && equals(ARG(1,ARG(1,v)),a))
           )
          ) ||
          ( ONE(b) && ONE(ARG(2,v)) && equals(ARG(1,v),a))
         )
          { if(FUNCTOR(a) == ROOT && equals(ARG(0,a),three))
               acube = ARG(1,a);
            else
               acube = make_power(a,three);
            if(FUNCTOR(b) == ROOT && equals(ARG(0,b),three))
               bcube = ARG(1,b);
            else
               bcube = make_power(b,three);
            *next = sum(acube,tnegate(bcube));
            strcpy(reason,english(899));
                /* multiply out        and simplify */
            HIGHLIGHT(*next);
            goto out;
          }
       else
          return 1;
     }
  if(n==3)  /* cubic case, positive sign */
     { if(!NEGATIVE(ARG(1,v)))
          return 1;
       w = ARG(0,ARG(1,v));
       if(
          (FUNCTOR(w) == '*' &&
           (
            (equals(ARG(0,w),a) && equals(ARG(1,w),b)) ||
            (equals(ARG(0,w),b) && equals(ARG(1,w),a))
           )
          ) ||
          (ONE(b) && ONE(ARG(2,v)) && equals(w,a))
         )
          { if(FUNCTOR(a) == ROOT && equals(ARG(0,a),three))
               acube = ARG(1,a);
            else
               acube = make_power(a,three);
            if(FUNCTOR(b) == ROOT && equals(ARG(0,b),three))
               bcube = ARG(1,b);
            else
               bcube = make_power(b,three);
            *next = sum(acube,bcube);
            strcpy(reason,english(899));
                /* multiply out         and simplify */
            goto out;
          }
       else
          return 1;
     }
  if(sign == -1)  /* general case, negative sign */
     { for(i=1;i<n-1;i++)
          { w = ARG(i,v);
            if(ONE(b))
               { if(!equals(w,make_power(a,make_int(i))))
                    return 1;
               }
            else if(OBJECT(b) && OBJECT(w))
               { int err;
                 term temp;
                 err = value(make_power(a,make_int(i)),&temp);
                 if(err || !equals(temp,w))
                     return 1;
               }
            else
               { if(FUNCTOR(w) != '*')
                    return 1;
                 if(!equals(ARG(0,w),make_power(a,make_int(i))))
                    return 1;
                 if(!equals(ARG(1,w),make_power(b,make_int(n-i-1))))
                    return 1;
               }
          }
       if(FUNCTOR(a) == ROOT && INTDATA(ARG(0,a))==n)
          acube = ARG(1,a);
       else
          acube = make_power(a,make_int(n));
       if(FUNCTOR(b) == ROOT && INTDATA(ARG(0,b))==n)
          bcube = ARG(1,b);
       else if(ONE(b))
          bcube = one;
       else
          bcube = make_power(b,make_int(n));
       *next = sum(acube,tnegate(bcube));
       goto out;
     }
  else            /* general case, positive sign */
     { for(i=1;i<n-1;i++)
          { w = ARG(i,v);
            if(i&1)
               { if(!NEGATIVE(w))
                    return 1;
                 w = ARG(0,w);
               }
            if(ONE(b))
               { if(!equals(w,make_power(a,make_int(i))))
                    return 1;
               }
            else if(OBJECT(b) && OBJECT(w))
               { term temp;
                 int err;
                 err = value(make_power(a,make_int(i)),&temp);
                 if(err || !equals(temp,w))
                     return 1;
               }
            else
               { if(FUNCTOR(w) != '*')
                    return 1;
                 if(!equals(ARG(0,w),make_power(a,make_int(i))))
                    return 1;
                 if(!equals(ARG(1,w),make_power(b,make_int(n-i-1))))
                    return 1;
               }
          }
       if(FUNCTOR(a) == ROOT && INTDATA(ARG(0,a))==n)
          acube = ARG(1,a);
       else
          acube = make_power(a,make_int(n));
       if(FUNCTOR(b) == ROOT && INTDATA(ARG(0,b))==n)
          bcube = ARG(1,b);
       else if(ONE(b))
          bcube = one;
       else
          bcube = make_power(b,make_int(n));
       *next = sum(acube,bcube);
       goto out;
     }
  out:
     HIGHLIGHT(*next);
     release(cancelop);     /* in case it was inhibited by rationalizenum or rationalizedenom */
     release(polyvalop);
     return 0;
}
/*___________________________________________________________________*/
int squareofdif(term t, term arg, term *next, char *reason)
/* (a-b)^2 = a^2 - 2ab + b^2.  Does not work on (a+b-c)^2. */
{ term u,a,b;
  if(FUNCTOR(t) != '^')
     return 1;  /* inapplicable */
  u = ARG(0,t);
  if(FUNCTOR(u )!= '+')
     return 1;  /* inapplicable */
  if(ARITY(u) != 2)
     return 1;
  a = ARG(0,u);
  b = ARG(1,u);
  if(
     (NEGATIVE(a) && !NEGATIVE(b)) ||
     (NEGATIVE(b) && !NEGATIVE(a))
    )
      return squareofsum(t,arg,next,reason);
  return 1;
}
/*___________________________________________________________________*/
int squareofsum(term t, term arg, term *next, char *reason)
/*  (a  \pm  b)^2^n  = (a^2  \pm  2ab + b^2)^n  */
{ int i,j,err;
  int sign;  /* 1 if it's (a+b), -1 if it's (a-b) */
  term a,b,u,power,asq,bsq,twoab,temp,temp2;
  term n;
  unsigned short m;
  if(FUNCTOR(t) != '^')
     return 1;  /* inapplicable */
  if(FUNCTOR(ARG(0,t)) != '+')
     return 1;  /* inapplicable */
  if(ARITY(ARG(0,t)) != 2)
     { if(get_mathmode() == AUTOMODE)
          return 1;   /* inapplicable */
       /* but in menu mode, use ((a+b) + c)^2 */
       m = ARITY(ARG(0,t));
       assert(m > 2);
       a = ARG(0,t);
       SETFUNCTOR(a,'+',(unsigned short)(m-1));
       u = make_term('+',2);
       ARGREP(u,0,a);
       ARGREP(u,1,ARG(m-1,ARG(0,t)));
       /* Not  u = sum(a,ARG(m-1,ARG(0,t))) because this
          would just produce a loop, because sum flattens the result. */
       return squareofsum(make_power(u,ARG(1,t)),arg,next,reason);
     }
  power = ARG(1,t);
  if(OBJECT(power) && TYPE(power) == INTEGER )
     { if(ODD(power))
          return 1;
       divide(power,two,&n);
     }
  else if(OBJECT(power) && TYPE(power) == BIGNUM)
     { if(BIGODD(t))
          return 1;
       divide(t,two,&n);
     }
  else if(FUNCTOR(power) == '*')
     /*  also can do the case when there is a 2 among the factors of power */
     { for(i=0;i<ARITY(power);i++)
          { if(OBJECT(ARG(i,power)) && TYPE(ARG(i,power))==INTEGER && INTDATA(ARG(i,power))==2)
               { if(ARITY(power)==2)
                    n = (i ? ARG(0,power) : ARG(1,power));
                 else
                    { n = make_term('*',(unsigned short)(ARITY(power)-1));
                      for(j=0;j<ARITY(n);j++)
                         ARGREP(n,j, ((j<i) ? ARG(j,power) : ARG(j+1,power)));
                    }
               }
          }
     }
  else
     return 1;  /* exponent not suitable */
  if(FUNCTOR(ARG(0,ARG(0,t))) == '-' && FUNCTOR(ARG(1,ARG(0,t))) != '-')
     { b= ARG(0,ARG(0,ARG(0,t)));
       sign = -1;
       a = ARG(1,ARG(0,t));
     }
  else if(FUNCTOR(ARG(1,ARG(0,t))) == '-' && FUNCTOR(ARG(0,ARG(0,t))) != '-')
     { b= ARG(0,ARG(1,ARG(0,t)));
       sign = -1;
       a = ARG(0,ARG(0,t));
     }
  else if(FUNCTOR(ARG(1,ARG(0,t))) == '-' && FUNCTOR(ARG(0,ARG(0,t))) == '-')
    { a = ARG(0,ARG(0,ARG(0,t)));
       sign = 1;
       b = ARG(0,ARG(1,ARG(0,t)));
     }
  else
     { sign = 1;
       a = ARG(0,ARG(0,t));
       b = ARG(1,ARG(0,t));
     }
   /* Now we have n, a, and b */
   /* make u = a^2  \pm  2ab + b^2 */
   asq = square(a);
   bsq = square(b);
   temp = product3(two,a,b);
   if(status(squareofsum) <= LEARNING)
      twoab = temp;
   else
      { err = collectnumbers_for_polyval(temp,&temp2);
        if(err)
           temp2 = temp;
        err = value(temp2,&twoab);
        if(err==1)
           twoab = temp2;
      }
   u = make_term('+',3);
   ARGREP(u,0,asq);
   ARGREP(u,1,sign > 0 ? twoab : tnegate(twoab));
   ARGREP(u,2,bsq);
   if(FUNCTOR(asq) == '+' || FUNCTOR(bsq) == '+')
      u = topflatten(u);  /* this happens if a is a sqrt */
   if(sign > 0)
       strcpy(reason,"$(a+b)^2 = a^2+2ab+b^2$");
   else
       strcpy(reason,"$(a-b)^2 = a^2-2ab+b^2$");
   if(ONE(n))
      *next = u;
   else
      *next = make_power(u,n);
   if(contains_sqrt(asq) == SQRT || contains_sqrt(bsq)==SQRT)
      { inhibit(factorsquareofsum);
        inhibit(factorsquareofdif);
        inhibit(factorquadratic);
        /* to be released by powerofsqrt */
      }
   HIGHLIGHT(*next);
   return 0;
}
/*______________________________________________________________________*/
int tmult(term a, term b, term *ans)
/* multiply a and b, getting *ans */
/* ans->args is allocated fresh */
/* Return 0 for success.  It can fail if one of a or b has more than 64 arguments */

{  if(status(multiplyout) >= KNOWN)
      return mvpolymult(a,b,ans); /* with terms already collected */
   return distribute(a,b,ans);   /* with terms not collected yet */
}
/*________________________________________________________________________*/
int simpprod(term t, term *ans)
/* simplify t using all non-multiplying-out laws on the simplify_products
menu, viz.,  x \cdot 0 = 0, x \cdot 1 = x, a(-b) = -ab,regroup factors,collect numbers,
order factors, collect powers */
/* return 0 if something was done, 1 if nothing was done; in either
case return the answer in *ans (it's just t if nothing was done) */

{ int i,err;
  int nvariables;
  int rval;  /* return value */
  term temp,temp2,temp3;
  char reason[DIMREASONBUFFER];
  if(FUNCTOR(t) != '*')
     { *ans = t;
       return 1;
     }
  err = multbyzero(t,one,ans,reason); /* one and reason are dummies here */
  if(!err)
     return 0;  /* finished, got zero */
  err = factors(t,&temp);  /* uses bringminusout too */
  if(!err && FUNCTOR(temp) == '*')
     sortargs(temp);
  err = collectnumbers_for_polyval(temp,&temp2);
  if(err)
     temp2 = temp;
  err = 0;  /* so we at least enter the next loop */
  i=0;
  nvariables = get_nvariables();
  while(! err)   /* each time rawcollectpowers collects only one variable */
    { err = rawcollectpowers(temp2,&temp3,1);
      ++i;
      if(err==0)
          temp2 = temp3;
      if(i==nvariables)
          break;     /* no need to apply rawcollectpowers more times than
                        there are variables */
    }
  /* collectnumbers can leave a 1 at the front */
  if(FUNCTOR(temp2) == '*' && ONE(ARG(0,temp2)))
     { if(ARITY(temp2)==2)
          { *ans = ARG(1,temp2);
            return 0;
          }
       *ans = make_term('*',(unsigned short)(ARITY(temp2)-1));
       for(i=0;i<ARITY(*ans);i++)
          { ARGREP(*ans,i,ARG(i+1,temp2));
          }
       return 0;
     }
  rval = equals(temp2,t) ? 1 : 0;
  *ans = temp2;  /* even if nothing was done */
  return rval;
}
/*__________________________________________________________*/
int distriblaw(term t, term arg, term *next, char *reason)
/* apply a(b+c) = ab+ac to a product containing only ONE or TWO sums */
{ int i,count,marker,err;
  unsigned short n,m;
  term s,c,cc,cancelled,u,v;
  if(NEGATIVE(t) && FUNCTOR(ARG(0,t)) == '*')
     /* work on -3(x+y) producing -3x-3y */
     { err = distriblaw(ARG(0,t),arg,&u,reason);
       if(err)
          return 1;
       *next = strongnegate(u);
       return 0;
     }
  if(FUNCTOR(t) != '*')
     return 1;
  n = ARITY(t);
  count = 0;
  for(i=0;i<n;i++)
     { v = ARG(i,t);
       if(FUNCTOR(v)=='-')
          v = ARG(0,v);
       if(FUNCTOR(v)=='+')
          { ++count;
            marker = i;
          }
     }
  if(count > 2)
     { errbuf(0, english(421)); /* Use multiply out if you want */
       errbuf(1, english(422)); /* to multiply out a product of */
       errbuf(2, english(423)); /* more than two sums. */
       return 1;
     }
  if(count == 0)
     return 1;
  s = ARG(marker,t);  /* the last sum */
  m = ARITY(s);
  *next = make_term('+',m);
  if(n==2)
      c = ARG(marker ? 0 : 1,t);
  else
      cancel(t,s,&cancelled,&c);
  if(status(cancelop) > LEARNING &&
     status(collectpowers) > LEARNING &&
     status(orderfactors) > LEARNING
    )
     { for(i=0;i<m;i++)
          { copy(c,&cc);  /* To avoid creating a DAG */
            ARGREP(*next,i,multiply_cancel_and_order(cc,ARG(i,s)));
          }
     }
  else
     { for(i=0;i<m;i++)
          { copy(c,&cc);
            ARGREP(*next,i,signedproduct(cc,ARG(i,s)));
          }
     }
  if(status(orderfactors) >= KNOWN)
     { for(i=0;i<m;i++)
          { u = ARG(i,*next);
            if(FUNCTOR(u) == '*')
                sortargs(u);
          }
     }
  strcpy(reason, "$a(b+c)=ab+ac$");
  HIGHLIGHT(*next);
  return 0;
}
/*__________________________________________________________*/
int distribandcancel(term t, term arg, term *next, char *reason)
/* used in automode only.  Distributes (a/b)(u1 + u2 + ...)  provided
one of the products (a/b) u1  will cancel.  Does not work if a or b is
a sum. Also works on examples like x^2(1+x^-2). */

{ term a,b,c,u,v,cancelled,p;
  unsigned short n;
  int i,err;
  if(FUNCTOR(t) != '*' || ARITY(t) != 2)
     return 1;
  c = ARG(0,t);
  v = ARG(1,t);
  if(FUNCTOR(v) != '+')
     return 1;
  if(contains(c,'+'))
     return 1;
  n = ARITY(v);
  if(!FRACTION(c))
     { for(i=0;i<n;i++)
          { u = ARG(i,v);
            if(NEGATIVE(u))
               u = ARG(0,u);
            if(!FRACTION(u))
               continue;
            err = cancel(product(c,ARG(0,u)),ARG(1,u),&cancelled,&p);
            if(!err)
               return distriblaw(t,arg,next,reason);
          }
       if(FUNCTOR(c) == '*')
          { ratpart2(c,&a,&b);
            c = b;
          }
       if(FUNCTOR(c) != '^' && !ISATOM(c))
          return 1;
       if(FUNCTOR(c) == '^')
          c = ARG(0,c);
       if(!ISATOM(c))
          return 1;
       for(i=0;i<n;i++)
          { /* look for a negative power of c */
            u = ARG(i,v);
            if(NEGATIVE(u))
               u = ARG(0,u);
            twoparts(u,c,&a,&b);
            if(FUNCTOR(b) == '^' && NEGATIVE(ARG(1,b)) && NUMBER(ARG(0,ARG(1,b))))
               return distriblaw(t,arg,next,reason);
          }
       return 1;
     }
  /* Now c is a fraction */
  a = ARG(0,c);
  b = ARG(1,c);
  for(i=0;i<n;i++)
     { u = ARG(i,v);
       if(FRACTION(u))
          err = cancel(product(a,ARG(0,u)),product(b,ARG(1,u)),&cancelled,&p);
       else
          err = cancel(product(a,u),b,&cancelled,&p);
       if(!err)
          return distriblaw(t,arg,next,reason);
     }
  return 1;
}
/*_______________________________________*/
int multiplyout(term t, term arg, term *next, char *reason)
/* an operator, which is the supremum of difofsquares, multiplypolynomials,
and 'distribute' (which multiplies out without using polymult).
It multiplies out products of sums; it also multiplies out powers up to
the tenth of sums, but not higher powers. */

{ unsigned short n = ARITY(t);
  unsigned short f = FUNCTOR(t);
  char buffer[DIMREASONBUFFER];
  unsigned short path[3];
  term u,v,temp,trash;
  void  *savenode;
  int i,j,err,flag;
  unsigned short m;
  int  first = -1,second = -1;
  term partialproduct;
  if(f == '^' && FUNCTOR(ARG(0,t)) == '+')
    { term exp = ARG(1,t);
      if(equals(exp,two) && ARITY(ARG(0,t))==2)
         { err = squareofsum(t,arg,next,reason);
           if(!err)
              { term u = ARG(0,t);
                term a = ARG(0,u);
                term b = ARG(1,u);
                if((NEGATIVE(a) && !NEGATIVE(b)) || (NEGATIVE(b) && !NEGATIVE(a)))
                    SetShowStepOperation(squareofdif);
                else
                    SetShowStepOperation(squareofsum);
              }
           return err;
         }
      if(equals(exp,two))  /* and arity of base exceeds 2 */
         { term temp=ARG(0,t);
           mvpolymult(temp,temp,next);
           strcpy(reason, english(425));  /* multiply out */
           return 0;
         }
      if(ISINTEGER(exp) && INTDATA(exp) <= 10 && INTDATA(exp) != 0)
         { if(ARITY(ARG(0,t)) == 2)
              { err = plainbinomialtheorem(t,arg,next,buffer);
                if(err)
                   assert(0);
                strcpy(reason,buffer);
                if(GetShowStepOperation() == NULL)
                   SetShowStepOperation(plainbinomialtheorem);
                return 0;
              }
           /* Now we have a power of a sum of arity 3 or more */
           err = plainbinomialtheorem(t,arg,&temp,buffer);
           if(err  || FUNCTOR(temp) != '+')
              assert(0);
           m = ARITY(temp);
           v = make_term('+',m);
           for(i=0;i<m;i++)
              { err = multiplyout(ARG(i,temp),arg,&u,buffer);
                if(err)
                   ARGREP(v,i,ARG(i,temp));
                else
                   ARGREP(v,i,u);
              }
           polyval(v,next);
         }
      if(FUNCTOR(ARG(0,t))== '+')
         { errbuf(0, english(424));
             /* To expand a power, use the binomial theorem. */
           commentbuf(0,english(424));
              /* it can be called by multiplyoutandsimp, and succeed on
                 some squares but leave a cube unexpanded */
         }
      *next = t;
      return 1;  /* inapplicable to powers with too-large exponents or exponent 0 */
    }
  err = n > 2 ? 1 : difofsquares(t,arg,next,reason);
  if(!err)
     { SetShowStepOperation(difofsquares);
       return 0;
     }
  if(FUNCTOR(t) != '*')
     return 1;
  /*  Example:  (a-3)(a/(a-3) + 3) = a+3a-9, not a^2/(a-3)-3/(a-3)+3a-9 */
  /* Now check for the case of a product containing two factors which
     are sums, one of which contains some fractions and the other
     does not */
  for(i=0;i<n;i++)
     { u = ARG(i,t);
       if(FUNCTOR(u) == '+')
          { for(j=0;j<ARITY(u);j++)
               { v = ARG(j,u);
                 if(NEGATIVE(v))
                    v = ARG(0,v);
                 if(FUNCTOR(v) == '/')
                    break;
               }
            if(j==ARITY(u))
               first = i;
            else  /* it contained fractions */
               second = i;
          }
     }
  if(first >=0 && second >= 0)
     { err = multiplyoutandcancel(ARG(first,t),ARG(second,t),&temp);
       if(!err)
          { /* there was a cancellation */
            if(n==2)
               *next = temp;
            else
               { cancel(t,product(ARG(first,t),ARG(second,t)),&trash,&v);
                 polyval1(product(v,temp),next);
               }
            strcpy(reason, english(428)); /* multiply and cancel */
            HIGHLIGHT(*next);
            return 0;
          }
     }

   /* do all factors at once, term by term  */
  if(n == 2 &&
     (!contains(ARG(0,t),'+') || !contains(ARG(1,t),'+')) &&
     !equals(ARG(0,t),complexi) &&
     !equals(ARG(1,t),complexi)
     /* don't change (a+b)i to ai + bi because polyval1 will
        change it back again.  In a sum (a+b)i + ci, we
        don't need to multiply out before collecting. */
    )
     { err = distriblaw(t,arg,next,reason);
       if(!err)
          { SetShowStepOperation(distriblaw);
            return 0;
          }
     }
  if(n > 2)
      savenode = heapmax();  /* prepare for memory management below */
  temp = ARG(0,t);
  if(FUNCTOR(temp) == '^' && FUNCTOR(ARG(0,temp)) == '+' &&
     !multiplyout(temp,arg,&partialproduct,reason)
     )
     flag = 1;
  else
     { partialproduct = temp;
       flag = 0;
     }
  for(i=1;i<n;i++)
     { u = ARG(i,t);
       if(FUNCTOR(u)=='^' && FUNCTOR(ARG(0,u)) == '+' &&
          !multiplyout(u,arg,&v,reason) &&
          !tmult(partialproduct,v,&temp)  /* tmult could fail if too many args */
          )
            ++flag;
       else
          { err = tmult(partialproduct,u,&temp);
            if(err) /* too many args to multiply out */
               { /* temp has to have new space at toplevel  */
                 temp = make_term(FUNCTOR(partialproduct),ARITY(partialproduct));
                 for(j=0;j<ARITY(partialproduct);j++)
                    ARGREP(temp,j,ARG(j,partialproduct));
               }
          }
       if(flag)
          { SetShowStepOperation(NULL);
            path[0] = 0;
            set_pathtail(path);
          }
          /* because multiplyout may have set it to something else; but
             now this operation is not imitable by simpler operators, since
             it has involved at least two steps. */
       if(COMPOUND(partialproduct) && i > 1)
          { RELEASE(partialproduct);
                /* allocated by tmult on previous time through the loop */
          }
       partialproduct = temp;
       if(n > 2)
          save_and_reset(partialproduct,savenode,&partialproduct);
          /* recover wasted memory */
     }
  *next = partialproduct;
  if(equals(*next,t))
     return 1;  /* e.g.  x(1+x)^5 + 1  */
  HIGHLIGHT(*next);
  strcpy(reason, english(425)); /* multiply out */
  if(n==2 && contains(ARG(0,t),ROOT) && contains(ARG(1,t),ROOT))
     release(cancelop);  /* possibly inhibited by rationalizefraction etc. */
  return 0;
}
/*___________________________________________________________________*/
/* This operator works on a sum, and applies multiplyout to
one of the summands.  It is called only in automode or term selection mode.
Since it comes AFTER contentfactor in autosimp, it will only
get terms which have been content-factored already */

int expand(term t, term arg, term *next, char *reason)
{ int i,j,err;
  int k;
  int binomial_failure = 0;
  term temp,summand,u,w,power;
  unsigned short f,n = ARITY(t);
  unsigned short tail[7];
  void  *savenode;
  int signflag = 0;
  if(FUNCTOR(t) != '+')
     return 1;  /* inapplicable */
  savenode = heapmax();
  for(i=0;i<n;i++)
     { summand = (FUNCTOR(ARG(i,t)) == '-' ? ARG(0,ARG(i,t)) : ARG(i,t));
       f = FUNCTOR(summand);
       if(f != '*' && f != '^')
          continue;
       if(f == '*' && (k = power_of_sum(summand,&power)) != 0 && INTEGERP(power))
          { /* example, summand is x^2(x+1)^2; only expand the (x+1)^2 */
            if(equals(power,two))
               err = squareofsum(ARG((unsigned short) (abs(k)-1),summand),arg,&u,reason);
            else
               err = binomialtheorem(ARG((unsigned short) (abs(k)-1),summand),arg,&u,reason);
            if(!err)
               { HIGHLIGHT(u);
                 temp = make_term('*', ARITY(summand));
                 for(j=0;j<ARITY(summand);j++)
                    ARGREP(temp,j,j == abs(k)-1 ? u  : ARG(j,summand));
               }
            else
               { binomial_failure = 1;
                 err = multiplyout(summand,arg,&temp,reason);
                 if(!err)
                    HIGHLIGHT(temp);
               }
          }
       else
          { if(status(multiplyoutandsimp) >= KNOWN)
               err = multiplyoutandsimp(summand,arg,&temp,reason);
            else
               err = multiplyout(summand,arg,&temp,reason);
            if(!err)
               HIGHLIGHT(temp);
          }
       if(err)
          reset_heap(savenode);
       else
          { save_and_reset(temp,savenode,&temp);
            break;
          }
     }
  if(i<n) /* a term was found that multiplied out successfully */
     { if(FUNCTOR(ARG(i,t)) == '-' &&
          FUNCTOR(ARG(0,ARG(i,t))) == '*' &&
          !two_sums(ARG(0,ARG(i,t)))
         )
          {   /* subst(strongnegate(temp),ARG(i,t),t,next)
                 gets the result but loses the yellow color, so:  */
            if(COLOR(temp))
               { temp = strongnegate(temp);
                 HIGHLIGHT(temp);
                 subst(temp,ARG(i,t),t,next);
               }
            else  /* the color is in the subterms of temp anyway */
               subst(strongnegate(temp),ARG(i,t),t,next);
            /* example:  expanding  u -3(x+y) => u-3x-3y, not u-(3x+3y)  */
            tail[0] = '+';
            tail[1] = (unsigned short)(i+1);
            tail[2] = 0;  /* include the negation in the ShowStep selection */
            set_pathtail(tail);
            SetShowStepOperation(distriblaw);
            return 0;
          }
       else if(FUNCTOR(ARG(i,t)) == '-')
          { subst(tnegate(temp),ARG(i,t),t,next);
            /* not strongnegate as it's too hard for ShowStep to follow */
            signflag = 1;
          }
       else
          subst(temp,ARG(i,t),t,next);
       tail[0] = '+';
       tail[1] = (unsigned short)(i+1);
       if(signflag)
           { tail[2] = '-';
             tail[3] = 1;
             tail[4] = 0;
           }
       else
          tail[2] = 0;
       set_pathtail(tail);
       u = signflag ? ARG(0,ARG(i,t)) : ARG(i,t);
       if(FUNCTOR(u) == '^' && equals(ARG(1,u),two) && FUNCTOR(ARG(0,u)) == '+')
          { /* is it sumofsquares or sumofdif? */
            w = ARG(0,u);
            if(ARITY(w) == 2 &&
                (
                  (NEGATIVE(ARG(0,w)) && !NEGATIVE(ARG(1,w))) ||
                  (NEGATIVE(ARG(1,w)) && !NEGATIVE(ARG(0,w)))
                )
              )
               SetShowStepOperation(squareofdif);
            else
               SetShowStepOperation(squareofsum);
          }
       else if(FUNCTOR(u) == '^' && equals(ARG(1,u),three) && FUNCTOR(ARG(0,u)) == '+')
           SetShowStepOperation(NEGATIVE(ARG(1,ARG(0,u))) ? cubeofdif : cubeofsum);
       else if(FUNCTOR(u) == '*')
          { k = power_of_sum(u,&power);
            if(k && !binomial_failure)
               { int offset = 2*signflag;
                 tail[2+offset] = '*';
                 tail[3+offset] = (unsigned short) abs(k);
                 tail[4+offset] = 0;
                 set_pathtail(tail);
                 if(equals(power,two))
                    SetShowStepOperation(k > 0 ? squareofsum : squareofdif);
                 else if(GetShowStepOperation() == NULL)
                    SetShowStepOperation(binomialtheorem);
               }
            else if(!two_sums(u))
               SetShowStepOperation(distriblaw);
            else if(GetShowStepOperation() == NULL)
               SetShowStepOperation(status(multiplyoutandsimp) >= KNOWN ? multiplyoutandsimp : multiplyout);
          }
       else if(GetShowStepOperation() == NULL)
          SetShowStepOperation(status(multiplyoutandsimp) >= KNOWN ? multiplyoutandsimp : multiplyout);
       return 0;
     }
  return 1;  /* nothing to multiply out */
}
/*___________________________________________________________________*/
int expandnumerator(term t, term arg, term *next, char *reason)
{ int err,i,flag;
  term num;
  unsigned short f;
  unsigned short tail[5];
  if(FUNCTOR(t) != '/')
     return 1;  /* applies only to a fraction */
  *next = make_term('/',2);
  num = ARG(0,t);
  f = FUNCTOR(num);
  if(f == '+')
     { err  = expand(num,arg,ARGPTR(*next),reason);
       if(err)
          { RELEASE(*next);
            return 1;
          }
       /* expand has already set the 'tail', but now we have to
          make it longer. */
       pathncopy(tail,5,get_pathtail());
       i = tail[1];  /* which summand was multiplied out, numbered from 1 */
       tail[0] = '/';
       tail[1] = 1;  /* numerator */
       tail[2] = '+';
       tail[3] = (unsigned short) i;
       tail[4] = 0;
       set_pathtail(tail);
       /* expand already called ShowStepOperation */
       ARGREP(*next,1,ARG(1,t));
       return 0;
     }
  if(f == '*' || f == '^')
     { if(status(multiplyoutandsimp) >= KNOWN)
          { err = multiplyoutandsimp(num,arg,ARGPTR(*next),reason);
            flag = 0;
          }
       else
          { err = multiplyout(num,arg,ARGPTR(*next),reason);
            flag = 1;
          }
       if(err)
          { RELEASE(*next);
            return 1;
          }
       tail[0] = '/';
       tail[1] = 1;  /* the numerator */
       tail[2] = 0;  /* terminator    */
       set_pathtail(tail);
       if(FUNCTOR(num) == '^' && equals(ARG(1,num),two) && FUNCTOR(ARG(0,num)) == '+')
          SetShowStepOperation(squareofsum);
       else if(FUNCTOR(num) == '^' && equals(ARG(1,num),three) && FUNCTOR(ARG(0,num)) == '+')
          SetShowStepOperation(NEGATIVE(ARG(1,ARG(0,num))) ? cubeofdif : cubeofsum);
       else if(FUNCTOR(num) == '*' && !two_sums(num))
          SetShowStepOperation(distriblaw);
       else
          SetShowStepOperation(flag ? multiplyout : multiplyoutandsimp);
       ARGREP(*next,1,ARG(1,t));
       return 0;
     }
  RELEASE(*next);
  return 1;  /* failure */
}
/*___________________________________________________________________*/
int expanddenominator(term t, term arg, term *next, char *reason)
{ int err,i,flag;
  term den;
  unsigned short f;
  unsigned short tail[5];
  if(FUNCTOR(t) != '/')
     return 1;  /* applies only to a fraction */
  *next = make_term('/',2);
  den = ARG(1,t);
  f = FUNCTOR(den);
  if(f == '+')
     { err  = expand(den,arg,ARGPTR(*next)+1,reason);
       if(err)
          { RELEASE(*next);
            return 1;
          }
       /* expand has already set the 'tail', but now we have to
          make it longer. */
       pathncopy(tail,5,get_pathtail());
       i = tail[1];  /* which summand was multiplied out, numbered from 1 */
       tail[0] = '/';
       tail[1] = 2;  /* denominator */
       tail[2] = '+';
       tail[3] = (unsigned short) i;
       tail[4] = 0;
       set_pathtail(tail);
       ARGREP(*next,0,ARG(0,t));
       return 0;
     }
  if(f == '*' || f == '^')
     { if(status(multiplyoutandsimp) >= KNOWN)
          { err = multiplyoutandsimp(den,arg,ARGPTR(*next)+1,reason);
            flag = 0;
          }
       else
          { err = multiplyout(den,arg,ARGPTR(*next)+1,reason);
            flag = 1;
          }
       if(err)
          { RELEASE(*next);
            return 1;
          }
       tail[0] = '/';
       tail[1] = 2;  /* the numerator */
       tail[2] = 0;  /* terminator    */
       set_pathtail(tail);
       if(FUNCTOR(den) == '^' && equals(ARG(1,den),two) && FUNCTOR(ARG(0,den)) == '+')
          SetShowStepOperation(squareofsum);
       else if(FUNCTOR(den) == '^' && equals(ARG(1,den),three) && FUNCTOR(ARG(0,den)) == '+')
          SetShowStepOperation(NEGATIVE(ARG(1,ARG(0,den))) ? cubeofdif : cubeofsum);

       else if(FUNCTOR(den) == '*' && !two_sums(den))
          SetShowStepOperation(distriblaw);
       else
          SetShowStepOperation(flag ? multiplyout : multiplyoutandsimp);
       ARGREP(*next,0,ARG(0,t));
       return 0;
     }
  RELEASE(*next);
  return 1;  /* failure */
}
/*_____________________________________________________________*/
int multiplyoutandsimp(term t, term arg, term *next, char *reason)
/* called from 'simplify' menu */
{ term temp;
  int err;
  if(FUNCTOR(t) == '=')
     { term temp1, temp2;
       int err1, err2;
       err1 = multiplyoutandsimp(ARG(0,t),arg,&temp1,reason);
       if(!err1)
          { *next = equation(temp1,ARG(1,t));
             SetShowStepArg(ARG(0,t));
             return 0;
          }
       err2 = multiplyoutandsimp(ARG(1,t),arg,&temp2,reason);
       if(!err2)
          { *next = equation(ARG(0,t),temp2);
             SetShowStepArg(ARG(1,t));
             return 0;
          }
       return 1;
     }
  if(FUNCTOR(t) == '+')
     err = expand(t,arg,&temp,reason);
  else
     err = multiplyout(t,arg,&temp,reason);
  if(err)
     return 1;
  polyval1(temp,next);
  HIGHLIGHT(*next);
  release(cancelop);
  release(polyvalop);  /* in case inhibited by rationalizefraction etc. */
  if((void  *)GetShowStepOperation() == (void  *) multiplyout)
     SetShowStepOperation(multiplyoutandsimp);
  return 0;
}
/*_____________________________________________________________*/
static int mifc_aux(term a, term b)
/* auxiliary to the next function */
/* b is a sum; is there a fraction among the                                                                summands of b whose
product with a will reduce by cancellation?  If so return 1,
else return 0. */

{ unsigned short n;
  int i;
  term temp,summand;
  assert(FUNCTOR(b) == '+');
  n = ARITY(b);
  for(i=0;i<n;i++)
    { summand = (FUNCTOR(ARG(i,b))=='-' ? ARG(0,ARG(i,b)) : ARG(i,b));
      if(FUNCTOR(summand)=='/' && FUNCTOR(a)=='/')
         { naive_gcd(ARG(0,a),ARG(1,summand),&temp);
           if(!ONE(temp))
              goto out;
           naive_gcd(ARG(1,a),ARG(0,summand),&temp);
           if(!ONE(temp))
              goto out;
           destroy_term(temp);  /* naive_gcd makes it in fresh space */
           continue;  /* failure with this arg */
         }
      if(FUNCTOR(summand)=='/')
         { naive_gcd(a,ARG(1,summand),&temp);
           if(!ONE(temp))
              goto out;
           destroy_term(temp);
           continue;
         }
    }
  return 0;
  out:
    destroy_term(temp);
    return 1;
}
/*_____________________________________________________________*/
int multiplyifcancels(term t, term arg, term *next, char *reason)
/* called only from auto mode; multiply out if there will
be a multiplicative cancellation afterwards to prevent looping with
contentfactor;  it only multiplies out  a(b+c) if a is not a sum and
not a fraction, otherwise it still can loop,  for example on
(1/2) (2a/3 - 6x/a  + 3)
 */

{ int err,i,j,k;
  unsigned short n;
  term temp, argi, argj;
  if(FUNCTOR(t) != '*')
     return 1;
  n = ARITY(t);
  for(i=0;i<n;i++)
    { argi = ARG(i,t);
      if(FUNCTOR(argi)=='-')
         argi = ARG(0,argi);
      if(FUNCTOR(argi)=='+')
         continue;
      if(FUNCTOR(argi) == '/')
         continue;
      for(j=0;j<n;j++)
        { argj = ARG(j,t);
          if(FUNCTOR(argj)=='-')
             argj = ARG(0,argj);
          if(j!=i && FUNCTOR(argj)=='+' && mifc_aux(argi,argj))
              break;
        }
      if(j<n)
         break;
    }
  if(i==n)  /* can't find a pair that works */
     return 1;  /* failure */
  if(get_factorflag() == 0 && get_polyvalfactorflag() == 0)
     {  /* Example:  3x(a/x + 1) => 3x(a/x) + 3x, not 3(x(a/x) + 1) as
           it otherwise will do */
       err = distriblaw(t,arg,next,reason);
       if(!err)
          return 0;
     }
  if(i>j)
     { k=i;i=j;j=k;}  /* swap i and j */
  /* so now i<j */
  tmult(ARG(i,t),ARG(j,t),&temp);
  HIGHLIGHT(temp);
  if(n==2)
     *next = temp;
  else
     { *next = make_term('*',(unsigned short)(n-1));
       for(k=0;k<i;k++)
          ARGREP(*next,k,ARG(k,t));
       ARGREP(*next,i,temp);
       for(k=i+1;k<j;k++)
          ARGREP(*next,k,ARG(k,t));
       for(k=j;k<n-1;k++)
          ARGREP(*next,k,ARG(k+1,t));
     }
  strcpy(reason,"$a(b\\pm c)=ab\\pm ac$");
  return equals(t,*next);  /* just defensive programming */
}
/*____________________________________________________________________*/
int mvpolymult(term a, term b, term *ans)
/* multiply and collect.  Nonzero return is an error or failure. */
/* ans->args is allocated fresh */
{ aflag arithflag = get_arithflag();
  unsigned short f = FUNCTOR(a);
  unsigned short g = FUNCTOR(b);
  unsigned short n = ARITY(a);
  unsigned short m = ARITY(b);
  term temp;
  if(n > 64 || m > 64)
     return 1;  /* too many args */
  if(f== '+' || g == '+')
     { distribute(a,b,&temp);  /* allocates temp.args */
       if(arithflag.comdenom == 0 && (contains(b,'/') || contains(a,'/')))
            /* slow down: don't take 24(3/4+3/8) => 27 all in one step;
               at this point temp will be 18 + 9 which is much more
               comprehensible output */
          *ans = temp;
       else
          collect(temp,ans);
     }
  else monomult(a,b,ans);  /* allocates ans->args */
  return 0;
}
/*________________________________________________________________________*/
static int distribute(term a, term b, term *ans)
{ unsigned short n = ARITY(a);
  unsigned short m = ARITY(b);
  unsigned short f = FUNCTOR(a);
  unsigned short g = FUNCTOR(b);
  int i,j;
  if(f=='+' && g=='+')
     { if(n > 64 || m > 64)
          { errbuf(0,english (1508));
            /* MathXpert will not multiply out terms with more than 64 summands */
            return 1;
          }
       *ans = make_term('+',(unsigned short)(n*m));
       for(i=0;i<n;i++)
          { for(j=0;j<m;j++)
               monomult(ARG(i,a),ARG(j,b),ARGPTR(*ans) + m*i + j);
          }
       return 0;
     }
  if(f == '+' )  /* and g != '+' */
     { *ans = make_term('+',n);
        for(i=0;i<n;i++)
           monomult(ARG(i,a),b,ARGPTR(*ans)+i);
        return 0;
     }
  if(g == '+' )  /* and f != '+' */
     { *ans = make_term('+',m);
        for(i=0;i<m;i++)
           monomult(a,ARG(i,b),ARGPTR(*ans)+i);
        return 0;
     }
  monomult(a,b,ans);
  return 0;
}
/*________________________________________________________________________*/
static void monomult(term a, term b, term *ans)
/* multiply monomials, collecting powers, ordering the factors,
and performing arithmetic on the numerical part */
/* ans->args is allocated unless ans is zero or one */
{ term temp,temp2;
  mt(a,b,&temp);  /* see maketerm.c */
  if(NEGATIVE(temp) && FUNCTOR(ARG(0,temp))== '*')
     { simpprod(ARG(0,temp),&temp2);
       if(FUNCTOR(temp2)=='-')
          { /* can't just return ARG(0,temp2)  because
               of the specification that says ans->args is
               allocated here */
            copy(ARG(0,temp2),ans);
          }
       else
          tneg(temp2,ans);
     }
  else if(FUNCTOR(temp) == '*')
     simpprod(temp,ans);  /* ans might be temp */
  else
     *ans = temp;
}

/*________________________________________________________________________*/
int expandable(term t)
/* test t for the applicability of multiplyout */
/* accepts squares, cubes or powers up to the power specified by
get_binomial_exponent(), or products, at least one of whose
factors is a nonconstant sum or square or cube of a sum, or a square or sum
containing a SQRT.  (Or if t itself is constant, drop the "nonconstant"
requirement, so that 2(1-1/e) - 3/e will get simplified because its first
summand is expandable.)

   Return 1 for acceptable, 0 for not acceptable */
/* But:  it does NOT expand a product like  u((a+b)(c+d) + e)v;
   instead we should expand the (a+b)(c+d).  So we reject t if it has
   a factor which is a nonconstant sum, if the nonconstant sum has a
   summand which is an expandable product. */

/* Also fails on a product, one of whose factors is an integral
or derivative, and the others don't contain INTEGRAL or DIFF (respectively) */


{ unsigned short f = FUNCTOR(t);
  unsigned short g;
  unsigned short n = ARITY(t);
  term u;
  int i,k;
  int flag=0;
  unsigned short calcfunctor = 0;
  if(ATOMIC(t))
     return 0;
  for(k=0;k<n;k++)
     { if( FUNCTOR(ARG(k,t))==INTEGRAL)
          { calcfunctor = INTEGRAL;
            break;
          }
       if(FUNCTOR(ARG(k,t)) == DIFF)
          calcfunctor = DIFF;  /* but don't break, maybe an integral comes later */
     }
  if (f == '^' && FUNCTOR(ARG(0,t)) == '+')
     { term power = ARG(1,t);
       if(!ISINTEGER(power))
          return 1;
       if(INTDATA(power) <= get_binomial_exponent())
          return 1;
     }
  if (f != '*')
     return 0;
  for(i=0;i<n;i++)
     { u = ARG(i,t);
       g = FUNCTOR(u);
       if(g == '^' && (equals(ARG(1,u),two) || equals(ARG(1,u),three)))
          { u = ARG(0,u);
            g = FUNCTOR(u);
          }
       if(g != '+')
          continue;
       if(    (!calcfunctor &&
               ( !constant(u)
                 || constant(t)
                 || contains_sqrt(u)
               )
              )
           || (calcfunctor && contains(u,calcfunctor))
         )
         { for(k=0;k<ARITY(u);k++)
              { if(expandable(ARG(k,u)))
                   return 0;  /* should expand ARG(k,u) first */
              }
           flag = 1;  /* t can be expanded by distributing other factors over u */
         }
     }
  return flag;
}
/*____________________________________________________________________*/
int collectterms(term t, term arg, term *next, char *reason)
/* an operator; collects all occurrences of one kind of term */
/* Except when currenttopic == _verify_algebraic_identity, it will apply
regroupterms automatically first */

{  int err,cancel_occurred;
   term temp;
   if(FUNCTOR(t) != '+')
      return 1;
   err = summands(t,&temp);
   if(!err)
      return collectterms(temp,arg,next,reason);
   err = collect1(0,t,next,&cancel_occurred);
       /* collect1 colors the collected terms */
   if(err)
      return 1;
   if(cancel_occurred == 2)
      return additivecancel(t,arg,next,reason);
        /* get the right reason string mentioning the cancelled term */
        /* don't be picky and refuse to cancel terms when asked to collect */
   if(cancel_occurred == 1)
      strcpy(reason, english(437)); /* collect and cancel */
   else
      strcpy(reason, english(438));  /* collect like terms */
   return 0;
}
/*__________________________________________________________________*/
int additivecommute(term t, term arg, term *next, char *reason)
/* a+b = b+a */
/* Useful only in term selection mode */
{ if(FUNCTOR(t) != '+' || ARITY(t) != 2)
     return 1;
  *next = sum(ARG(1,t),ARG(0,t));
  HIGHLIGHT(*next);
  strcpy(reason, "$a+b = b+a$");
  return 0;
}
/*__________________________________________________________________*/
int multcommute(term t, term arg, term *next, char *reason)
/* ab = ba */
/* Useful only in term selection mode */
{ if(FUNCTOR(t) != '*' || ARITY(t) != 2)
     return 1;
  *next = product(ARG(1,t),ARG(0,t));
  HIGHLIGHT(*next);
  strcpy(reason, "$ab = ba$");
  return 0;
}

/* ________________________________________________________________________*/
static int two_sums(term t)
/* assumes t is a product.  Return 1 if two or more of the factors
are sums, or if one factor is an integer power of a sum.
Return 0 otherwise. */

{ unsigned short n = ARITY(t);
  int i,count=0;
  term u;
  if(FUNCTOR(t) != '*')
     assert(0);
  for(i=0;i<n;i++)
     { u = ARG(i,t);
       if(FUNCTOR(u) == '+')
          ++count;
       if(count > 1)
          return 1;
       if(FUNCTOR(u) == '^' && FUNCTOR(ARG(0,u)) == '+' && isinteger(ARG(1,u)))
          return 1;
     }
  return 0;
}

/*____________________________________________________________________*/
static int power_of_sum(term u, term *power)
/* u is a product.  If one of its args is an integer power of a sum,
return k or -k, where k is the arg number of the power of a sum,
starting from 1, not from 0.  Return -k if the sum has the form a-b,
k otherwise.  Return 0 if no arg is a power of a sum.
  If nonzero is returned, *power gets the exponent of the sum.
*/
{ int i;
  term v,w;
  unsigned short n = ARITY(u);
  if(FUNCTOR(u) != '*')
     return 0;
  for(i=0;i<n;i++)
     { v = ARG(i,u);
       if(FUNCTOR(v) == '^' && FUNCTOR(ARG(0,v)) == '+' && INTEGERP(ARG(1,v)))
          { w = ARG(0,v);
            *power = ARG(1,v);
            if(ARITY(w) == 2 &&
               (
                 (NEGATIVE(ARG(1,w)) && !NEGATIVE(ARG(0,w))) ||
                 (NEGATIVE(ARG(0,w)) && !NEGATIVE(ARG(1,w)))
               )
              )
               return -i-1;
            return i+1;
          }
     }
  return 0;
}
/*___________________________________________________________________*/
static int get_binomial_exponent(void)
/* the maximum exponent that will be expanded by the binomial theorem */
{ int problemtype = get_problemtype();
  int currenttopic = get_currenttopic();
  if( problemtype == LIMITS && CALC1_TOPIC(currenttopic))
     return 10;  /* status(lhopital) == UNKNOWN), but we don't want
                    to refer to lhopital which lives in trigcalc.dll */
  if(problemtype == DIFFERENTIATE_FROM_DEFN)
     return 10;
  return 4;
}
/*__________________________________________________________________*/
term square(term a)
/*  return a^2, but if a = sqrt c, and c>=0,  return c, else return |c|.
    Distribute the exponent over a product and
    evaluate numerical squares resulting.
    Example:  square(2xy) = 4x^2y^2
    Use the laws (a^(1/2))^2 = a and  (a^(n/2))^2 = a^n , provided
    powertopower is KNOWN.
*/
{ int i,err;
  unsigned short n;
  term c,ans,u;
  if(ZERO(a))
     return zero;
  if(ONE(a))
     return one;
  if(ISINTEGER(a))
     { if(INTDATA(a) < 50)
          { err = value(product(a,a),&ans);
            assert(!err);  /* because a is not zero or one */
            return ans;
          }
     }
  if(NEGATIVE(a) && status(minustopower) == WELLKNOWN)
     return square(ARG(0,a));
  if(FUNCTOR(a) == '^' && FRACTION(ARG(1,a)) &&
     equals(ARG(1,ARG(1,a)),two) &&
     status(powertopower) >= KNOWN
    )
     { if(ONE(ARG(0,ARG(1,a))))
          return ARG(0,a);
       else
          return make_power(ARG(0,a),ARG(0,ARG(1,a)));
     }
  if(FRACTION(a) && status(quotienttopower) == WELLKNOWN)
     return make_fraction(square(ARG(0,a)),square(ARG(1,a)));
  if(ATOMIC(a))
     return make_power(a,two);
  if(FUNCTOR(a) == SQRT)
     { c = ARG(0,a);
       if(!get_polyvaldomainflag())
          return c;
       err = infer(le(zero,c));
       if(!err)
          return c;
       return abs1(c);
     }
  if(FUNCTOR(a) == ROOT && status(powerofroot) == WELLKNOWN)
     { term index = ARG(0,a);
       if(equals(index,four))
          return sqrt1(ARG(1,a));
       if(ISINTEGER(index) && EVEN(index))  /* it can't be a bignum anyway */
          return make_root(make_int(INTDATA(index)/2),ARG(1,a));
     }
  if(FUNCTOR(a) == '*' && status(producttopower) == WELLKNOWN)
     { n = ARITY(a);
       ans = make_term('*',n);
       for(i=0;i<n;i++)
          { u = ARG(i,a);
            if(numerical(u))
               { err = value(product(u,u),ARGPTR(ans)+i);
                 if(err)  /* for example if u = sqrt 3  */
                    ARGREP(ans,i,square(u));
               }
            else
               ARGREP(ans,i,square(u));
          }
       return ans;
     }
  return make_power(a,two);
 }
/*___________________________________________________________*/
int multdef(term t, term arg, term *next, char *reason)
/* na = a + ... + a */
{ long n;
  term a;
  int i;
  if(FUNCTOR(t) != '*')
     return 1;
  if(ARITY(t) != 2)
     return 1;
  if(!ISINTEGER(ARG(0,t)))
     return 1;
  n = INTDATA(ARG(0,t));
  if(n > 100)  /* this is enforced in selectops */
     return 1;
  a = ARG(1,t);
  *next = make_term('+',(unsigned short) n);
  for(i=0;i<n;i++)
     copy(a,ARGPTR(*next) +i); /* don't create a DAG */
  HIGHLIGHT(*next);
  strcpy(reason,"$na = a + ... + a$");
  return 0;
}

/*____________________________________________________________*/
int minusintoproduct1(term t, term arg, term *next, char *reason)
/*  -ab = a(-b)  */
{ term u,v;
  unsigned n,i;
  if(FUNCTOR(t) != '-' || FUNCTOR(ARG(0,t)) != '*')
     return 1;
  u = ARG(0,t);
  n = ARITY(u);
  if(n==2)
     { *next = make_term('*',2);
       ARGREP(*next,0, ARG(0,u));
       ARGREP(*next,1,tnegate(ARG(1,u)));
       /* can't use product as it pushes the minus back to the front */
     }
  else
     { v = make_term('*',n-1);
       for(i=0;i<n-1;i++)
          ARGREP(v,i,ARG(i+1,u));
       *next = make_term('*',2);
       ARGREP(*next,0,ARG(0,u));
       ARGREP(*next,1,tnegate(v));
       HIGHLIGHT(*next);
     }
  strcpy(reason,"$-ab = a(-b)$");
  return 0;
}

/*____________________________________________________________*/
int minusintoproduct2(term t, term arg, term *next, char *reason)
/* -abc = ab(-c) */
{ term u,v;
  unsigned n,i;
  if(FUNCTOR(t) != '-' || FUNCTOR(ARG(0,t)) != '*' || ARITY(ARG(0,t)) < 3)
     return 1;
  u = ARG(0,t);
  n = ARITY(u);
  if(n==3)
     { *next = make_term('*',3);
       ARGREP(*next,0,ARG(0,u));
       ARGREP(*next,1,ARG(1,u));
       ARGREP(*next,2,tnegate(ARG(2,u)));
     }
  else
     { v = make_term('*',n-2);
       for(i=0;i<n-2;i++)
          ARGREP(v,i,ARG(i+2,u));
       *next = make_term('*',3);
       ARGREP(*next,0,ARG(0,u));
       ARGREP(*next,1,ARG(1,u));
       ARGREP(*next,2,tnegate(v));
       HIGHLIGHT(*next);
     }
  strcpy(reason,"$-abc = ab(-c)$");
  return 0;
}

/*____________________________________________________________*/
int minusintoproduct3(term t, term arg, term *next, char *reason)
/*  a(-b)c = ab(-c) */
{ unsigned n,i,j;
  if(FUNCTOR(t) != '*')
     return 1;
  n = ARITY(t);
  if(n < 3)
     return 1;
  for(i=0;i<n;i++)
     { if(FUNCTOR(ARG(i,t)) == '-')
           break;
     }
  if(i==n || i==n-1)
     return 1;
  *next = make_term('*',n);
  for(j=0;j<n;j++)
     ARGREP(*next,j, i==j ? ARG(0,ARG(i,t)) : j==i+1 ? tnegate(ARG(j,t)) : ARG(j,t));
  strcpy(reason,"$a(-b)c = ab(-c)$");
  return 0;
}

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