/*
Test univariate (vpoly) factorization over Z and Algebraic Number Fields.
AKS 27/2/95, 7/8/01.
*/

load "galpols";

VERB := true;
VERB := false;
VERB2 := false;

Z := IntegerRing();
Q := RationalField();

/*******************************************************************************
			    Test factorization over Z
*******************************************************************************/

test_fact_set := procedure(Q)
    S := { PrimitivePart(f): f in Q };
    P := &*S;
    if VERB then
	"";
	"S:", S;
	"P:", P;
    end if;
    PF := Factorization(P);
    if { t[1]: t in PF } ne S then
	S, "FAILS";
	"Factorization:", PF;
	error "";
    end if;
end procedure;

procedure test_fact_Q(F, Q)
    if &*[t[1]^t[2]: t in Q] ne F then
	F, "FAILS";
	"Factorization:", Q;
	error "";
    end if;
    for i := 1 to #Q do
	if not IsIrreducible(Q[i, 1]) then 
	    F, "FAILS";
	    printf "Factor %o reducible\n", i;
	    error "";
	end if;
    end for;
end procedure;

test_fact := procedure(F: Al := "Default")
    if VERB then
	"";
	"F:", F;
    end if;
    F := Normalize(F);
    Q := Factorization(F: Al := Al);
    if VERB then
	"Factorization:", Q;
    end if;
    test_fact_Q(F, Q);

    if Type(BaseRing(Parent(F))) in {RngInt, FldRat} then
	S := Random(10, 100);
	if VERB then "Use scale S:", S; end if;
	P<y> := PolynomialRing(RationalField());
	G := Normalize(Evaluate(F, y/S));
//"Here F:", F; "Here G:", G;
	GQ := Factorization(G);
	test_fact_Q(G, GQ);
	QQ := Sort(
	    [<Normalize(Evaluate(t[1], y/S)), t[2]>: t in Q | Degree(t[1]) gt 0]
	);
// "QQ:", QQ; "GQ:", GQ;
	assert QQ eq GQ;
    end if;
end procedure;

test_fact_anf := procedure(KK)
    "test_fact_anf:", KK;
    "Seed:", GetSeed();

    K<w> := KK;
    P<x> := PolynomialRing(K);

    test_fact(P ! Coefficients(DefiningPolynomial(K)));
    test_fact(x^2 - w);
    test_fact(x^5 - 1);
    test_fact(x^6 - 1);
    test_fact(&*[x^i - w: i in [1..5]]);
end procedure;

test_fact_anf(CyclotomicField(5));
test_fact_anf(CyclotomicField(3));

P<x> := PolynomialRing(IntegerRing());

test_fact_set(
    { j*x^2 + (i)^(j - 1)*x + i: i, j in [1..5] }
);

test_fact_set(
    { (i^j)*x^2 + (j)^(j - 1)*x + j^i: i, j in [1..5] }
);

test_fact_set(
    {
	55555555555555555555*x^3 + 1125899906842625*x + 11231,
	88888888888888888888*x^5 + 1048576*x + 1152921504606846977
    }
);

ran := func<k | Ranbig(k)>;

test_fact_set(
    { ran(50) * x^5 + ran(50) * x^4 + ran(50): i in [1..10] }
);

test_fact_set(
    { x^6 + ran(50) * x^5 + ran(50) * x^3 + ran(50): i in [1..10] }
);

test_fact_set(
    { x^i + i * x^j + 1: j in [2 .. i - 1], i in [1 .. 10] }
);

test_fact((x + 1)^100 - 1);
test_fact((2*x + 1)^50 - 1);
test_fact((3*x + 2)^20 - 2^20);
test_fact(x^100 + x + 1);
test_fact(x^200 + x^3 + 1);

test_fact_set({ x - 1, x - 50702946090, x + 50702946080});
test_fact_set({ x - 1, x - 50702946090, x + 50702946090});
test_fact_set(
    { x - 910150549564, 16*x - 167365831373057, 65473*x - 899480494396 }
);

/*******************************************************************************
			Test over finite fields
*******************************************************************************/

for q in
    [2 .. 100] cat
    [PreviousPrime(2^i): i in [8 .. 32]] cat
    [NextPrime(2^i): i in [8 .. 32]] cat
    [NextPrime(2^i)^2: i in [8 .. 32]]
