Sindbad~EG File Manager

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

/* operators for ADVANCED FACTORING menu */
/*
M. Beeson, for MathXpert
Original date 4.4.91
3.25.99 last modified
1.10.00 added differenceofnth2
1.10.00 modified differenceofnthpowers, sumofnthpowers to call sortargs on the terms
1.15.00 modified factorbygrouping for sumofsquares
5.5.13  added include advfact.h
10.23.23 \\sqrt instead of OEM character
5.30.24  enclosed a+b = b+a in $
*/

#include <string.h>
#include <assert.h>
#include "globals.h"
#include "ops.h"
#include "polynoms.h"
#include "order.h"
#include "cancel.h"
#include "prover.h"   /* domain, infer, check */
#include "algaux.h"
#include "factor.h"
#include "symbols.h"
#include "errbuf.h"
#include "pvalaux.h"  /* content_factor */
#include "mathmode.h"   /* automode, set_mathmode */
#include "autosimp.h"  /* SetShowStepOperation, set_pathtail */
#include "advfact.h"  /* nthroot_aux */

static int xgroup(term t, term x, term *ans);
static int quadratic_group(term t, term x,term y, term *ans);
static int finish_factorbygrouping(term x, term t, term arg, term *next, char *reason);
/*____________________________________________________________________*/
int nthroot_aux(term u, term m, term *ans)
/*  *ans is returned as the mth root of u as required
for differenceofnthpowers.  m must be a specific integer (unless u = c^(am) )
If u is a number, ringflag will be consulted as
described in documentation of differenceofsquares.
If u is not a number, product, power, or a quotient, *ans = make_root(m,u)
will be returned.
Zero return value indicates success. */
{ int i,err;
  long mm;
  unsigned short n;
  if(ONE(u))   /* common special case, no need to go to overlaid file arith.c */
     { *ans = one;
       return 0;
     }
  if(!ISINTEGER(m))   /* can work only if u = c^(am) yielding c^a, or if u is constant */
     { term a,b,c,d;
       if(FUNCTOR(u)== '^')
          { c = ARG(0,u);
            b = ARG(1,u);
            err = cancel(b,m,&d,&a);
            if(err)
               return 1;
            *ans = make_power(c,a);
            return 0;
          }
       if(constant(u) && (get_ringflag() & ALGINT) ) /* \sqrt const allowed in factors */
          { err = infer(lessthan(zero,m));
            if(err)
               return 1;
            *ans = make_root(m,u);
            return 0;
          }
     }
  mm = INTDATA(m);  /* from now on, m is an integer */
  if(FUNCTOR(u) == ROOT)
     { term index,temp;
       if(get_mathmode() == AUTOMODE)
          return 1;
       temp = product(m,ARG(0,u));
       err = value(temp,&index);
       if(err != 0 && err != 2)
          index = temp;
       else
          RELEASE(temp);
       *ans = make_root(index,ARG(1,u));
       return 0;
     }
  if(FUNCTOR(u) == SQRT)
     { term index,temp;
       if(get_mathmode() == AUTOMODE)
          return 1;
       temp = product(m,two);
       err = value(temp,&index);
       if(err != 0 && err != 2)
          index = temp;
       else
          RELEASE(temp);
       *ans = make_root(index,ARG(0,u));
       return 0;
     }
  if(FUNCTOR(u) == '/')
     { term num,den;
       err = nthroot_aux(ARG(0,u),m,&num);
       if(err)
          return 1;
       err = nthroot_aux(ARG(1,u),m,&den);
       if(err)
          return 1;
       *ans = ONE(den) ? num : make_fraction(num,den);
       return 0;
     }
  if(FUNCTOR(u) == '^')
     { term a,n,trash,expa;
       a = ARG(0,u);  /* u = a^n */
       n = ARG(1,u);   /* u = a^n */
       if(equals(n,m))
          { *ans = a;
            return 0;
          }
       err = cancel(n,m,&trash,&expa);
       if(err)
          return 1;
       if(FUNCTOR(expa) == '/')
          return 1;  /* example:  u = x^2, m = 4, don't return x^(1/2)  */
       *ans = make_power(a,expa);
       return 0;
     }
  if(OBJECT(u) || (FUNCTOR(u) == '-' && OBJECT(ARG(0,u))))
      { err = value(make_power(u,make_fraction(one,m)),ans);
        if(err == 0)
           return 0;  /* success, an exact root */
        if(get_mathmode() != AUTOMODE || (get_ringflag() & ALGINT))  /* algebraic factors desired, see globals.h */
           { if( !(mm &1) && !get_complex())  /* m is even */
                err = infer(nonnegative(u));
             else   /* m odd */
                err = 0;
             if(err == 0)
                { if(constant(u))
                     { *ans = make_root(m,u);
                       return 0;
                      }
                  else
                     return 1;
                }
             else if( !(mm &1) && get_ringflag() & GAUSSINT)  /* even m and complex factors ok */
                { if(FUNCTOR(u)=='-')
                     { term temp;
                       term qq;  /* m-th root of -1, e.g. if m-4, qq = (1+i)/\sqrt 2 */
                       err = nthroot_aux(ARG(0,u),m,&temp);
                       if(err==0)
                          { if(mm==4)
                               qq = make_fraction(sum(one,complexi),make_sqrt(two));
                            else
                               qq = sum(cos1(make_fraction(pi_term,m)),product(complexi,sin1(make_fraction(pi_term,m))));
                            *ans = product(qq,temp);
                            return 0;
                          }
                       else
                          return 1;
                      }
                }
             return 1;  /* complex factors not desired */
           }
        return 1;  /* algebraic factors not desired */
      }
  if(FUNCTOR(u) != '*')
    { if(constant(u))
         { *ans = make_root(m,u);
           if( (mm & 1) || get_complex())
              return 0;
           err = check1(domain(*ans));  /* even m and !complex */
           if(err)
              return 1;
           return 0;
         }
      return 1;
    }
  /* now FUNCTOR(u) == '*'  */
  n = ARITY(u);
  *ans = make_term('*',n);
  for(i=0;i<n;i++)
     { err = nthroot_aux(ARG(i,u),m,ARGPTR(*ans)+i);
       if(err)
          return 1;
     }
  return 0;
}

/*_______________________________________________________________________*/
int differenceofcubes(term t, term arg, term *next, char *reason)
/* x^3 - y^3 = (x-y) (x^2 + xy + y^2) */
/* must work regardless of order of terms */
/* also catches  a^3-b^n  for integers n divisible by 3, and a^n - b^3, etc. */
/* when doing  a^(3n) - b^3 in auto mode, if either this operator or powertopower
   is not wellknown, it just uses powertopower in reverse to get (a^n)^3 - b^3 */
/* Must also work on x^3-8 to get (x-2)(x^2+2x+4), and so on.
   What about  x^3-3?   if ringflag == INTEGER it will fail, but if
   ringflag == REAL or COMPLEX, it will get factor it using sqrt 3.
   Unlike differenceofsquares, it doesn't do anything different when
   ringflag = GAUSSIAN or COMPLEX; that is, it still takes the real
   cube root of a negative number.
*/

