SetEchoInput(true);

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

Zp := pAdicRing(7, 50);
U := UnramifiedExtension(Zp, x^2 + 6*x + 3);
R := TotallyRamifiedExtension(U, x^3 + 7*x^2 + 7*x + 7);

Qp := FieldOfFractions(Zp);
QU := FieldOfFractions(U);
QR := FieldOfFractions(R);

assert Valuation(Evaluate(DefiningPolynomial(R), R.1)) ge Precision(R);
assert Valuation(Evaluate(DefiningPolynomial(U), U.1)) ge Precision(U);
assert Valuation(Evaluate(DefiningPolynomial(QR), QR.1)) ge Precision(QR);
assert Valuation(Evaluate(DefiningPolynomial(QU), QU.1)) ge Precision(QU);

assert RamificationDegree(R) eq 3;
assert InertiaDegree(R) eq 1;
assert RamificationDegree(U) eq 1;
assert InertiaDegree(U) eq 2;

assert Valuation(UniformizingElement(R)) eq 1;
assert Valuation(UniformizingElement(U)) eq 1;
assert Valuation(UniformizingElement(QR)) eq 1;
assert Valuation(UniformizingElement(QU)) eq 1;

assert Precision(U) eq Precision(Zp);
assert Precision(R) eq 3*Precision(Zp);
assert Prime(U) eq Prime(CoefficientRing(U));

rf, rfm := ResidueClassField(U);
assert Valuation(rfm(U.1) @@ rfm - U.1) ge 1;

for i in [1 .. 5] do

    r := &+[Random(0, 500)*R.1^i : i in [0 .. 2*Degree(R)]];
    s := &+[Random(0, 300)*R.1^i : i in [0 .. 2*Degree(R)]];

    assert r + s eq s + r;
    assert Valuation(r + s) ge Minimum(Valuation(r), Valuation(s));
    assert r - s eq -(s - r);
    assert r*s eq s*r;
    assert Valuation(r*s) eq Valuation(r) + Valuation(s);
    //assert (r div s)*r + r mod s eq s; // eq 1/(s div r);
    assert IsWeaklyEqual(r/s*(s/r), 1);
    assert RelativePrecision((r/s)*(s/r) - 1) eq 0;//Precision(L);
    assert r^4 eq r*r*r*r;

    assert IsWeaklyZero(r + (-r));

    assert IsWeaklyZero(Evaluate(MinimalPolynomial(r), r));
    assert IsWeaklyZero(Evaluate(MinimalPolynomial(s), s));
    assert IsWeaklyZero(Evaluate(CharacteristicPolynomial(r), r));
    assert IsWeaklyZero(Evaluate(CharacteristicPolynomial(s), s));
end for;

R!0/5;
QR!0/5;

for i in [1 .. 5] do

    r := &+[Random(0, 500)*QR.1^i : i in [0 .. 2*Degree(QR)]];
    s := &+[Random(0, 300)*QR.1^i : i in [0 .. 2*Degree(QR)]];

    assert r + s eq s + r;
    assert Valuation(r + s) ge Minimum(Valuation(r), Valuation(s));
    assert r - s eq -(s - r);
    assert r*s eq s*r;
    assert Valuation(r*s) eq Valuation(r) + Valuation(s);
    assert r div s eq 1/(s div r);
    assert RelativePrecision((r/s)*(s/r) - 1) eq 0;//Precision(L);
    assert r^4 eq r*r*r*r;

    assert IsWeaklyZero(r + (-r));
    assert IsWeaklyZero(Evaluate(MinimalPolynomial(r), r));
    assert IsWeaklyZero(Evaluate(MinimalPolynomial(s), s));
end for;

assert IsOne(R!1);
assert IsMinusOne(-R!1);
assert IsOne(U!1);
assert IsMinusOne(-U!1);
assert IsZero(R!0);
assert Valuation(R!0) ge Precision(R);
assert IsZero(U!0);
assert Valuation(U!0) ge Precision(U);
assert IsOne(QR!1);
assert IsMinusOne(-QR!1);
assert IsOne(QU!1);
assert IsMinusOne(-QU!1);
assert IsZero(QR!0);
assert Valuation(QR!0) ge Precision(QR);
assert IsZero(QU!0);
assert Valuation(QU!0) ge Precision(QU);