do
    if not IsPrimePower(q) then
	continue;
    end if;

    K<w> := GF(q);
    if #K le 16 then
	dl := 1000;
    elif #K gt 2^30 then
	dl := 30;
    else
	dl := 100;
    end if;

    "Finite field test:", K;
    IndentPush();

    P<x> := PolynomialRing(K);

    for d in [dl, dl div 2 + 1, dl div 3 + 1, Random(dl)] do
	if d lt 1 then continue; end if;
	if VERB2 then "Cyclotomic -", d; end if;
	f := x^d - 1;
	test_fact(f);
	if VERB2 then "Cyclotomic +", d; end if;
	f := x^d + 1;
	test_fact(f);
    end for;

    for d in [Random(dl div 2, dl): j in [1 .. 3]] do
	"Degree", d;
	f := P![Random(K): i in [0 .. d]];
	time test_fact(f);

	ds := Min(10, d div 3);
	db := d div 2;
	f := &*(
	    //[RandomIrreduciblePolynomial(K, Random(1, ds)): i in [1 .. 5]] cat
	    [RandomIrreduciblePolynomial(K, ds): i in [1 .. 5]] cat
	    [RandomIrreduciblePolynomial(K, db): i in [1 .. 2]]
	);
	printf "Small (%o)/big (%o) -> degree %o\n", ds, db, Degree(f);
	time test_fact(f: Al := "GKS");
    end for;
    IndentPop();
end for;

/*******************************************************************************
			Test factorization over an ANF
*******************************************************************************/

test_fact_anf := procedure(KK)
    "test_fact_anf:", KK;
    "Seed:", GetSeed();

    K<w> := KK;
    P<x> := PolynomialRing(K);

    test_fact(P ! Coefficients(DefiningPolynomial(K)));
    test_fact(x^2 - w);
    test_fact(x^5 - 1);
    test_fact(x^6 - 1);
    test_fact(&*[x^i - w: i in [1..5]]);
end procedure;

P<x> := PolynomialRing(IntegerRing());

test_fact_anf(CyclotomicField(5));
test_fact_anf(CyclotomicField(3));
test_fact_anf(QuadraticField(-1));
test_fact_anf(QuadraticField(5));
test_fact_anf(QuadraticField(-21));
test_fact_anf(NumberField(x^2 + x + 1));
test_fact_anf(NumberField(x^3 + 2));
test_fact_anf(NumberField(x^5 + x - 3));

test_fact_anf(CyclotomicField(12));
test_fact_anf(CyclotomicField(20));
test_fact_anf(CyclotomicField(24));
time test_fact_anf(NumberField(PolynomialWithGaloisGroup(12, 50)));
time test_fact_anf(NumberField(PolynomialWithGaloisGroup(15, 5)));

/*******************************************************************************
			    Misc checks
*******************************************************************************/

P<x> := PolynomialRing(IntegerRing());
f := -1047568*x^3 + 11911402060081731649*x^2
    - 9973541554970288316120870153080*x
    + 137016157758816972609784907691499182608;
L := Factorization(f);
assert L eq 
    [
	<x - 910150549564, 1>,
	<16*x - 167365831373057, 1>,
	<65473*x - 899480494396, 1>
    ];


f := x^16 - 12*x^14 + 38*x^12 + 36*x^10 - 59*x^8 - 1320*x^6 + 5028*x^4
    - 7200*x^2 + 22500;
g := x^16 - 16*x^14 + 162*x^12 - 1048*x^10 + 4741*x^8 - 14760*x^6 + 30972*x^4 -
    39600*x^2 + 22500;
K<w> := NumberField(f);
PK<z> := PolynomialRing(K);
L := Factorization(PK!g);
assert L eq 
    [
        <z + 1/64474200*(-2254*w^15 + 10509*w^13 + 121999*w^11 - 539598*w^9 - 
            2009755*w^7 + 5385033*w^5 + 11824602*w^3 - 16738650*w), 1>,
        <z + 1/64474200*(2254*w^15 - 10509*w^13 - 121999*w^11 + 539598*w^9 + 
            2009755*w^7 - 5385033*w^5 - 11824602*w^3 + 16738650*w), 1>,
        <z^2 + 1/21491400*(-3025*w^14 + 5081*w^12 + 159121*w^10 - 290909*w^8 - 
            2796286*w^6 - 2498962*w^4 + 16039944*w^2 - 50678400), 1>,
        <z^2 + 1/859656*(-121*w^14 + 905*w^12 + 49*w^10 - 12689*w^8 - 10798*w^6 
            - 39958*w^4 - 78408*w^2 - 1623624), 1>,
        <z^2 + 1/859656*(-121*w^14 + 1636*w^12 - 6530*w^10 + 4124*w^8 - 
            12991*w^6 + 327004*w^4 - 1258242*w^2 - 307824), 1>,
        <z^2 + 1/21491400*(-3025*w^14 + 58444*w^12 - 321146*w^10 + 76784*w^8 + 
            2201561*w^6 + 9675112*w^4 - 49456194*w^2 + 2392200), 1>,
        <z^2 + 1/21491400*(3025*w^14 - 58444*w^12 + 321146*w^10 - 76784*w^8 - 
            2201561*w^6 - 9675112*w^4 + 49456194*w^2 - 88357800), 1>,
        <z^2 + 1/859656*(121*w^14 - 1636*w^12 + 6530*w^10 - 4124*w^8 + 12991*w^6
            - 327004*w^4 + 1258242*w^2 - 3130800), 1>,
        <z^2 + 1/21491400*(3025*w^14 - 5081*w^12 - 159121*w^10 + 290909*w^8 + 
            2796286*w^6 + 2498962*w^4 - 16039944*w^2 - 35287200), 1>
    ];

