%mathpiper,title="jFactorsPoly"

/* -------------    POLYNOMIALS -- CAN BE UNI   --------------*/

50 # jFactorsPoly( poly_CanBeUni ) <--
[
    If(InVerboseMode(),Tell("jFactorsPoly_100612",poly));
    Local(content,pp,ppFactors,monomialFactors,result,moreDetails);
    Local(vars,nvars,nterms);
    Local(isHomogeneous);
    Local(extraFactor);
    
    moreDetails := False;

    If(HasRealCoefficients(poly), poly := RealToRationalConvert(poly));
    If(HasRationalCoefficients(poly), poly := RationalToIntegerConvert(poly));

    // Now: get Content and Primitive Part
    content := xContent( poly );
    pp      := Simplify( poly / content );
    If(IsBound(extraFactor),content := content / extraFactor);
    If(InVerboseMode(),Tell("   ",{content,pp}));
    //If(IsConstant(pp) And Length(VarList(content))>0,{content,pp}:={pp,content});
    If( Length(VarList(pp))=0, result := {{pp*content,1}},
      [
         Local(disassem);
         //If(InVerboseMode(),Tell("  ",{content,pp}));
         vars     := VarList(pp);
         nvars    := Length(vars);
         disassem := DisassembleExpression(pp);
         nterms   := Length(disassem[3]);

         If(nvars > 0,
           [
             isHomogeneous     := [
                 // A polynomial is homogeneous of degree n 
                 // if all terms have degree n.   
                 Local(sd,cmp); 
                 sd  := Sum /@ disassem[2];
                 cmp := FillList(sd[1],Length(sd));
                 IsZeroVector(sd - cmp);
             ];
           ]
         );
    
         If(InVerboseMode() And moreDetails,
           [
             Local(degrees, allCoeffs, allPowers);

             degrees := {};
             ForEach(v,vars) [ DestructiveAppend(degrees,Degree(pp,v)); ];
             allCoeffs := disassem[3];
             allPowers := Flatten(disassem[2],"List");

             Tell("               ",vars);
             Tell("              ",nvars);
             Tell("             ",nterms);
             Tell("            ",degrees);
             Tell("           ",disassem);
             Tell("          ",allCoeffs);
             Tell("          ",allPowers);
             Tell("      ",isHomogeneous);
             NewLine();
           ]
         );
    
         //  Does the Content have factors?  If so, get them.
         //If(InVerboseMode(),NewLine());
         monomialFactors := FactorsMonomial(content);
         If(InVerboseMode(),Tell("  ",monomialFactors));
    
         //  OK.  Now factor the PrimitivePart
         ppFactors := jFactorsPrimitivePart( pp );
         If(InVerboseMode(),Tell("                    ",ppFactors));
         If( Not IsListOfLists(ppFactors), 
           [
               Local(L,op,var,exp);
               L := If(IsAtom(ppFactors[1]), ppFactors, FunctionToList(ppFactors[1]) );
               If(InVerboseMode(),Tell("                    ",L));
               If( L[1] = ^, ppFactors := {L[2],L[3]} );
               ppFactors := {ppFactors};
           ]
         );
         If(InVerboseMode(),Tell("  ",ppFactors));
    
         // Next, include the factors of the Content, if any
         If( monomialFactors[1][1] = 1,
             result := ppFactors,        
             result := Concat(monomialFactors,ppFactors)      // hso 100803
         );
      ]
    );
    
    If(InVerboseMode(),
      [
          NewLine();
          Tell("  ",monomialFactors);
          Tell("        ",ppFactors);
          Tell("  final    ",result);
      ]
    );
    result;
];
UnFence("jFactorsPoly",1);



// -----------------  FACTOR PRIMITIVE PART  -----------------

     //  special case: binomials
60 # jFactorsPrimitivePart( _pp )_(isHomogeneous And nterms=2 And nvars=2) <--
[
    If(InVerboseMode(),Tell("Bivariate Binomial",pp));
    Local(ppFactors,isDiagonal);
    isDiagonal := IsDiagonal(disassem[2]);   //  mod hso 10-11-25
    ppFactors := If(isDiagonal,jFactorsBivariateBinomial(pp),jasFactorsInt(pp) );
];
UnFence("jFactorsPrimitivePart",1);


     //  special case: homogeneous bivariates
65 # jFactorsPrimitivePart( _pp )_(isHomogeneous And nterms>1 And nvars=2) <--
[
    If(InVerboseMode(),Tell("Homogeneous and Bivariate"));
    Local(ppFactors);
    ppFactors := jFactorsHomogeneousBivariate(disassem);
];
UnFence("jFactorsPrimitivePart",1);


     //  special case:  no variables in pp!