{ term u,v,newu,newv,a,b,temp,first,middle,last,w;
  int err,switchflag =0;
  unsigned short path[9];
  if(FUNCTOR(t) != '+')
     return 1;
  if(ARITY(t) != 2)
     return arity_aux2(t,arg,next,reason,differenceofcubes);
  u = ARG(0,t);
  v = ARG(1,t);
  if(FUNCTOR(u) != '-' && FUNCTOR(v) != '-')
     return 1;
  if(FUNCTOR(u) == '-' && FUNCTOR(v)== '-')
     return 1;
  if(FUNCTOR(v) == '-')
     v = ARG(0,v);
  else
     { temp = ARG(0,u);
       u = v;
       v = temp;
       switchflag = 1;
     }
      /* Now t = u - v  */
  err = nthroot_aux(v,three,&b);
  if(err)
     return 1;
  err = nthroot_aux(u,three,&a);
  if(err)
     return 1;
  if(ZERO(b) || ZERO(a))
     return 1;
     /* otherwise the SETORDERED clause at the end
        can cause an error, since the product can be just 1 */
  if(
     get_mathmode() == AUTOMODE &&
     (
        (FUNCTOR(u) == '^' && !equals(ARG(1,u),three))
      || FUNCTOR(u) == '*'
      || (INTEGERP(u) && !ONE(u))
     ) &&
     (status(powertopower) < WELLKNOWN ||
      status(differenceofsquares) < WELLKNOWN  ||
      status(producttopower) < WELLKNOWN
     )
    )
     { newu = make_power(a,three);
       PROTECT(newu);
       *next = sum(newu,tnegate(v));
       HIGHLIGHT(*next);
       path[0] = '+';
       path[1] = switchflag ? 2 : 1;
       path[2] = 0;
       /* wait with set_pathtail till after prodofpower has been called */
       if(FUNCTOR(a) == '*' &&
          FUNCTOR(u) == '*' &&
          FUNCTOR(ARG(0,u)) == '^' &&
          FUNCTOR(ARG(1,u)) == '^' &&
          !prodofpowers(u,arg,&w,reason)
         )

          { if(ARITY(u) > 2)
               pathcat(path,get_pathtail());
               /* prodofpowers can select a subrange */
            SetShowStepOperation(prodofpowers);
          }
       else
          { strcpy(reason,"a^(3n) = (a^n)^3");
            if(INTEGERP(a))
               SetShowStepOperation(writenumberascube);
            else
               SetShowStepOperation(writeascube);
          }
       set_pathtail(path);
       return 0;
     }
  if(
     get_mathmode() == AUTOMODE &&
     (
      (FUNCTOR(v) == '^' && !equals(ARG(1,v),three))
      || FUNCTOR(v) == '*'
      || (INTEGERP(v) && !ONE(v))
     ) &&
     (status(powertopower) < WELLKNOWN ||
      status(differenceofsquares) < WELLKNOWN  ||
      status(producttopower) < WELLKNOWN
     )
    )
     { newv = make_power(b,three);
       PROTECT(newv);
       *next = sum(u,tnegate(newv));
       HIGHLIGHT(*next);
       path[0] = '+';
       path[1] = switchflag ? 1 : 2;
       path[2] = '-';
       path[3] = 1;
       path[4] = 0;
       set_pathtail(path);
       if(FUNCTOR(b) == '*' &&
          FUNCTOR(v) == '*' &&
          FUNCTOR(ARG(0,v)) == '^' &&
          FUNCTOR(ARG(1,v)) == '^' &&
          !prodofpowers(v,arg,&w,reason)
         )
          SetShowStepOperation(prodofpowers);
       else
          { strcpy(reason,"a^(3n) = (a^n)^3");
            if(INTEGERP(b))
               SetShowStepOperation(writenumberascube);
            else
               SetShowStepOperation(writeascube);
          }
       return 0;
     }
  UNPROTECT(a);
  UNPROTECT(b);
  middle = product(a,b);
  if(FUNCTOR(middle)=='*')
      sortargs(middle);  /* thus get 2x for example instead of x2 */
  last = make_power(b,two);
  first = make_power(a,two);
  if(status(differenceofcubes) > LEARNING)
     { err = value(last,&temp);
       if(!err)
          { RELEASE(last);
            last = temp;
          }
       err = value(first,&temp);
       if(!err)
          { RELEASE(first);
            first = temp;
          }
      }
  temp = make_term('+',3);
  ARGREP(temp,0,first);
  ARGREP(temp,1,middle);
  ARGREP(temp,2,last);
  *next = product(sum(a,tnegate(b)),temp);
  SETORDERED(ARG(0,*next));
  SETORDERED(ARG(1,*next));  /* tell auto mode not to reorder the summands */
  if(ATOMIC(a) || ATOMIC(b))
     { SETPRIME(ARG(1,*next));
       SETPRIME(ARG(0,*next));
     }
  HIGHLIGHT(*next);
  SETORDERED(*next);   /* don't rearrange order of these factors, it looks silly */
  strcpy(reason,"a^3 - b^3 =           (a-b)(a^2+ab+b^2)");
  return 0;
}
/*_______________________________________________________________________*/
int sumofcubes(term t, term arg, term *next, char *reason)
/* a^3 + b^3 = (a+b)(a^2-ab+b^2) */
/* see comments on differenceofcubes above, which also apply here */
{ term u,v,a,b,temp,first, middle,last;
  int err;
  if(FUNCTOR(t) != '+')
     return 1;
  if(ARITY(t) != 2)
     return arity_aux2(t,arg,next,reason,sumofcubes);
  u = ARG(0,t);
  v = ARG(1,t);
      /*  t = u + v  */
  if(NEGATIVE(u) || NEGATIVE(v))
     return 1;  /* use differenceofcubes instead */
  err = nthroot_aux(v,three,&b);
  if(err)
     return 1;
  err = nthroot_aux(u,three,&a);
  if(err)
     return 1;
  if(ZERO(b) || ZERO(a))
     return 1;
  if(
     get_mathmode() == AUTOMODE &&
     (  (FUNCTOR(u) == '^' && !equals(ARG(1,u),three))
      ||(FUNCTOR(v) == '^' && !equals(ARG(1,v),three))
      || FUNCTOR(u) == '*' || FUNCTOR(v) == '*'
     ) &&
     (status(powertopower) < WELLKNOWN ||
      status(sumofcubes) < WELLKNOWN  ||
      status(producttopower) < WELLKNOWN
     )
    )
     { u = make_power(a,three);
       v = make_power(b,three);
       PROTECT(u);
       PROTECT(v);
       *next = sum(u,v);
       HIGHLIGHT(*next);
       if(FUNCTOR(u) == '*' || FUNCTOR(v) == '*')
          strcpy(reason,"a^nb^n = (ab)^n");
       else
          strcpy(reason,"a^(3n) = (a^n)^3");
       return 0;
     }
  UNPROTECT(a); /* in case protected by a previous application */
  UNPROTECT(b);
  middle = product(a,b);
  if(FUNCTOR(middle)=='*')
      sortargs(middle);  /* thus get 2x for example instead of x2 */
  last = make_power(b,two);
  first = make_power(a,two);
  if(status(sumofcubes) > LEARNING)
     { err = value(last,&temp);
       if(!err)
          { RELEASE(last);
            last = temp;
          }
       err = value(first,&temp);
       if(!err)
          { RELEASE(first);
            first = temp;
          }
     }
  temp = make_term('+',3);
  ARGREP(temp,0,first);
  ARGREP(temp,1,tnegate(middle));
  ARGREP(temp,2,last);
  *next = product(sum(a,b),temp);
  SETORDERED(ARG(0,*next));
  SETORDERED(ARG(1,*next));  /* tell auto mode not to reorder the summands */
  if(ATOMIC(a) || ATOMIC(b))
     { SETPRIME(ARG(1,*next));
       SETPRIME(ARG(0,*next));
     }
  HIGHLIGHT(*next);
  SETORDERED(*next);   /* don't rearrange order of these factors, it looks silly */
  strcpy(reason,"a^3 + b^3 =           (a+b)(a^2-ab+b^2)");
  return 0;
}
/*_______________________________________________________________________*/
static int getexp(term u, long *m)
/* if u is a power with pos. integer exponent, return that exponent in *m;
   if u is a product of such powers, return the gcd of their exponents;
   ignore numerical factors but fail if there are other symbolic factors
   which are not powers.  Zero return is success, 1 is failure.
*/
{ long nn = 0L;
  unsigned short i,n = ARITY(u);
  if(FUNCTOR(u)=='^')
     { if(! ISINTEGER(ARG(1,u)))
           return 1;
       *m = INTDATA(ARG(1,u));
       return 0;
     }
  if(FUNCTOR(u) == '*')
     { for(i=0;i<n;i++)
        { if(FUNCTOR(ARG(i,u))=='^' && ISINTEGER(ARG(1,ARG(i,u))))
            { nn = intgcd(nn,INTDATA(ARG(1,ARG(i,u))));
            }
          else if(! numerical(ARG(i,u)))
             return 1;  /* fail */
        }
      if(nn==1 || nn==0)
         return 1;
      else
         *m = nn;
      return 0;
     }
  if(numerical(u))
     { *m = 0L;
       return 0;
     }
  return 1;  /* failure -- wrong functor or non-numerical exponent */
}

/*_______________________________________________________________________*/
int differenceofnthpowers(term t, term arg, term *next, char *reason)
/* a^n-b^n = (a-b)(a^(n-1)+...+b^(n-1)) */
/* in menu mode, applies directly.  In auto mode, only applies if n is
prime.  (Otherwise, we should first factor the exponents and use a^(kj)=(a^k)^j,
and apply this law with a small j.) */
/* If n is a variable which can be 'checked' to be of type natnum and > 1,
then it will produce a term involving SUM */