L2 := Factorisation(PK!f);
assert L2 eq
    [
        <z - w, 1>,
        <z + w, 1>,
        <z^2 + w^2 - 3, 1>,
        <z^2 + 1/2686425*(-1384*w^14 + 14532*w^12 - 26408*w^10 - 128910*w^8 - 
            118288*w^6 + 2281032*w^4 - 3162201*w^2 - 3308175), 1>,
        <z^2 + 1/644742*(-242*w^14 + 2541*w^12 - 6481*w^10 - 8565*w^8 - 
            23789*w^6 + 287046*w^4 - 691908*w^2 - 427050), 1>,
        <z^2 + 1/328950*(-46*w^14 + 483*w^12 + 73*w^10 - 11415*w^8 - 2347*w^6 + 
            132858*w^4 + 294756*w^2 - 1174050), 1>,
        <z^2 + 1/328950*(46*w^14 - 483*w^12 - 73*w^10 + 11415*w^8 + 2347*w^6 - 
            132858*w^4 - 294756*w^2 + 187200), 1>,
        <z^2 + 1/644742*(242*w^14 - 2541*w^12 + 6481*w^10 + 8565*w^8 + 23789*w^6
            - 287046*w^4 + 691908*w^2 - 1507176), 1>,
        <z^2 + 1/2686425*(1384*w^14 - 14532*w^12 + 26408*w^10 + 128910*w^8 + 
            118288*w^6 - 2281032*w^4 + 3162201*w^2 - 4751100), 1>
    ];

K<w> := NumberField(g);
PK<z> := PolynomialRing(K);
L := Factorization(PK!f);
assert L eq 
    [
        <z + 1/1800*(-9*w^15 + 127*w^13 - 1254*w^11 + 7494*w^9 - 32155*w^7 + 
            89649*w^5 - 162942*w^3 + 139050*w), 1>,
        <z + 1/1800*(9*w^15 - 127*w^13 + 1254*w^11 - 7494*w^9 + 32155*w^7 - 
            89649*w^5 + 162942*w^3 - 139050*w), 1>,
        <z^2 + 1/36*(-w^14 + 13*w^12 - 120*w^10 + 658*w^8 - 2521*w^6 + 6207*w^4 
            - 9798*w^2 + 7542), 1>,
        <z^2 + 1/900*(-25*w^14 + 327*w^12 - 3024*w^10 + 16678*w^8 - 64209*w^6 + 
            159621*w^4 - 254286*w^2 + 198450), 1>,
        <z^2 + 1/900*(-25*w^14 + 373*w^12 - 3576*w^10 + 21322*w^8 - 86641*w^6 + 
            227079*w^4 - 368214*w^2 + 268650), 1>,
        <z^2 + 1/36*(-w^14 + 15*w^12 - 144*w^10 + 862*w^8 - 3513*w^6 + 9261*w^4 
            - 15102*w^2 + 11142), 1>,
        <z^2 + 1/900*(25*w^14 - 373*w^12 + 3576*w^10 - 21322*w^8 + 86641*w^6 - 
            227079*w^4 + 368214*w^2 - 271350), 1>,
        <z^2 + 1/900*(25*w^14 - 327*w^12 + 3024*w^10 - 16678*w^8 + 64209*w^6 - 
            159621*w^4 + 254286*w^2 - 201150), 1>,
        <z^2 + 1/36*(w^14 - 13*w^12 + 120*w^10 - 658*w^8 + 2521*w^6 - 6207*w^4 +
            9798*w^2 - 7650), 1>
    ];

L2 := Factorisation(PK!g);
assert L2 eq 
    [
        <z - w, 1>,
        <z + w, 1>,
        <z^2 + w^2 - 4, 1>,
        <z^2 + 1/600*(-37*w^14 + 518*w^12 - 4956*w^10 + 28840*w^8 - 117509*w^6 +
            309918*w^4 - 521682*w^2 + 411300), 1>,
        <z^2 + 1/24*(-w^14 + 14*w^12 - 132*w^10 + 760*w^8 - 3017*w^6 + 7734*w^4 
            - 12426*w^2 + 9300), 1>,
        <z^2 + 1/50*(-w^14 + 14*w^12 - 138*w^10 + 820*w^8 - 3507*w^6 + 9714*w^4 
            - 17536*w^2 + 14700), 1>,
        <z^2 + 1/50*(w^14 - 14*w^12 + 138*w^10 - 820*w^8 + 3507*w^6 - 9714*w^4 +
            17536*w^2 - 14900), 1>,
        <z^2 + 1/24*(w^14 - 14*w^12 + 132*w^10 - 760*w^8 + 3017*w^6 - 7734*w^4 +
            12426*w^2 - 9396), 1>,
        <z^2 + 1/600*(37*w^14 - 518*w^12 + 4956*w^10 - 28840*w^8 + 117509*w^6 - 
            309918*w^4 + 521682*w^2 - 413700), 1>
    ];