70 # jFactorsPrimitivePart( _pp )_(nvars=0) <--
[
    Local(ppFactors);
    ppfactors := {};
];


     //  general case
100 # jFactorsPrimitivePart( _pp ) <-- 
[
    If(InVerboseMode(),Tell("jFactorsPrimitivePart_usingJAS",pp));
    Local(answer);
    answer := If(IsMonomial(pp),{pp,1},jasFactorsInt(pp));
    If(InVerboseMode(),Tell("          ",answer));
    answer;
];
UnFence("jFactorsPrimitivePart",1);

// ------------------  HOMOGENEOUS BIVARIATE  ------------------

10 # jFactorsHomogeneousBivariate( dis_IsList ) <-- 
[
    If(InVerboseMode(),[NewLine();Tell("jFactorsHomogeneousBivariate",dis);]);
    Local(dis1,f,dis2,poly1,ppFactors,residuals);
    Local(ii,lst,f,preassem);
    dis1  := {{xi},{{X},{X[1]}} /@ dis[2],dis[3]};
    If(InVerboseMode(),Tell("   ",dis1));
    poly1 := Sum(ReassembleListTerms(dis1));
    If(InVerboseMode(),Tell("   ",poly1));
    ppFactors := BinaryFactors(poly1);
    {ppFactors,residuals} := FixUpMonicFactors(ppFactors);
    For(ii:=1,ii<=Length(ppFactors),ii++)
    [
        f := ppFactors[ii];
        If(InVerboseMode(),Tell("          ",f[1]));
        lst := DisassembleExpression(f[1]);
        If(InVerboseMode(),
          [
             Tell("               ",lst);
             Tell("                            ",dis[1]);
          ]
        );
        DestructiveReplace(lst,1,dis[1]);
        DestructiveAppend(lst[2][1],0);
        DestructiveAppend(lst[2][2],1);
        If(Length(lst[2])=3, DestructiveAppend(lst[2][3],2));
        If(InVerboseMode(),Tell("               ",lst));
        preassem  := Sum(ReassembleListTerms(lst)) ;
        If(InVerboseMode(),Tell("               ",preassem));
        ppFactors[ii][1] := preassem;
    ];
    If(InVerboseMode(),[Tell("   ",ppFactors); Tell("   ",residuals);NewLine();] );  
    ppFactors;
];
UnFence("jFactorsHomogeneousBivariate",1);

// ---------------------   OTHER STUFF ------------------------

10 # RealToRationalConvert( poly_IsPolynomial ) <--
[
    // If the polynomial has REAL coefficients, convert them to
    // approximate RATIONALS
    If(InVerboseMode(),[NewLine();Tell("  REAL",poly);]);
    Local(disassem,coeffs);
    disassem := DisassembleExpression(poly);
    coeffs := Rationalize /@ disassem[3];
    If(InVerboseMode(), [
        Local(gcd,lcm);
        gcd    := Gcd(Numerator /@ coeffs);
        lcm    := Lcm(Denominator /@ coeffs);
        Tell("      to-Q",coeffs);
        Tell("       ",gcd);
        Tell("       ",lcm);
    ]);
    disassem[3] := coeffs;
    poly        := Sum(ReassembleListTerms(disassem));
    If(InVerboseMode(),Tell("   new",poly));
    poly;
];
UnFence("RealToRationalConvert",1);


10 # RationalToIntegerConvert( poly_IsPolynomial ) <--
[
    // If the polynomial has RATIONAL coefficients, convert to
    // approximate INTEGER
    Local(coeffs,gcd,lcm);
    coeffs := DisassembleExpression(poly)[3];
    If(InVerboseMode(),Tell("  ",coeffs));
    lcm         := Lcm(Denominator /@ coeffs);
    extraFactor := lcm;
    If(InVerboseMode(),[Tell("     ",extraFactor);]);
    poly := Simplify(extraFactor*poly);
    If(InVerboseMode(),Tell("  new ",poly));
    poly;
];
UnFence("RationalToIntegerConvert",1);


100 # CombineNumericalFactors( factrs_IsList ) <--
[
      If( InVerboseMode(), Tell("Combine",factrs) );
      Local(q,a,b,t,f,ff,err);
      err := False;
      t   := 1;
      f   := {};
      ForEach(q,factrs)
      [
          If( InVerboseMode(), Tell(1,q) );
          If( IsList(q) And Length(q)=2,
            [
                {a,b} := q;
                If( InVerboseMode(), Echo("     ",{a,b}) );
                If( IsNumericList( {a,b} ),
                    t := t * a^b,
                    f := {a,b}:f
                );
            ],
              err := True
          );
      ];
      If( InVerboseMode(),
        [
            Echo("      t = ",t);
            Echo("      f = ",f);
            Echo("    err = ",err);
        ]
      );
      ff := If(Not err And t != 1, {t,1}:Reverse(f), factrs);
      ff := Select(Lambda({x},x!={1,1}),ff);
      If(ff[1]<0,ff[1]:=-ff[1]);
];