{ term u,v,a,b,n,temp,w,d;
  int err,i,st;
  int flag = 0;
  int sflag = 0;  /* answer will include a SUM term */
  int mathmode = get_mathmode();
  long nn,mm;
  if(FUNCTOR(t) != '+')
     return 1;
  if(ARITY(t) != 2)
     return arity_aux2(t,arg,next,reason,differenceofnthpowers);
  u = ARG(0,t);
  v = ARG(1,t);
  if(FUNCTOR(u) != '-' && FUNCTOR(v) != '-')
     return 1;
  if(FUNCTOR(u) == '-' && FUNCTOR(v)== '-')
     return 1;
  if(FUNCTOR(v) == '-')
     v = ARG(0,v);
  else
     { temp = ARG(0,u);
       u = v;
       v = temp;
     }
      /* Now t = u - v  */
  /* Now identify the exponent */
  err = getexp(u,&nn);
  if(err)
     { /* exponent is not a (specific) integer */
        if(mathmode == AUTOMODE)
           return 1;
        if(FUNCTOR(u) != '^')
           return 1;
        a = ARG(0,u);
        n = ARG(1,u);
        err = infer(type(n,INTEGER));
        if(err)
           return 1;
        err = check1(lessthan(zero,n));
        if(err)
           return 1;
        sflag = 1;  /* exponent is a variable */
     }
  err = getexp(v,&mm);
  if(err && sflag)
     { /* 2nd exponent is not a (specific) integer and neither is first one */
        if(FUNCTOR(v) != '^')
           return 1;
        b = ARG(0,v);
        if(!equals(n,ARG(1,u)))  /* they'd better be the same */
           return 1;
        sflag = 3;  /* both exponents variable */
     }
  if(err && !sflag)
     { /* 2nd exponent is not a specific integer but first one is */
        if(nn !=0)
           return 1;  /* we can only do  things like 1 -x^n in this case */
        if(FUNCTOR(v) != '^')
           return 1;
        if(mathmode == AUTOMODE)
           return 1;
        b = ARG(0,v);
        n = ARG(1,v);
        err = infer(type(n,INTEGER));
        if(err)
           return 1;
        err = check1(lessthan(zero,n));
        if(err)
           return 1;
        sflag = 2;  /* second exponent is a variable and first one isn't */
     }
  if(!sflag)
     { if(!nn && !mm)
          return 1;    /* both u and v were numerical */
       if (nn==mm)
          n = make_int(nn);  /* the exponent to use for the rule */
       else if(nn==0)
          n = make_int(mm);
       else if(mm==0)
          n = make_int(nn);
       else
          { n = make_int(intgcd(nn,mm));   /* the exponent to use for the rule */
            flag = 1;  /* exponent is not same as in original problem */
          }
       nn = INTDATA(n);
       if(get_mathmode() == AUTOMODE && nn==2)
          return 1;
            /* differenceofsquares should do the job.  In a few examples
               differenceofsquares is blocked by ad-hoc code in pre_ops,
               and without this line, differenceofnth_powers is wrongly used. */
     }
  if(sflag)
     { term hi,index,running,q;
       if(sflag == 2)  /* only second exponent is variable */
          { err = nthroot_aux(u,n,&a);
            if(err)
               return 1;
          }
       else if(sflag == 1) /* only first exponent is variable */
          { err = nthroot_aux(v,n,&b);
            if(err)
               return 1;
          }

       if(mathmode == AUTOMODE)
          return 1;
       /* Now a,b, and n are all properly instantiated */
       temp = make_term(SUM,4);
       index = index_variable(t);
       ARGREP(temp,1,index);
       ARGREP(temp,2,zero);
       polyval(sum(n,minusone),&hi);
       ARGREP(temp,3,hi);
       polyval(sum(hi,tnegate(index)),&q);
       if(get_orderflag() == ASCENDING)
           running = product(make_power(a,index),make_power(b,q));
       else
           running = product(make_power(a,q),make_power(b,index));
       ARGREP(temp,0,running);
       q = make_term('+',2);
       ARGREP(q,0,a);
       tneg(b,ARGPTR(q)+1);  /* q = a-b */
       *next = product(q,temp);
     }
  else
     {  /* That finishes the case of a symbolic exponent.
           Now deal with numerical exponents. */

       err = nthroot_aux(v,n,&b);
       if(err)
          return 1;
       err = nthroot_aux(u,n,&a);
       if(err)
          return 1;
       if(
          get_mathmode() == AUTOMODE &&
          (  (FUNCTOR(u) == '^' && !equals(ARG(1,u),three))
           ||(FUNCTOR(v) == '^' && !equals(ARG(1,v),three))
           || FUNCTOR(u) == '*' || FUNCTOR(v) == '*'
          ) &&
          (status(powertopower) < WELLKNOWN ||
           status(differenceofnthpowers) < WELLKNOWN  ||
           status(producttopower) < WELLKNOWN
          ) &&
          (FUNCTOR(u) == '*' || FUNCTOR(v) == '*' || flag)
         )
          { PROTECT(a);
            PROTECT(b);
            *next = sum(make_power(a,n),tnegate(make_power(b,n)));
            HIGHLIGHT(*next);
            if(FUNCTOR(u) == '*' || FUNCTOR(v) == '*')
               strcpy(reason,"a^nb^n = (ab)^n");
            else   /*  if(flag)  */
               strcpy(reason,"a^(nm) = (a^n)^m");
            return 0;
           }
       if(nn > 100)
          { errbuf(0, english(351));
              /* That would create a very long expression. */
            errbuf(1, english(352));
              /* Try a problem with an exponent less than 100. */
            return 1;
          }
       UNPROTECT(a); /* in case protected by a previous application */
       UNPROTECT(b);
       st = status(differenceofnthpowers);
       temp = make_term('+',(unsigned short) nn);
       ARGREP(temp,0,make_power(a,make_int(nn-1)));
       ARGREP(temp,(unsigned short)(nn-1),make_power(b,make_int(nn-1)));
       for(i=1;i<nn-1;i++)
          { d = product(make_power(a,make_int(nn-1-i)),make_power(b,make_int(i)));
            if(st != LEARNING)
               polyval(d,&w);
            else
               w = d;
            ARGREP(temp,i,w);
          }
       *next = product(sum(a,tnegate(b)),temp);
     }  /* That finishes the case of numerical exponents.
           Now polish up the answer and return */
  SETORDERED(ARG(0,*next));
  SETORDERED(ARG(1,*next));  /* tell auto mode not to reorder the terms */
  SETORDERED(*next);
  HIGHLIGHT(*next);
  SETORDERED(*next);   /* don't rearrange order of these factors, it looks silly */
  strcpy(reason,"a^(n+1)-b^(n+1) =     (a-b)(a^n + ... + b^n");
  return 0;
}
/*_______________________________________________________________________*/
int sumofnthpowers(term t, term arg, term *next, char *reason)
/* a^n+b^n = (a+b)(a^(n-1)-a^(n-2)+...+b^(n-1)) if n is odd */
/* in menu mode, applies directly.  In auto mode, only applies if n is
prime.  (Otherwise, we should first factor the exponents and use a^(kj)=(a^k)^j,
and apply this law with a small j.) */
/* If n is a variable which can be 'checked' to be of type natnum and > 1,
then it will produce a term involving SUM */

{ term u,v,a,b,n,temp,w,c;
  int err,i,st;
  int flag = 0;
  int sflag = 0;  /* answer will include a SUM term */
  long nn,mm;
  if(FUNCTOR(t) != '+')
     return 1;
  if(ARITY(t) != 2)
     return arity_aux2(t,arg,next,reason,differenceofnthpowers);
  u = ARG(0,t);
  v = ARG(1,t);
      /* Now t = u + v  */
  if(NEGATIVE(u) || NEGATIVE(v))
     return 1;  /* use difference of nth powers instead */
  /* Now identify the exponent */
  err = getexp(u,&nn);
  if(err)
     { /* exponent is not a (specific) integer */
        if(FUNCTOR(u) != '^')
           return 1;
        a = ARG(0,u);
        n = ARG(1,u);
        err = infer(type(n,INTEGER));
        if(err)
           return 1;
        err = check1(lessthan(zero,n));
        if(err)
           return 1;
        err = infer(odd(n));
        if(err)
           { errbuf(0, english(1335));
             /* Can't verify exponent is odd. */
             return 1;
           }
        sflag = 1;  /* exponent is a variable */
     }
  err = getexp(v,&mm);
  if(err && sflag)
     { /* 2nd exponent is not a (specific) integer and neither is first one */
        if(FUNCTOR(v) != '^')
           return 1;
        b = ARG(0,v);
        if(!equals(n,ARG(1,u)))  /* they'd better be the same */
           return 1;
        sflag = 3;  /* both exponents variable */
     }
  if(err && !sflag)
     { /* 2nd exponent is not a specific integer but first one is */
        if(nn !=0)
           return 1;  /* we can only do  things like 1 -x^n in this case */
        if(FUNCTOR(v) != '^')
           return 1;
        b = ARG(0,v);
        n = ARG(1,v);
        err = infer(type(n,INTEGER));
        if(err)
           return 1;
        err = check1(lessthan(zero,n));
        if(err)
           return 1;
        err = infer(odd(n));
        if(err)
           { errbuf(0, english(1335));
             /* Can't verify exponent is odd. */
             return 1;
           }
        sflag = 2;  /* second exponent is a variable and first one isn't */
     }
  if(!sflag)
     { if(!nn && !mm)
          return 1;    /* both u and v were numerical */
       if (nn==mm)
          n = make_int(nn);  /* the exponent to use for the rule */
       else if(nn==0)
          n = make_int(mm);
       else if(mm==0)
          n = make_int(nn);
       else
          { n = make_int(intgcd(nn,mm));   /* the exponent to use for the rule */
            flag = 1;  /* exponent is not same as in original problem */
          }
       nn = INTDATA(n);
       if(! (nn & 1) )
          {  errbuf(0, english(1334));
             /* Exponent is not odd */
             return 1;  /* reject even exponents */
          }
     }
  if(sflag)
     { term hi,index,running,q;
       if(sflag == 2)  /* only second exponent is variable */
          { err = nthroot_aux(u,n,&a);
            if(err)
               return 1;
          }
       else if(sflag == 1) /* only first exponent is variable */
          { err = nthroot_aux(v,n,&b);
            if(err)
               return 1;
          }
       /* Now a,b, and n are all properly instantiated */
       temp = make_term(SUM,4);
       index = index_variable(t);
       ARGREP(temp,1,index);
       ARGREP(temp,2,zero);
       polyval(sum(n,minusone),&hi);
       ARGREP(temp,3,hi);
       polyval(sum(hi,tnegate(index)),&q);
       if(get_orderflag() == ASCENDING)
           running = product(make_power(a,index),make_power(b,q));
       else
           running = product(make_power(a,q),make_power(b,index));
       running = product(make_power(minusone,index),running);
       ARGREP(temp,0,running);
       *next = product(sum(a,b),temp);
     }
  else
     {  /* That finishes the case of a symbolic exponent.
           Now deal with numerical exponents. */

       err = nthroot_aux(v,n,&b);
       if(err)
          return 1;
       err = nthroot_aux(u,n,&a);
       if(err)
          return 1;
       if(
          get_mathmode() == AUTOMODE &&
          (  (FUNCTOR(u) == '^' && !equals(ARG(1,u),three))
           ||(FUNCTOR(v) == '^' && !equals(ARG(1,v),three))
           || FUNCTOR(u) == '*' || FUNCTOR(v) == '*'
          ) &&
          (status(powertopower) < WELLKNOWN ||
           status(differenceofnthpowers) < WELLKNOWN  ||
           status(producttopower) < WELLKNOWN
          ) &&
          (FUNCTOR(u) == '*' || FUNCTOR(v) == '*' || flag)
         )
          { PROTECT(a);
            PROTECT(b);
            *next = sum(make_power(a,n),tnegate(make_power(b,n)));
            HIGHLIGHT(*next);
            if(FUNCTOR(u) == '*' || FUNCTOR(v) == '*')
               strcpy(reason,"a^nb^n = (ab)^n");
            else   /*  if(flag)  */
               strcpy(reason,"a^(nm) = (a^n)^m");
            return 0;
           }
       if(nn > 100)
          { errbuf(0, english(351));
              /* That would create a very long expression. */
            errbuf(1, english(352));
              /* Try a problem with an exponent less than 100. */
            return 1;
          }
       UNPROTECT(a); /* in case protected by a previous application */
       UNPROTECT(b);
       st = status(sumofnthpowers);
       temp = make_term('+',(unsigned short) nn);
       ARGREP(temp,0,make_power(a,make_int(nn-1)));
       ARGREP(temp,(unsigned short)(nn-1),make_power(b,make_int(nn-1)));
       for(i=1;i<nn-1;i++)
          { c = product(make_power(a,make_int(nn-1-i)),make_power(b,make_int(i)));
            if(st != LEARNING)
               polyval1(c,&w);
            else
               w = c;
            if( i & 1)
               w = tnegate(w);
            ARGREP(temp,i,w);
          }
       *next = product(sum(a,b),temp);
     }  /* That finishes the case of numerical exponents.
           Now polish up the answer and return */
  SETORDERED(ARG(0,*next));
  SETORDERED(ARG(1,*next));  /* tell auto mode not to reorder the terms */
  SETORDERED(*next);
  HIGHLIGHT(*next);
  SETORDERED(*next);   /* don't rearrange order of these factors, it looks silly */
  strcpy(reason,"a^(n+1)+b^(n+1) =     (a+b)(a^n - ... + b^n");
  return 0;
}
/*_______________________________________________________________________*/
int differenceofnth2(term t, term arg, term *next, char *reason)
/* a^n-b^n = (a+b)(a^(n-1)-a^(n-2)+...+b^(n-1)) if n is even */
/* in menu mode, applies directly.  In auto mode, only applies if n is
prime.  (Otherwise, we should first factor the exponents and use a^(kj)=(a^k)^j,
and apply this law with a small j.) */
/* If n is a variable which can be 'checked' to be of type natnum and > 1,
then it will produce a term involving SUM */