assert IsIntegral(U.1);
assert IsIntegral(R.1);
assert IsIntegral(UniformizingElement(R));
assert not IsIntegral(1/UniformizingElement(R));

assert IsIntegral(QU.1);
assert IsIntegral(QR.1);
assert IsIntegral(UniformizingElement(QR));
assert not IsIntegral(1/UniformizingElement(QR));

A, am := AutomorphismGroup(R);
autos := [am(a) : a in A];
autos_inv := [am(a^-1) : a in A];
assert Minimum([Valuation(R.1 @ autos[i] @ autos_inv[i] - R.1) : i in [1 .. #autos]]) ge 0.95*Precision(R);

A, am := AutomorphismGroup(U);
autos := [am(a) : a in A];
autos_inv := [am(a^-1) : a in A];
assert Minimum([Valuation(U.1 @ autos[i] @ autos_inv[i] - U.1) : i in [1 .. #autos]]) ge 0.95*Precision(U);

A, am := AutomorphismGroup(QR);
autos := [am(a) : a in A];
autos_inv := [am(a^-1) : a in A];
assert Minimum([Valuation(QR.1 @ autos[i] @ autos_inv[i] - QR.1) : i in [1 .. #autos]]) ge 0.95*Precision(R);

A, am := AutomorphismGroup(QU);
autos := [am(a) : a in A];
autos_inv := [am(a^-1) : a in A];
assert Minimum([Valuation(QU.1 @ autos[i] @ autos_inv[i] - QU.1) : i in [1 .. #autos]]) ge 0.95*Precision(U);

assert RelativePrecision(Discriminant(R) - -3724) eq 0;
assert RelativePrecision(Discriminant(U) - 24) eq 0;
assert Valuation(Discriminant(R, Zp)) eq 4;
assert RelativePrecision(Discriminant(R, Zp) - 191713665024) eq 0;
assert RelativePrecision(Discriminant(QR, QU) - -3724) eq 0;
assert RelativePrecision(Discriminant(QU, Qp) - 24) eq 0;
assert Valuation(Discriminant(QR, Qp)) eq 4;
assert RelativePrecision(Discriminant(QR, Qp) - 191713665024) eq 0;

assert IsRamified(R);
assert IsRamified(QR);
assert not IsUnramified(R);
assert not IsUnramified(QR);
assert IsTotallyRamified(R);
assert IsTotallyRamified(QR);
assert IsWildlyRamified(R) xor IsTamelyRamified(R);
assert IsWildlyRamified(QR) xor IsTamelyRamified(QR);

assert IsUnramified(U);
assert IsUnramified(QU);

f := FrobeniusAutomorphism(R);
fL := f(R.1);
i := 1;
while not IsWeaklyZero(fL - R.1) do
    fL := f(fL);
    i +:= 1;
end while;
assert i eq InertiaDegree(R);
assert f(UniformizingElement(R)) eq UniformizingElement(R);
fL := f(R.1^30);
i := 1;
while not IsWeaklyZero(fL - R.1^30) do
    fL := f(fL);
    i +:= 1;
end while;
assert i eq InertiaDegree(R);

f := FrobeniusAutomorphism(U);
fL := f(U.1);
i := 1;
while not IsWeaklyZero(fL - U.1) do
    fL := f(fL);
    i +:= 1;
end while;
assert i eq InertiaDegree(U);
assert f(UniformizingElement(U)) eq UniformizingElement(U);
fL := f(U.1^30);
i := 1;
while not IsWeaklyZero(fL - U.1^30) do
    fL := f(fL);
    i +:= 1;
end while;
assert i eq InertiaDegree(U);

f := FrobeniusAutomorphism(QR);
fL := f(QR.1);
i := 1;
while not IsWeaklyZero(fL - QR.1) do
    fL := f(fL);
    i +:= 1;
end while;
assert i eq InertiaDegree(QR);
assert f(UniformizingElement(QR)) eq UniformizingElement(QR);
fL := f(QR.1^30);
i := 1;
while not IsWeaklyZero(fL - QR.1^30) do
    fL := f(fL);
    i +:= 1;
end while;
assert i eq InertiaDegree(QR);

f := FrobeniusAutomorphism(QU);
fL := f(QU.1);
i := 1;
while not IsWeaklyZero(fL - QU.1) do
    fL := f(fL);
    i +:= 1;
end while;
assert i eq InertiaDegree(QU);

assert f(UniformizingElement(QU)) eq UniformizingElement(QU);
fL := f(QU.1^30);
i := 1;
while not IsWeaklyZero(fL - QU.1^30) do
    fL := f(fL);
    i +:= 1;
end while;
assert i eq InertiaDegree(QU);

A, am := AutomorphismGroup(U);
autos := [am(a) : a in A];
autos_inv := [am(a^-1) : a in A];
assert Minimum([Valuation(U.1 @ autos[i] @ autos_inv[i] - U.1) : i in [1 .. #autos]]) ge Precision(U) - 1;

URR := UnramifiedExtension(R, Polynomial(R, [5, 10, 2, 1]));
URF := FieldOfFractions(URR);
RRR := TotallyRamifiedExtension(URR, Polynomial(URR, [R.1, 5*R.1^20, 1]));
RRF := FieldOfFractions(RRR);

rf, rfm := ResidueClassField(URR);
assert Valuation(rfm(URR.1) @@ rfm - URR.1) ge 1;

    r := &+[Random(0, 500)*RRR.1^i : i in [0 .. 2*Degree(RRR)]];
    s := &+[Random(0, 300)*RRR.1^i : i in [0 .. 2*Degree(RRR)]];

    assert r + s eq s + r;
    assert Valuation(r + s) ge Minimum(Valuation(r), Valuation(s));
    assert r - s eq -(s - r);
    assert r*s eq s*r;
    assert Valuation(r*s) eq Valuation(r) + Valuation(s);
    // assert r div s eq 1/(s div r);
    assert Valuation((r/s)*(s/r) - 1) ge Precision(RRR) - 5;
    assert r^4 eq r*r*r*r;

    assert IsWeaklyZero(r + (-r));
    assert IsOne(RRR!1);
    assert IsMinusOne(-RRR!1);

    assert IsWeaklyZero(Evaluate(MinimalPolynomial(r), r));
    assert IsWeaklyZero(Evaluate(MinimalPolynomial(s), s));
    assert IsWeaklyZero(Evaluate(CharacteristicPolynomial(r), r));
    assert IsWeaklyZero(Evaluate(CharacteristicPolynomial(s), s));

    r := &+[Random(0, 500)*RRF.1^i : i in [0 .. 2*Degree(RRF)]];
    s := &+[Random(0, 300)*RRF.1^i : i in [0 .. 2*Degree(RRF)]];

    assert r + s eq s + r;
    assert Valuation(r + s) ge Minimum(Valuation(r), Valuation(s));
    assert r - s eq -(s - r);
    assert r*s eq s*r;
    assert Valuation(r*s) eq Valuation(r) + Valuation(s);
    assert r div s eq 1/(s div r);
    assert Valuation((r/s)*(s/r) - 1) ge Precision(RRR) - 5;
    assert r^4 eq r*r*r*r;

    assert IsWeaklyZero(r + (-r));
    assert IsOne(RRF!1);
    assert IsMinusOne(-RRF!1);
    assert IsWeaklyZero(Evaluate(MinimalPolynomial(r), r));
    assert IsWeaklyZero(Evaluate(MinimalPolynomial(s), s));

assert IsOne(RRR!1);
assert IsMinusOne(-RRR!1);
assert IsOne(URR!1);
assert IsMinusOne(-URR!1);
assert IsZero(RRR!0);
assert Valuation(RRR!0) ge Precision(RRR);
assert IsZero(URR!0);
assert Valuation(URR!0) ge Precision(URR);

assert IsOne(RRF!1);
assert IsMinusOne(-RRF!1);
assert IsOne(URF!1);
assert IsMinusOne(-URF!1);
assert IsZero(RRF!0);
assert Valuation(RRF!0) ge Precision(RRF);
assert IsZero(URF!0);
assert Valuation(URF!0) ge Precision(URF);

assert IsIntegral(RRR.1);
assert IsIntegral(UniformizingElement(RRR));
assert not IsIntegral(1/UniformizingElement(RRR));

assert IsIntegral(RRF.1);
assert IsIntegral(UniformizingElement(RRF));
assert not IsIntegral(1/UniformizingElement(RRF));

A, am := AutomorphismGroup(URR);
autos := [am(a) : a in A];
autos_inv := [am(a^-1) : a in A];
assert Minimum([Valuation(URR.1 @ autos[i] @ autos_inv[i] - URR.1) : i in [1 .. #autos]]) ge Precision(URR) - 5;

A, am := AutomorphismGroup(RRR);
autos := [am(a) : a in A];
autos_inv := [am(a^-1) : a in A];
assert Minimum([Valuation(RRR.1 @ autos[i] @ autos_inv[i] - RRR.1) : i in [1 .. #autos]]) ge Precision(RRR) - 5;

Discriminant(RRR);

assert IsRamified(RRR);
assert not IsUnramified(RRR);
assert IsTotallyRamified(RRR);
assert IsWildlyRamified(RRR) xor IsTamelyRamified(RRR);

for p in [2, 3, 5, 7, 17, 19, 23, 29] do 

    Qp := pAdicField(p, 50);
    Qpx<x> := PolynomialRing(Qp);

    for k in [1 .. 20] do
	f := Qpx![Random(-100, 100)/Random(1, 100) : i in [1 .. Random(1, 10)]];
	g := Qpx![Random(-100, 100)/Random(1, 100) : i in [1 .. Random(1, 10)]];

	gcd, a, b := XGCD(f, g);
	assert 
	   &and[RelativePrecision(c) eq 0 : c in Coefficients(a*f + b*g - gcd)];
    end for;


    degs := [];
    gcds := [];
    for k in [1 .. 20] do
	f := Qpx![Random(-50, 50)/Random(1, 50) : i in [1 .. Random(1, 5)]];
	g := Qpx![Random(-50, 50)/Random(1, 50) : i in [1 .. Random(1, 5)]];
	g *:= f;
	f *:= Qpx![Random(-50, 50)/Random(1, 50) : i in [1 .. Random(1, 5)]];

	gcd, a, b := XGCD(f, g);
	assert 
	   &and[RelativePrecision(c) eq 0 : c in Coefficients(a*f + b*g - gcd)];
	Append(~degs, Degree(gcd));
	Append(~gcds, gcd);
    end for;
    Sort(~degs);
    degs;
    assert degs[Ceiling(#degs/2)] gt 0;
end for;

ZX<x> := PolynomialRing(Integers());
K := NumberField(x^3 + x^2 - 4*x + 1);
OK := MaximalOrder(K);
for p in [2, 3, RandomPrime(3), RandomPrime(4), RandomPrime(5)] do
    v := Factorization(p*OK)[1,1];
    Kv,iota:=Completion(K,v);
    Kv:=ChangePrecision(Kv,400);
    KvX := PolynomialRing(Kv);
    ZX<x> := PolynomialRing(Integers());
    f1 := KvX ! (x^2+1);
    if not IsIrreducible(f1) then
	f1 := RandomPrimePolynomial(PolynomialRing(GF(p)), 2);
    end if;
    E1 := LocalField(Kv, f1);
    E1 := RamifiedRepresentation(E1);
    pi:=UniformizingElement(E1);
    E1X<x> := PolynomialRing(E1);
    pi := UniformizingElement(E1);
    f:= x^3 - pi;
    F:=TotallyRamifiedExtension(E1, f);
    pSelmerGroup(3, F);
    if Prime(F)^InertiaDegree(F, PrimeField(F)) lt 1000000 then
    assert #ResidueSystem(F) eq #Seqset(ResidueSystem(F));
    end if;
    PrincipalUnitGroupGenerators(F);
    f:= x^4 - pi;
    F:=TotallyRamifiedExtension(E1, f);
    F;
    PrincipalUnitGroupGenerators(F);
    pSelmerGroup(3, F);
end for;