// ----------------  RATIONAL POLYNOMIALS  -----------------

150 # jFactors( expr_IsRationalFunction )_
        (IsPolynomial(Numerator(expr)) And IsPolynomial(Denominator(expr))) <--
[
    If(InVerboseMode(),[NewLine();Tell("jFactors_Rational_Function",expr);]);
    Local(Numer,Denom,fNumer,fDenom);
    Numer  := Numerator(expr);
    Denom  := Denominator(expr);
    fNumer := jFactors(Numer);
    fDenom := jFactors(Denom);
    If(InVerboseMode(),[Tell("   ",fNumer); Tell("   ",fDenom);]);
    fNumer/fDenom;
];


152 # jFactors( expr_IsRationalFunction )_
        (IsConstant(Numerator(expr)) And IsPolynomial(Denominator(expr))) <--
[
    If(InVerboseMode(),[NewLine();Tell("jFactors_Rational_Denom",expr);]);
    Local(Numer,Denom,fNumer,fDenom);
    Numer  := Numerator(expr);
    Denom  := Denominator(expr);
    fNumer := jFactors(Numer);
    fDenom := jFactors(Denom);
    If(InVerboseMode(),[Tell("   ",fNumer); Tell("   ",fDenom);]);
    fNumer/fDenom;
];


// ----------   POSSIBLE NON-INTEGER EXPONENTS  ----------

200 # jFactors( _expr )_(Length(VarList(expr)) = 1) <--
[
    If(InVerboseMode(),[NewLine();Tell("Some other kind of expression",expr);]);
    Local(dis,X,pows);
    dis := DisassembleExpression(expr);
    X   := VarList(expr)[1];
    pows := matchPower /@ dis[1];
    rats := NearRational /@ pows;
    dis[1] := x^rats;
    p := Sum(ReassembleListTerms(dis));
    If(InVerboseMode(),Tell("    new ",p));
    jFactors(p);
];



/*-------------------------------------------------------------------
 *  Factoring Binomial expressions of the form  A X^n � B Y^n,
 *     Uses JAS library, but converts to univariate equivalent
 *     before factoring. (JAS is inefficient for multivariate
 *     factoring when degree is large.)
 *-------------------------------------------------------------------*/

10 # jFactorsBivariateBinomial( poly_IsPolynomial )_(Length(VarList(poly))=2)  <-- 
[
    If(InVerboseMode(),Tell(jFactorsBivariateBinomial,poly));
    Local(dis,n,X,Y,vars,A,B,s,Ar,Br,Arr,Brr,DAr,DBr,result);
    dis := DisassembleExpression(poly);
    If(InVerboseMode(),Tell("   ",dis));
    n     := Maximum(dis[2])[1];
    X     := dis[1][1];
    Y     := dis[1][2];
    vars  := dis[1];
    A     := Abs(dis[3][1]);
    B     := Abs(dis[3][2]);
    s     := Sign(dis[3][1]*dis[3][2]);
    //Ar    := NearRational(N(A^(1/n)));
    //Br    := NearRational(N(B^(1/n)));
    Ar    := N(A^(1/n));    Arr := Round(Ar);   DAr := Abs(Ar-Arr);
    Br    := N(B^(1/n));    Brr := Round(Br);   DBr := Abs(Br-Brr);
    If(InVerboseMode(),
      [
          Tell("       ",{n,X,Y}); 
          Tell("       ",{vars,A,B});
          Tell("       ",{Ar,Br,s});
          Tell("       ",{Arr,Brr});
          Tell("       ",{DAr,DBr});
          Tell("       ",dis);
      ]
    );
    result := If( DAr < 10^(-9) And DBr < 10^(-9), jFB(dis), {{poly,1}} );
    result;
];
UnFence("jFactorsBivariateBinomial",1);