{ term u,v,a,b,n,temp,w,c,d;
  int err,i;
  int st;
  int flag = 0;
  int sflag = 0;  /* answer will include a SUM term */
  long nn,mm;
  if(FUNCTOR(t) != '+')
     return 1;
  if(ARITY(t) != 2)
     return arity_aux2(t,arg,next,reason,differenceofnthpowers);
  if(NEGATIVE(ARG(0,t)))
     { v = ARG(0,ARG(0,t));
       u = ARG(1,t);
     }
  else if(NEGATIVE(ARG(1,t)))
     { u = ARG(0,t);
       v = ARG(0,ARG(1,t));
     }
  else
     return 1;
      /* Now t = u - v  */
  /* Now identify the exponent */
  err = getexp(u,&nn);
  if(err)
     { /* exponent is not a (specific) integer */
        if(FUNCTOR(u) != '^')
           return 1;
        a = ARG(0,u);
        n = ARG(1,u);
        err = infer(type(n,INTEGER));
        if(err)
           return 1;
        err = check1(lessthan(zero,n));
        if(err)
           return 1;
        err = infer(even(n));
        if(err)
           { errbuf(0, english(2394));
             /* Can't verify exponent is even. */
             return 1;
           }
        sflag = 1;  /* exponent is a variable */
     }
  err = getexp(v,&mm);
  if(err && sflag)
     { /* 2nd exponent is not a (specific) integer and neither is first one */
        if(FUNCTOR(v) != '^')
           return 1;
        b = ARG(0,v);
        if(!equals(n,ARG(1,u)))  /* they'd better be the same */
           return 1;
        sflag = 3;  /* both exponents variable */
     }
  if(err && !sflag)
     { /* 2nd exponent is not a specific integer but first one is */
        if(nn !=0)
           return 1;  /* we can only do  things like 1 -x^n in this case */
        if(FUNCTOR(v) != '^')
           return 1;
        b = ARG(0,v);
        n = ARG(1,v);
        err = infer(type(n,INTEGER));
        if(err)
           return 1;
        err = check1(lessthan(zero,n));
        if(err)
           return 1;
        err = infer(even(n));
        if(err)
           { errbuf(0, english(2394));
             /* Can't verify exponent is odd. */
             return 1;
           }
        sflag = 2;  /* second exponent is a variable and first one isn't */
     }
  if(!sflag)
     { if(!nn && !mm)
          return 1;    /* both u and v were numerical */
       if (nn==mm)
          n = make_int(nn);  /* the exponent to use for the rule */
       else if(nn==0)
          n = make_int(mm);
       else if(mm==0)
          n = make_int(nn);
       else
          { n = make_int(intgcd(nn,mm));   /* the exponent to use for the rule */
            flag = 1;  /* exponent is not same as in original problem */
          }
       nn = INTDATA(n);
       if(nn & 1)
          {  errbuf(0, english(2395));
             /* Exponent is not even */
             return 1;  /* reject odd exponents */
          }
     }
  if(sflag)
     { term hi,index,running,q;
       if(sflag == 2)  /* only second exponent is variable */
          { err = nthroot_aux(u,n,&a);
            if(err)
               return 1;
          }
       else if(sflag == 1) /* only first exponent is variable */
          { err = nthroot_aux(v,n,&b);
            if(err)
               return 1;
          }
       /* Now a,b, and n are all properly instantiated */
       temp = make_term(SUM,4);
       index = index_variable(t);
       ARGREP(temp,1,index);
       ARGREP(temp,2,zero);
       polyval(sum(n,minusone),&hi);
       ARGREP(temp,3,hi);
       polyval(sum(hi,tnegate(index)),&q);
       if(get_orderflag() == ASCENDING)
           running = product(make_power(a,index),make_power(b,q));
       else
           running = product(make_power(a,q),make_power(b,index));
       running = product(make_power(minusone,index),running);
       ARGREP(temp,0,running);
       *next = product(sum(a,b),temp);
     }
  else
     {  /* That finishes the case of a symbolic exponent.
           Now deal with numerical exponents. */

       err = nthroot_aux(v,n,&b);
       if(err)
          return 1;
       err = nthroot_aux(u,n,&a);
       if(err)
          return 1;
       if(
          get_mathmode() == AUTOMODE &&
          (  (FUNCTOR(u) == '^' && !equals(ARG(1,u),three))
           ||(FUNCTOR(v) == '^' && !equals(ARG(1,v),three))
           || FUNCTOR(u) == '*' || FUNCTOR(v) == '*'
          ) &&
          (status(powertopower) < WELLKNOWN ||
           status(differenceofnthpowers) < WELLKNOWN  ||
           status(producttopower) < WELLKNOWN
          ) &&
          (FUNCTOR(u) == '*' || FUNCTOR(v) == '*' || flag)
         )
          { PROTECT(a);
            PROTECT(b);
            *next = sum(make_power(a,n),make_power(b,n));
            HIGHLIGHT(*next);
            if(FUNCTOR(u) == '*' || FUNCTOR(v) == '*')
               strcpy(reason,"a^nb^n = (ab)^n");
            else   /*  if(flag)  */
               strcpy(reason,"a^(nm) = (a^n)^m");
            return 0;
           }
       if(nn > 100)
          { errbuf(0, english(351));
              /* That would create a very long expression. */
            errbuf(1, english(352));
              /* Try a problem with an exponent less than 100. */
            return 1;
          }
       UNPROTECT(a); /* in case protected by a previous application */
       UNPROTECT(b);
       st = status(differenceofnth2);
       temp = make_term('+',(unsigned short) nn);
       ARGREP(temp,0,make_power(a,make_int(nn-1)));
       ARGREP(temp,(unsigned short)(nn-1),tnegate(make_power(b,make_int(nn-1))));
       for(i=1;i<nn-1;i++)
          { c = make_power(a,make_int(nn-1-i));
            d= product(c,make_power(b,make_int(i)));
            if(st != LEARNING)
               polyval1(d,&w);
            else
               w = d;
            if( i & 1)
               w = tnegate(w);
            ARGREP(temp,i,w);
          }
       *next = product(sum(a,b),temp);
     }  /* That finishes the case of numerical exponents.
           Now polish up the answer and return */
  SETORDERED(ARG(0,*next));
  SETORDERED(ARG(1,*next));  /* tell auto mode not to reorder the terms */
  SETORDERED(*next);
  HIGHLIGHT(*next);
  SETORDERED(*next);   /* don't rearrange order of these factors, it looks silly */
  strcpy(reason,"a^n-b^n =     (a+b)(a^(n-1)- ... -b^(n-1)");
  return 0;
}