F:=GF(9);
R<x>:=PolynomialRing(F);
f:=R![ F.1^7, F.1^6, F.1^5, F.1^6, F.1^7, F.1^5, 0, 0, 1 ];
m:=R![2,1,1,1];
assert IsIrreducible(m);
S,pi:=quo<R|m>;
assert IsField(S) and IsFinite(S);
p:=pi(f);
PP<z>:=PolynomialRing(S);
L:=Factorization(z^2-p);
assert #L eq 2;
assert L[1,1]*L[2,1] eq z^2-p;
assert Sqrt(p)^2 eq p;

// Aug 17

P<x> := PolynomialRing(IntegerRing());
L := Factorization(3*x);
assert IsIrreducible(L[1][1]);
assert IsIrreducible(L[2][1]);
assert IsIrreducible(x);
assert not IsIrreducible(P!4);
assert not IsIrreducible(L[1][1]^2);
assert not IsIrreducible(L[1][1]*2);
assert not IsIrreducible(x^2);

P<x> := PolynomialRing(RationalField());
L := Factorization(3*x);
assert IsIrreducible(x);
assert IsIrreducible(2*x);
assert not IsIrreducible(x^2);
assert not IsIrreducible(P!3); // unit

// Nov 17, with DegreeLimit

//E := EllipticCurve("3360q3");
E := EllipticCurve([0, -1, 0, -102060000, 396888659352]);
K<w>:=NumberField(Polynomial([ 42, 0, -14, 0, 1 ]));
P := DivisionPolynomial(E,8);
PK := ChangeRing(P,K);
_<x> := Parent(PK);
Roots(PK);
assert $1 eq [
    <5832, 1>,
    <5833, 1>,
    <5834, 1>,
    <-20*w^3 + 50*w^2 + 150*w + 5484, 1>,
    <20*w^3 + 50*w^2 - 150*w + 5484, 1>
];
Factorization(PK: DegreeLimit := 1);
assert $1 eq [
    <x - 5834, 1>,
    <x - 5833, 1>,
    <x - 5832, 1>,
    <x - 20*w^3 - 50*w^2 + 150*w - 5484, 1>,
    <x + 20*w^3 - 50*w^2 - 150*w - 5484, 1>
];
Factorization(PK: DegreeLimit := 2);
assert $1 eq [
    <x - 5834, 1>,
    <x - 5833, 1>,
    <x - 5832, 1>,
    <x - 20*w^3 - 50*w^2 + 150*w - 5484, 1>,
    <x + 20*w^3 - 50*w^2 - 150*w - 5484, 1>,
    <x^2 + 5832*x - 68041944, 1>,
    <x^2 + (100*w^2 - 12368)*x - 583300*w^2 + 38118656, 1>
];

assert #Factorization(PK) eq 10;

// Oct 2019

//SetVerbose("PolyFact",1);
P<y> := PolynomialRing(Q);
p1:=
  y^18 - 354*y^17 + 162406/3*y^16 - 43225088/9*y^15 + 22959342160/81*y^14 -
    2997897228832/243*y^13 + 2876192264161216/6561*y^12 -
    91112909215933568/6561*y^11 + 24007223192592574112/59049*y^10 -
    1908025049263147405120/177147*y^9 + 393610775078822022727616/1594323*y^8 -
    22346216518846675484690432/4782969*y^7 +
    9029028784518687477590797312/129140163*y^6 -
    103066786920263119122642936832/129140163*y^5 +
    7851706155458603524655936826880/1162261467*y^4 -
    143920013207422265507745577766912/3486784401*y^3 +
    5465395062857334887299076841179392/31381059609*y^2 -
    43144557948179619134207461913210368/94143178827*y +
    4318442162712327282465178685635245568/7625597484987;

p2 := Evaluate(p1, -y);
for f in <p1, p2> do
    for g in <p1, p2> do
	h := ChangeRing(p1, NumberField(p2));
	time L := Factorization(h);
	assert &*[t[1]^t[2]: t in L] eq h;
    end for;
end for;

// Sep 2021

P<x> := PolynomialRing(Z);
f := 19044*x^12 - 9320447952072660*x^6 + 1142025375387986641604536225;
assert IsIrreducible(f);