50 # jFB( dis_IsList )_(Length(dis)=3 And Length(dis[3])=2) <--
[
    If(InVerboseMode(),[NewLine();Tell("jFB",dis);]);
    Local(ns,ii,fn,mx,my,fac);
    If(InVerboseMode(),
      [
         Tell("     ",n);
         Tell("     ",{X,Y});
         Tell("     ",{A,B,s});
         Tell("     ",{Ar,Br});
      ]
    );
    X     := Arr*X;
    Y     := Brr*Y;
    If(InVerboseMode(),Tell("     ",{X,Y}));
    
    fac  := jFac( X/Y,n,s);    //  factor using JAS and normalized variable
    If(InVerboseMode(),
      [
          NewLine();
          Tell("      ",X/Y);
          Tell("      ",fac);
      ]
    );
    
    // now convert factorization back to actual variables if required
    If( Y != 1,
      [
          Local(f,d,fs);
          For(ii:=1,ii<=Length(fac),ii++)
          [
              f := fac[ii][1];
              d := Degree(f,x);
              If(InVerboseMode(),Tell("             ",{ii,f,d}));
              fs := Subst(x,X/Y) f;
              If(InVerboseMode(),Tell("               ",{fs,d}));
              fac[ii][1] := Simplify(Simplify(Y^d*fs));
          ];
      ]
    );
    fac;
];
UnFence("jFB",1);



60 # jFac( _var, n_IsPositiveInteger, s_IsInteger ) <--
[
    //  Uses JAS to factor polynomial of form x^n � 1.
    If(InVerboseMode(),[NewLine();Tell("jFac",{var,n,s});]);
    Local(x,poly,result);
    poly := x^n+s;
    If(InVerboseMode(),Tell("   ",poly));
    result := jasFactorsInt(poly);     
];
UnFence("jFac",3);


10 # IsPureRational( N_IsRational )_(Not IsInteger(N)) <-- True;

12 # IsPureRational( _N ) <-- False;

10 # HasRealCoefficients( poly_IsPolynomial ) <--
[
    Local(disassem);
    disassem := DisassembleExpression(poly);
    (Length(Select(disassem[3],"IsDecimal")) > 0);
];

10 # HasRealCoefficients( poly_IsMonomial ) <--
[
    Local(disassem);
    disassem := DisassembleExpression(poly);
    (Length(Select(disassem[3],"IsDecimal")) > 0);
];


10 # HasRationalCoefficients( poly_IsPolynomial ) <--
[
    Local(disassem,answer);
    If(InVerboseMode(),Tell("  HasRationalCoefficients",poly));
    disassem := DisassembleExpression(poly);
    //Tell("              ",disassem);
    answer := (Length(Select(disassem[3],"IsPureRational")) > 0);
    If(InVerboseMode(),Tell("   ",answer));
    answer;
];

10 # HasRationalCoefficients( poly_IsMonomial) <--
[
    Local(disassem);
    disassem := DisassembleExpression(poly);
    (Length(Select(disassem[3],"IsPureRational")) > 0);
];


10 # FixUpMonicFactors( factrs_IsList ) <--
[
    If(InVerboseMode(),[ NewLine(); Tell("   doing monic fixup"); ] );
    Local(factrsnew,residuals,f,uni,);
    factrsnew := {};
    residuals := {};
    ForEach(f,factrs)
    [
        If(InVerboseMode(),Tell("               ",f));
        uni := MakeUni(f[1]);
        If(InVerboseMode(),Tell("                    ",uni));
        If( Degree(f[1])=1,
          [
            Local(cc,lcm,fnew);
            If(InVerboseMode(),Tell("                          ",Degree(f[1])));
            cc      := Coef(f[1],uni[1],0 .. 1);
            //Tell("                       ",cc);
            lcm     := Lcm( Denominator /@ cc );
            uni[3]  := lcm * cc;
            fnew    := NormalForm(uni);
            DestructiveAppend(factrsnew,{fnew,f[2]});
          ]
        );
        If( Degree(f[1])=2,
          [
            If(InVerboseMode(),Tell("                          ",Degree(f[1])));
            Local(pq);
            pq      := PrimitivePart(f[1]);
            DestructiveAppend(factrsnew,{pq,f[2]});
          ]
        );
        //  If any factors have degree >=3, store them in a 'residuals' array
        //  for further analysis
        If( Degree(f[1]) > 2,
          [
            If(InVerboseMode(),Tell("                          ",Degree(f[1])));
            Local(pq);
            pq      := PrimitivePart(f[1]);
            DestructiveAppend(residuals,{pq,f[2]});
            If(InVerboseMode(),Tell("                   appending to residuals",pq));
          ]
        );       
    ];
    {factrsnew,residuals};
];
UnFence("FixUpMonicFactors",1);


//10 # matchPower(_Z^n_IsNumber) <-- n;

//15 # matchPower(_Z) <-- 1;


//========================================================================

%/mathpiper

    %output,preserve="false"
      Result: True
.   %/output