/*_______________________________________________________________________*/
int sumoffourthpowers(term t, term arg, term *next, char *reason)
/* a^4+b^4=(a^2+\sqrt2ab+b^2)(a^2-\sqrt2ab+b^2) */
{ term u,v,a,b,temp,first, middle,last;
  term asq,a2s,a2c,as,ac,bsq,b2s,b2c,bs,bc;
  int err;
  aflag arithflag = get_arithflag();
  int saveit = arithflag.roots;
  if(FUNCTOR(t) != '+')
     return 1;
  if(ARITY(t) != 2)
     return arity_aux2(t,arg,next,reason,sumoffourthpowers);
  if(ARITY(t) != 2)
     return 1;
  u = ARG(0,t);
  v = ARG(1,t);
      /*  t = u + v  */
  arithflag.roots = 1;
  set_arithflag(arithflag);
  err = sqrt_aux(v,&bsq);
  if(err)
     { arithflag.roots = saveit;
       set_arithflag(arithflag);
       return 1;
     }
  if(NUMBER(bsq))
     { b2c = bsq;
       b2s = one;
     }
  else if(FUNCTOR(bsq) == '*')
     ratpart2(bsq,&b2c,&b2s);
  else
     { b2c = one;
       b2s = bsq;
     }
  err = sqrt_aux(b2s,&bs);
  if(err)
     { arithflag.roots = saveit;
       set_arithflag(arithflag);
       return 1;
     }
  if(INTEGERP(b2c) && ISEVEN(b2c))
     bc = make_sqrt(b2c);
  else if(get_mathmode() == AUTOMODE && !(get_ringflag() & ALGINT))
     { arithflag.roots = saveit;
       set_arithflag(arithflag);
       return 1;
     }
  else
     { err = value(make_power(b2c,make_fraction(one,two)),&bc);
       if(err)
           bc = make_sqrt(b2c);
     }
  b = product(bc,bs);
  err = sqrt_aux(u,&asq);
  if(err)
     { arithflag.roots = saveit;
       set_arithflag(arithflag);
       return 1;
     }
  if(NUMBER(asq))
     { a2c = asq;
       a2s = one;
     }
  else if(FUNCTOR(asq) == '*')
     ratpart2(asq,&a2c,&a2s);
  else
     { a2c = one;
       a2s = asq;
     }
  err = sqrt_aux(a2s,&as);
  if(err)
     { arithflag.roots = saveit;
       set_arithflag(arithflag);
       return 1;
     }
  if(ONE(a2c))
     ac = one;
  else
     { err = value(make_power(a2c,make_fraction(one,two)),&ac);
       if(err)
          { if(get_mathmode() == AUTOMODE && !(get_ringflag() & ALGINT))
               { arithflag.roots = saveit;
                 set_arithflag(arithflag);
                 return 1;
               }
            else
                ac = make_sqrt(a2c);
          }
     }
  a = product(ac,as);
  if(
     get_mathmode() == AUTOMODE &&
     (  (FUNCTOR(u) == '^' && !equals(ARG(1,u),four))
      ||(FUNCTOR(v) == '^' && !equals(ARG(1,v),four))
      || FUNCTOR(u) == '*' || FUNCTOR(v) == '*'
     ) &&
     (status(powertopower) < WELLKNOWN ||
      status(sumofcubes) < WELLKNOWN  ||
      status(producttopower) < WELLKNOWN
     )
    )
     { u = make_power(a,four);
       v = make_power(b,four);
       PROTECT(u);
       PROTECT(v);
       *next = sum(u,v);
       HIGHLIGHT(*next);
       if(FUNCTOR(u) == '*' || FUNCTOR(v) == '*')
          strcpy(reason,"a^2b^2 = (ab)^2");
       else
          strcpy(reason,"a^(4n) = (a^n)^4");
       return 0;
     }
  if(FUNCTOR(bc) == SQRT)
     { value(product(two,make_power(make_fraction(ARG(0,bc),two),make_fraction(one,two))),&temp);
       middle = product3(temp,a,bs);
     }
  else
     middle = product3(make_sqrt(two),a,b);
  if(FUNCTOR(middle)=='*')
     sortargs(middle);
  first = make_term('+',3);
  ARGREP(first,0,asq);
  ARGREP(first,1,tnegate(middle));
  ARGREP(first,2,bsq);
  last = make_term('+',3);
  ARGREP(last,0,asq);
  ARGREP(last,1,middle);
  ARGREP(last,2,bsq);
  *next = product(first,last);
  HIGHLIGHT(*next);
  SETORDERED(*next);   /* don't rearrange order of these factors, it looks silly */
  strcpy(reason,"$a^4+b^4=(a^2-\\sqrt2ab+b^2)$    $(a^2+\\sqrt2ab+b^2)$");
  arithflag.roots = saveit;
  set_arithflag(arithflag);
  return 0;
}

/*_______________________________________________________________________*/
int factorquartic(term t, term arg, term *next, char *reason)
/* x^4+(2p-q^2)x^2+p^2=(x^2+qx+p)(x^2-qx+p) */
/* example:  x^4 + x^2 + 1 = (x^2+x+1)(x^2-x+1) */
{ term u,v,w,b,x,p,q,qsq,xsq,s1,s2,qx;
  int err;
  if(FUNCTOR(t) != '+')
     return 1;
  if(ARITY(t) != 3)
     return 1;
  if(ARITY(t) == 2)  /* in menu mode */
     return sumoffourthpowers(t,arg,next,reason);
  u = ARG(0,t);
  w = ARG(1,t);
  v = ARG(2,t);
      /*  t = u + w + v  */
  err = sqrt_aux(v,&p);
  if(err)
     return 1;
  err = sqrt_aux(u,&xsq);
  if(err)
     return 1;
  err = sqrt_aux(xsq,&x);
  if(err)
     return 1;
  polyval(make_fraction(w,xsq),&b); /* w = bx^2 */
  polyval(sum(product(two,p),tnegate(b)),&qsq);
  err = sqrt_aux(qsq,&q);
  if(err)
     return 1;
  s1 = make_term('+',3);
  s2 = make_term('+',3);
  qx = product(q,x);
  if(FUNCTOR(qx) == '*')
     sortargs(qx);
  ARGREP(s1,0,xsq);
  ARGREP(s1,1,tnegate(qx));
  ARGREP(s1,2,p);
  ARGREP(s2,0,xsq);
  ARGREP(s2,1,qx);
  ARGREP(s2,2,p);
  *next = product(s1,s2);
  HIGHLIGHT(*next);
  strcpy(reason, "x^4 + (2p-q^2)x^2 + p^2 = (x^2-qx+p)(x^2+qx+p)");
  return 0;
}
/*_______________________________________________________________________*/
int guessfactor(term t, term arg, term *next, char *reason)
/* guess a factor (and test it by polydiv) */
/* arg is the guessed factor */
/* The variable for polynomial division is taken from arg; t itself
may contain many more variables.  If arg contains lots of variables
you'll get the eigenvariable, if it's contained in arg, otherwise
just the first one it contains. */

{ term x;  /* the variable in which things will be polynomials */
  term q,r; /* quotient and remainder */
  term *atomlist;
  int natoms;
  int err;
  if(FUNCTOR(t) != '+')
     return 1;  /* this only works on sums */
  assert(FUNCTOR(arg) != ILLEGAL);  /* arg must be supplied */
  x = get_eigenvariable();
  if(!contains(arg,FUNCTOR(x)))   /* then choose a better 'x' */
     { natoms = atomsin(arg,&atomlist); /* number of variables in arg */
       if(natoms==0)
          { errbuf(0, english(353));
              /* Proposed factor must contain a variable. */
            return 1;
          }
       x = atomlist[0];  /* as good as any other choice probably */
       free2(atomlist);
     }
  err = polydiv(x,t,arg,&q,&r);  /* in file polynoms.c */
  if(err)
     return 1;   /* either t or arg was not a polynomial in x */
  if(!ZERO(r))
     { errbuf(0,english(2370));  /* Sorry, the term you entered is not factor. */
       return 1;   /* arg does not divide t */
     }
  *next = product(arg,q);
  HIGHLIGHT(*next);
  strcpy(reason, english(350));  /* polynomial division */
  return 0;
}
/*_______________________________________________________________________*/
/* FACTORING BY GROUPING */
/*  In menu mode,
    it prompts the user for the group.

For example:  x^3 + xy -y^2 + y^3 =  (x^3 + y^3) + (xy -y^2) =
                                   = (x-y) (x^2 -xy + y^2 + y)
Note that even in the univariate case this could help, e.g.
(x-1)(x^2 + 2) + (x-1)(x) = x^3  +x -2 = (x-1)(x^2 +x + 2).

But in auto mode  it would slow things down when it doesn't work!  e.g.
on 1+x+...+x^7 there will be lots of partitions to check, especially
since on ones of length at least four this routine will be called
again.

Even with 4 terms it's slow:  e.g. on x^3 + xy -y^2 -y^3, where one of
the resulting factors also has four terms and doesn't factor further,
so all the pairs have to be tried.

Sometimes it's needed with three terms too, as in r^2 -s^2 + (r+s)^2

Difficult example:  r^2 - 4s^2 + (r+2s)^2  requires 2 steps of factoring
to factor r^2-4s^2.

So what we need is a way to recognize the good groupings.
One case is when there is a variable in which the polynomial is linear;
the two groups are the terms that contain it and the ones that
do not; or more generally, when it can be written as a polynomial
in one of the variables such that the coefficients have a nontrivial gcd.

In auto mode, MathXpert will try grouping 2 or 3 quadratic terms
together and putting the remaining (non-quadratic)
terms in the other group.  It ONLY tries this for bivariates.
If that fails, it will try, for each variable x in the problem,
the group of terms containing x.   Other groupings will not be tried.

There is a compromise here with cognitive fidelity:  students are taught
to factor the two groups and then pull out the content.  It is more efficient
to just take the polygcd of the two groups, which will produce the common
factor immediately, and then you can extract it by division. The efficient
way would be like this:
  (1) get arg from user (one of the two groups)
  (2) compute the two groups t = u + v
  (3) Convert to POLY form--
  (4) w= gcd(u,v) is the common factor. Compute p = u/w and q = v/w
      by polydiv (selecting one variable as the independent one)
      and return t = wp + wq.  This is ready to content-factor.

What we do is handle many simple and common cases in such a way as to produce
steps that can be done in term selection mode without using 'factor by grouping',
but in the end use the above algorithm if needed.

  Many simple examples can be done by re-ordering the terms, then
content-factoring each of two groups, then content-factoring the
whole expression. This operation will take the reordering step if necessary,
imitating the operation a+b=b+a; on a subsequent application it will
take more reording steps if required; on a subsequent application it will
content-factor and leave the reason string ab+ac = a(b+c).

   If this doesn't work, more generally the sum can be broken into two groups
a non-trivial gcd as described above.  Selecting one variable after
another we use a polynomial gcd algorithm to compute the gcds.
In this case the reason string will be "factor by grouping".  There
will still be a content-factor step to perform after this.
*/

int factorbygrouping(term t, term arg, term *next, char *reason)
{ int i,j,err,natoms,count=0,xindex=0,signflag;
  term p,u,v,x,y,common,c,d,c2,d2,w;
  term *atomlist;
  unsigned short path[7];
  char buffer[DIMREASONBUFFER];
  unsigned short n;
  int difsquaresflag = 0;
  if(FUNCTOR(t)!= '+')
     return 1;
  n = ARITY(t);
  /*  t does not have to be an mvpoly (sum of monomials) because one
      summand can be a sum, as in ac + bc + (a+b), or a factored product,
      as in ac + ac+ a(b+c) */
  if(FUNCTOR(arg) != ILLEGAL)
     { if(FUNCTOR(arg) != '+')
          { errbuf(0, english(1858));
            return 1;
          }
       return finish_factorbygrouping(zero,t,arg,next,reason);
     }
  if(n == 3)
     { /* example:  ab + ac + a(b+c) */
       for(i=0;i<n;i++)
          { v = ARG(i,t);
            if(NEGATIVE(v))
               v = ARG(0,v);
            if(contains(v,'+'))
               break;
          }
       if(i==n)
          return 1;
       switch(i)
          { case 0:
               u = sum(ARG(1,t),ARG(2,t));
               break;
            case 1:
               u = sum(ARG(0,t),ARG(2,t));
               break;
            case 2:
               u = sum(ARG(0,t),ARG(1,t));
               break;
          }
       v = ARG(i,t);
       err = content_factor(u,&c,&d);
       if(err)
          return 1;
       p = product(c,d);
       naive_gcd(p,v,&common);
       if(!contains(common,'+'))
          { if(NEGATIVE(v))
               v = ARG(0,v);
            if(FUNCTOR(v) != '+')
               return 1;
            /* You may need to factor v before the common factor is apparent
               Example: h^2k^2 + 4k^2 + (h^2 + 4k); here v is h^2k + 4k
            */
            err = content_factor(v,&c,&d);
            if(err)
               return 1;
            naive_gcd(p,d,&common);
            if(!contains(common,'+'))
               return 1;
          }
       if(i==1)
          { *next = make_term('+',3);
            ARGREP(*next,0,ARG(0,t));
            ARGREP(*next,1,ARG(2,t));
            ARGREP(*next,2,ARG(1,t));
            HIGHLIGHT(ARG(1,*next));
            HIGHLIGHT(ARG(2,*next));
            strcpy(reason,"$a+b=b+a$");
            SetShowStepOperation(additivecommute);
            path[0] = '+';
            path[1] = 2;
            path[2] = SUBRANGE;
            path[3] = 3;
            path[4] = 0;
            set_pathtail(path);
            return 0;  /* next time this operation will work with i=2 */
          }
       *next = sum(p,v);
       HIGHLIGHT(*next);
       strcpy(reason, "$ab+ac = a(b+c)$");
       SetShowStepOperation(contentfactor);
       path[0] = '+';
       path[1] = (unsigned short)(i == 2? 1 : 2);
       path[2] = SUBRANGE;
       path[3] = (unsigned short)(i == 2? 2 : 3);
       path[4] = 0;
       set_pathtail(path);
       return 0;
     }
  if(n == 4)
     { for(i=1;i<4;i++)
          { u = sum(ARG(0,t),ARG(i,t));
            switch(i)
               { case 1:
                    v = sum(ARG(2,t),ARG(3,t));
                    break;
                 case 2:
                    v = sum(ARG(1,t),ARG(3,t));
                    break;
                 case 3:
                    v = sum(ARG(1,t),ARG(2,t));
                    break;
               }
            err = content_factor(u,&c,&d);
            if(!err)
               { if(eqtest(d,v))
                    { if(i==1)
                         { w = product(c,d);
                           HIGHLIGHT(w);
                           *next = sum(w,v);
                           strcpy(reason,"$ab+ac = a(b+c)$");
                           SetShowStepOperation(contentfactor);
                           path[0] = '+';
                           path[1] = 1;
                           path[2] = SUBRANGE;
                           path[3] = 2;
                           path[4] = 0;
                           set_pathtail(path);
                           return 0;
                         }
                      if(i==2)
                         goto swap12;
                      if(i==3)
                         goto swap23;
                    }
                 err = content_factor(v,&c2,&d2);
                 if(err && contains(v,'^'))
                    { err = differenceofsquares(v,zero,&d2,buffer);
                      if(!err)
                         { c2 = one;
                           difsquaresflag += 2;
                         }
                    }
                 if(!err)
                    { naive_gcd(d,d2,&common);
                      if(contains(common,'+'))
                         { if(i==1 && !difsquaresflag)
                              { *next = sum(product(c,d),v);
                                   /* with  product(c2,d2) in place of v, the
                                      result can't be duplicated in Term Selection Mode.
                                      You need to take two steps to factor.
                                      Example:   ab + ac + bd + cd
                                                 a(b+c) + bd + cd
                                                 a(b+c) + d(b+c)
                                                 (a+d)(b+c)
                                  */
                                HIGHLIGHT(*next);
                                strcpy(reason,"$ab+ac = a(b+c)$");
                                SetShowStepOperation(contentfactor);
                                path[0] = '+';
                                path[1] = 1;
                                path[2] = SUBRANGE;
                                path[3] = 2;
                                path[4] = 0;
                                set_pathtail(path);
                                return 0;
                              }
                           if(i==1) /* && difsquaresflag */
                              { *next = make_term('+',3);
                                ARGREP(*next,0,ARG(0,t));
                                ARGREP(*next,1,ARG(1,t));
                                ARGREP(*next,2,d2);
                                strcpy(reason,buffer);
                                SetShowStepOperation(differenceofsquares);
                                path[0] = '+';
                                path[1] = 2;
                                path[2] = SUBRANGE;
                                path[3] = 3;
                                path[4] = 0;
                                set_pathtail(path);
                                return 0;
                              }
                           if(i==2)
                              goto swap12;
                           if(i==3)
                              goto swap23;
                         }
                    }
               }
            else if(!differenceofsquares(u,zero,&d,buffer))
               { err = content_factor(v,&c2,&d2);
                 if(err)
                    { d2 = v;
                      c2 = one;
                    }
                 naive_gcd(d2,d,&common);
                 if(!ONE(common))
                    { if(i==1)
                         { *next = sum(d,v);
                           strcpy(reason,buffer);
                           if(FUNCTOR(u) == '+' && !NEGATIVE(ARG(0,u)) && !NEGATIVE(ARG(1,u)))
                              SetShowStepOperation(sumofsquares);
                           else
                              SetShowStepOperation(differenceofsquares);
                           path[0] = '+';
                           path[1] = 1;
                           path[2] = SUBRANGE;
                           path[3] = 2;
                           path[4] = 0;
                           set_pathtail(path);
                           return 0;
                         }
                      if(i==2)
                         goto swap12;
                      if(i==3)
                         goto swap23;
                    }
               }
            else if(!content_factor(v,&c2,&d2))
               { naive_gcd(u,d2,&common);
                 if(!ONE(common))
                     { if(i==1)
                          { w = product(c2,d2);
                            HIGHLIGHT(w);
                            *next = sum(u,w);
                            strcpy(reason,"$ab+ac = a(b+c)$");
                            SetShowStepOperation(contentfactor);
                            path[0] = '+';
                            path[1] = 3;
                            path[2] = SUBRANGE;
                            path[3] = 4;
                            path[4] = 0;
                            set_pathtail(path);
                            return 0;
                          }
                       if(i==2)
                          goto swap12;
                       if(i==3)
                          goto swap23;
                     }
               }
          }
       /* Now look for something like (p-q)^2-r^2+2r-1, which arises
          in Show Step after the first application of factorsquareofdif. */
       for(i=0;i<4;i++)
          { w = ARG(i,t);
            if(NEGATIVE(w))
               { w = ARG(0,w);
                 signflag = 1;
               }
            else
               signflag = 0;
            if(FUNCTOR(w) == '^' && iseven(ARG(1,w)))
               { u = make_term('+',3);
                 for(j=0;j<3;j++)
                    ARGREP(u,j,j<i ? ARG(j,t) : ARG(j+1,t));
                 err = factorsquareofsum(u,zero,&d2,reason);
                 if(!err &&
                    ((signflag && !NEGATIVE(d2)) || (!signflag && NEGATIVE(d2)))
                   )
                    { path[0] = '+';
                      path[1] = i==0 ? 2 : 1;
                      path[2] = SUBRANGE;
                      path[3] = i==3? 3 : 4;
                      path[4] = 0;
                      set_pathtail(path);
                      SetShowStepOperation(factorsquareofsum);
                      *next = sum(ARG(i,t),d2);
                      return 0;
                    }
                 err = factorsquareofdif(u,zero,&d2,reason);
                 if(!err &&
                    ((signflag && !NEGATIVE(d2)) || (!signflag && NEGATIVE(d2)))
                   )
                    { path[0] = '+';
                      path[1] = i==0 ? 2 : 1;
                      path[2] = SUBRANGE;
                      path[3] = i==3? 3 : 4;
                      path[4] = 0;
                      set_pathtail(path);
                      SetShowStepOperation(factorsquareofdif);
                      *next = sum(ARG(i,t),d2);
                      return 0;
                    }
               }
          }
     }
  /* The rest of the code assumes we're working with polynomials,
     rather than e.g. polynomials in tan x and sin x.  So we
     reject non-polynomial input before getting to code that
     depends on the input being polynomial. */
  for(i=0;i<n;i++)
    { u = ARG(i,t);
      if(NEGATIVE(u))
         u = ARG(0,u);
      if(monomial(u))
         continue;
      if(FUNCTOR(u) == '+')
         { for(j=0;j<ARITY(u);j++)
              { if(!monomial(ARG(j,u)))
                   return 1;
              }
         }
      if(FUNCTOR(u) == '*')
         { for(j=0;j<ARITY(u);j++)
              { if(!mvpoly(ARG(j,u)))
                   return 1;
              }
         }
     }
  /* Now for selecting the group */
  natoms = atomsin(t,&atomlist);
  if(natoms == 1)
     { free2(atomlist);
       return 1;  /* don't work on univariates */
     }
  if(natoms > 2)
     {  /* example:  ax^2 -3bx^2 -6by + 2ay */
        /* We must select two variables x and y to work on */
        /* select two variables in which it is not linear */
        for(i=0;i<natoms;i++)
           { u = atomlist[i];
             err = makepoly(t,u,&p);
             if(err)
                return 1;
             if(ARITY(p) <= 2)
                continue;
             if(count)
                { y = u;
                  ++count;
                  break;
                }
             else
                { x = u;
                  ++count;
                  xindex = i;
                  continue;
                }
           }
        if(count < 2)
           {  /* nonlinear in only one, or in no, variables */
              /* example, ax^2 -3bx^2 - 6by + 2ay = x^2(a-3b) +2y(a-3b)  */
              y = atomlist[xindex ? 0 : 1]; /* any other variable */
              xindex = -1;  /* to show we are in this case */
           }
      }
   else  /* the selection is easy when there are only two variables */
      { x = atomlist[0];
        y = atomlist[1];
      }
   if( (n==4 || n == 5 || n == 6) && xindex >= 0 && natoms==2)
      /* example: 2x+4y+x^2+2xy shows it can work when n==4 */
      { err = quadratic_group(t,x,y,&arg);
        if(err==1)
           return 1;
        if(!err)
           { err = finish_factorbygrouping(zero,t,arg,next,reason);
             if(!err)
                { free2(atomlist);
                  return 0;  /* success */
                }
           }
      }
   if(n == 6 && natoms >= 2)
      { /* example:  x^2 + 2xy + y^2 - p^2 - 2p - 1;  the two groups are
           a square and the negation of a square so the whole thing can be
           factored as a difference of squares */
         /* arity_aux is programmed to fail in automode sometimes so we
            make sure it doesn't think it's in automode */
         int mathmode = get_mathmode();
         menumode();
         err = arity_aux(t,arg,&u,reason,factorsquareofdif);
         if(!err)
            { SaveShowStepState();
              err = arity_aux(u,arg,next,reason,factorsquareofdif);
              if(!err)
                 { set_mathmode(mathmode);
                   RestoreShowStepState();
                   SetShowStepOperation(factorsquareofdif);
                   return 0;
                 }
              err = arity_aux(u,arg,next,reason,factorsquareofsum);
              if(!err)
                 { set_mathmode(mathmode);
                   RestoreShowStepState();
                   SetShowStepOperation(factorsquareofdif);
                   return 0;
                 }
              RestoreShowStepState();
            }
         err = arity_aux(t,arg,&u,reason,factorsquareofsum);
         if(!err)
            { SaveShowStepState();
              err = arity_aux(u,arg,next,reason,factorsquareofsum);
              if(!err)
                 { set_mathmode(mathmode);
                   RestoreShowStepState();
                   SetShowStepOperation(factorsquareofsum);
                   return 0;
                 }
              RestoreShowStepState();
            }
         set_mathmode(mathmode);
      }
   for(i=0;i<natoms;i++)
      { x = atomlist[i];
        err = xgroup(t,x,&arg);
        if(err==1)
           continue;
        if(ARITY(t) - ARITY(arg) < 2)
           continue;  /* there must be at least two terms that do not contain x */
                      /* without this clause we get a nasty loop when all terms
                         contain x; then arg comes back as x itself */
        if(!err)
          { err = finish_factorbygrouping(x,t,arg,next,reason);
            if(!err)
               { free2(atomlist);
                 return 0;  /* success */
               }
          }
      }
  free2(atomlist);
  return 1;     /* give up and fail */
  swap23:
     /* swap args 2 and 3 */
     *next = make_term('+',4);
     ARGREP(*next,0,ARG(0,t));
     ARGREP(*next,1,ARG(1,t));
     ARGREP(*next,2,ARG(3,t));
     ARGREP(*next,3,ARG(2,t));
     HIGHLIGHT(ARG(3,*next));
     HIGHLIGHT(ARG(2,*next));
     strcpy(reason,"$a+b=b+a$");
     SetShowStepOperation(additivecommute);
     path[0] = '+';
     path[1] = 3;
     path[2] = SUBRANGE;
     path[3] = 4;
     path[4] = 0;
     set_pathtail(path);
     return 0;
  swap12:
     /* swap args 1 and 2 */
     *next = make_term('+',4);
     ARGREP(*next,0,ARG(0,t));
     ARGREP(*next,1,ARG(2,t));
     ARGREP(*next,2,ARG(1,t));
     ARGREP(*next,3,ARG(3,t));
     HIGHLIGHT(ARG(1,*next));
     HIGHLIGHT(ARG(2,*next));
     strcpy(reason,"$a+b=b+a$");
     SetShowStepOperation(additivecommute);
     path[0] = '+';
     path[1] = 2;
     path[2] = SUBRANGE;
     path[3] = 3;
     path[4] = 0;
     set_pathtail(path);
     return 0;
}
/*__________________________________________________________________________*/
static int finish_factorbygrouping(term x, term t, term arg, term *next, char *reason)
/* x is an atom if xgroup has been used to select arg; otherwise x is zero */
/* user has entered arg or this is a recursive call
   where the above code chose arg.
*/
{ int err;
  term temp,p,u,v,common,commonterm,a,b,c,d,c2,d2,trash,first,rest,aa,bb,t1,t2;
  int save_polyvalgcdflag,savefactorflag2,savefactorflag;
  unsigned short n = ARITY(t);
  unsigned short m = ARITY(arg);
  unsigned short i,j;
  int saveit;
  assert(FUNCTOR(t) == '+' && FUNCTOR(arg) == '+');
  if(FUNCTOR(arg) != '+')
     { errbuf(0, english(355));
       /* Sum of terms expected. */
       return 1;
     }
  if(m > (unsigned short)(n-2))
     { errbuf(0, english(356));
       /* Too many terms in the group. */
       return 1;
     }
  for(i=0;i<m;i++)
     { for(j=0;j<n;j++)
          { if(equals(ARG(i,arg),ARG(j,t)))
               break;
          }
       if(j==n)  /* could not found the i-th term of arg in t */
          break;
     }
  if(i<m)
     { errbuf(0, english(357));
       /*  The group you entered contains a */
       errbuf(1, english(358));
       /* term which isn't in the problem.  */
       errbuf(2, english(359));
       /* Perhaps you made a typing mistake.*/
       return 1;
     }

 /* Now we may assume arg is instantiated */
  save_polyvalgcdflag = get_polyvalgcdflag();
  set_polyvalgcdflag(1);  /* (2x+2)/(x+1) should simplify to 2 */
       /* The coefficients of polynomials created below will involve
          other variables and should be presented in content-factored form
          so that the next step in computation can content-factor the whole
          expression. */
  savefactorflag = get_polyvalfactorflag();
  savefactorflag2 = get_polyvalfactorflag2();
  set_polyvalfactorflag(1);   /* content factoring */
  set_polyvalfactorflag2(0);  /* no other factoring */

  /* First handle the simple case in which the two groups have a common
     factor that can be found by content_factoring both groups.  This can
     work even if the expressions are not polynomials, e.g. if they're
     trig polys. */
  u = arg;
  polyval(sum(t,tnegate(arg)),&v);
  err = content_factor(u,&c,&d);
  if(err)
     { c = one;
       d = u;
     }
  if(FUNCTOR(d) != '+')
     goto fail;
  err = content_factor(v,&c2,&d2);
  if(err)
     { c2 = one;
       d2 = v;
     }
  if(equals(d,d2))
     { *next = product(sum(c,c2),d);
       HIGHLIGHT(*next);
       strcpy(reason, english(361)); /* factor by grouping */
       set_polyvalgcdflag(save_polyvalgcdflag);
       set_polyvalfactorflag(savefactorflag);
       set_polyvalfactorflag2(savefactorflag2);
       return 0;
     }
  if(ZERO(x))
     err = changetopoly(arg,&x,&u);
  else
     err = makepoly(arg,x,&u);
  if(err)
      { errbuf(0, english(360));
           /* The group you entered is not a polynomial. */
        goto fail;
      }
  err = makepoly(t,x,&p);
  if(err)
     goto fail;
  v = polysub(p,u);
  err = polygcd(u,v,&common);
  if(err || ARITY(common) == 1)
     goto fail;  /* we're not looking for only a constant factor */
  saveit = get_polyvalgcdflag();
  set_polyvalgcdflag(1);
  /* content_factor all the coefficients of u and v */
  for(i=0;i<ARITY(u);i++)
     { if(FUNCTOR(ARG(i,u)) == '+' && !content_factor(ARG(i,u),&t1,&t2))
          ARGREP(u,i,product(t1,t2));
     }
  for(i=0;i<ARITY(v);i++)
     { if(FUNCTOR(ARG(i,v)) == '+' && !content_factor(ARG(i,v),&t1,&t2))
          ARGREP(v,i,product(t1,t2));
     }
  pseudodiv(u,common,&a,&trash,&c);
  pseudodiv(v,common,&b,&trash,&d);
     /* now t = (1/c)*a*common + (1/d)*b*common   */
  aa = poly_term(a,x);
  commonterm = poly_term(common,x);
  PROTECT(commonterm); /* in case it isn't primitive, which it might
          not be in menu mode, we don't want to content-factor IT
          at the next step when the whole line should be content-factored */
  bb = poly_term(b,x);
  if(ONE(c))
     { if(equals(aa,commonterm))
          copy(make_power(aa,two),&first);
       else
          copy(product(aa,commonterm),&first);
     }
  else
     { temp = product(reciprocal(c),aa);
       polyval(temp,&c);
       if(FUNCTOR(c) == '*')
          copy(topflatten(product(c,commonterm)),&first);
       else if(equals(c,commonterm))
          copy(make_power(c,two),&first);
       else
          copy(product(c,commonterm),&first);
     }
  if(ONE(d))
     { if(equals(bb,commonterm))
          copy(make_power(bb,two),&rest);
       else
          copy(product(bb,commonterm),&rest);
     }
  else
     { temp = product(reciprocal(d),bb);
       polyval(temp,&d);
       if(FUNCTOR(d)=='*')
          copy(topflatten(product(d,commonterm)),&rest);
       else if(equals(d,commonterm))
          copy(make_power(d,two),&rest);
       else
          copy(product(d,commonterm),&rest);
     }
  set_polyvalgcdflag(saveit);
  *next = sum(first,rest);
  if(equals(t,*next))
     goto fail;   /* I don't think this can happen, but let's check it anyway. */
  HIGHLIGHT(*next);
  strcpy(reason, english(361)); /* factor by grouping */
  set_polyvalgcdflag(save_polyvalgcdflag);
  set_polyvalfactorflag(savefactorflag);
  set_polyvalfactorflag2(savefactorflag2);
  return 0;
  fail:
    set_polyvalgcdflag(save_polyvalgcdflag);
    set_polyvalfactorflag(savefactorflag);
    set_polyvalfactorflag2(savefactorflag2);
    return 1;
}
/*______________________________________________________________*/
static int quadratic_group(term t, term x,term y, term *ans)
/* t is an mvpoly of arity 4, 5 or 6, in two variables x and y */
/* return in *ans a group of the quadratic terms, if there are
two or three of them.   Return 0 for success, 1 for
input not a polynomial, 2 for other kinds of failure */
{ int err;
  unsigned short i,k,count = 0;
  term u,temp;
  unsigned n = ARITY(t);
  long degree[6],xdegree,ydegree;
  for(i=0;i<n;i++)
     { u = ARG(i,t);
       if(constant(u))
          { degree[i] = 0;
            continue;
          }
       if(!contains(u,FUNCTOR(x)))
          { err = monomial_form(u,y,&degree[i],&temp);
            if(err)
               return 1;
            if(degree[i] == 2)
                ++count;
          }
       else if(!contains(u,FUNCTOR(y)))
          { err = monomial_form(u,x,&degree[i],&temp);
            if(err)
               return 1;
            if(degree[i] == 2)
               ++count;
          }
       else  /* u contains both x and y */
          { err = monomial_form(u,x,&xdegree,&temp);
            if(err)
               return 1;
            err = monomial_form(temp,y,&ydegree,&temp);
            if(err)
               return 1;
            degree[i] = xdegree+ydegree;
            if(degree[i] == 2)
               ++count;
          }
     }
  if(count != 2 && count != 3)
     return 2;
  if(n-count != 2 && n-count != 3)
     return 2;
             /* Now group the quadratic terms together */
  *ans = make_term('+',count);
  k=0;
  for(i=0;i<n;i++)
     { if(degree[i] == 2)
          { ARGREP(*ans,k,ARG(i,t));
            ++k;
          }
     }
  assert(k==count);
  return 0;
}
/*__________________________________________________________________*/
static int xgroup(term t, term x, term *ans)
/* t is an mvpoly of arity 4 or more, in two variables x and y */
/* return in *ans a group of the terms containing x, if there
are two or more of them. Return 0 for success, 1 for failure  */
{ int i;
  term u;
  unsigned short n = ARITY(t);
  unsigned short k=0;
  *ans = make_term('+',n);
  for(i=0;i<n;i++)
    { u = ARG(i,t);
      if(contains(u,FUNCTOR(x)))
          { ARGREP(*ans,k,u);
            ++k;
          }
    }
  if(k<2)
     { RELEASE(*ans);
       return 1;
     }
  SETFUNCTOR(*ans,'+',k);
  return 0;
}

/*_______________________________________________________________________*/
int contentgcd(term t, term arg, term *next, char *reason)
/* t is a sum, each of whose summands contains_monomially a sum */
/* It is used only in automode, on examples like 2(x^2-1) + 3(x^2 + 2x + 1) */
/* It works only on sums of arity 2 */
/* Calculate the polygcd of these summands as in cancelgcd */
/* It could be made more general, but it's only meant to handle a few
   examples, and can't be used in menu mode anyway, so its
   shortcomings won't be important:  all problems which would be in
   books that can be handled this way can also be expanded and solved.
*/

{ unsigned n = ARITY(t);
  int err;
  term a,b,u,v;
  term uc, us, vc, vs;
  if(FUNCTOR(t) != '+')
     return 1;
  if(n != 2)
     return 1;   /* failure; too much work to program for n > 2 */
  u = ARG(0,t);
  v = ARG(1,t);
  if(FUNCTOR(u) == '*')
      ratpart2(u,&uc,&us);
  else
     { uc = one;
       us = u;
     }
  if(FUNCTOR(v) == '*')
      ratpart2(v,&vc,&vs);
  else
     { vc = one;
       vs = v;
     }
  err = cancelgcd_aux(us,vs,&a,&b);
  if(err)
     return 1;
  *next = sum(product(uc,a),product(vc,b));
  HIGHLIGHT(*next);
  strcpy(reason, "factor");
  return 0;
}

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