Skip to content

Instantly share code, notes, and snippets.

@Hermann-SW
Last active May 29, 2025 17:35
Show Gist options
  • Save Hermann-SW/76e7cf8545c5e8b0332faeaad878e08f to your computer and use it in GitHub Desktop.
Save Hermann-SW/76e7cf8545c5e8b0332faeaad878e08f to your computer and use it in GitHub Desktop.
Kunerth's algorithm from 1878, for determining modular sqrt
assert(b,s)=if(!(b), error(Str(s)));
m=eval(getenv("m"));
b=m.mod;
c=lift(m);
if(ispseudoprime(c),s=sqrt(Mod(-b,c)),issquare(Mod(b,-c),&s));
V=r=lift(s);;
print("V=",r);
e=simplify(((c*z+r)^2+b)/c);
f=1;
w=polcoeff(e,0);
if(type(w)!="t_INT"||w<0,e=simplify(((c*z+r)^2-b)/c);f=-1;w=polcoeff(e,0));
print("e=",e);
print("f=",f);
assert(issquare(w,&W));
print("W=",W);
beta=-(V\W);
alpha=W*(V+W*beta);
print("alpha=",alpha);
print("beta=",beta);
xx=alpha^2*x^2+(2*alpha*beta-f*b)*x+(beta^2-c);
print("xx=",xx);
nfr=nfroots(,xx);
print("nfr=",nfr);
{
foreach(nfr,X,
Y=Mod(alpha*X+beta,b);
if(lift(Y^2)==c,
print("X=",X);
print("Y=",Y);
print("Y^2=",Y^2)));
}
print("all asserts OK");
write("/dev/stderr", "\nb=m.mod; c=lift(m); V=r=lift(\"sqrt\"(Mod(-b,c)));");
write("/dev/stderr", "e=simplify(((c*z+r)^2±b)/c); f=±1; W=sqrt(polcoeff(e,0));");
write("/dev/stderr", "beta=-(V\\W); alpha=W*(V+W*beta);");
write("/dev/stderr", "xx=alpha^2*x^2+(2*alpha*beta-f*b)*x+(beta^2-c); nfr=nfroots(,xx);");
write("/dev/stderr", "\n∀X∈nfr: Y=Mod(alpha*X+beta,b);");
@Hermann-SW
Copy link
Author

Hermann-SW commented Jan 17, 2025

Hi,
I didcussed this on PARI/GP mailing list last year.
This posting names a big restriction of my implementation:
https://pari.math.u-bordeaux.fr/archives/pari-users-2311/msg00061.html

    It only works if constant term of quadratic form is a square.
    The Kunerth paper examples show how to deal with non-square constant term.
    But that is complex and not part of Kunerth.gp yet.

I cannot find the other posting on that mailing list where someone seriously said that Kunerth algorithm cannot work in general.

I will come back to this after having become student of Mathematics this October (at age of 60) — too much other stuff to investigate right now (CPUs benchmark for M_52 PRP, play with different GPUs to become even faster than CPUs, determining M_52=x¹+3*y², RSA_numbers_factored, 100 USD price money factoring challenge I started, ...).

@ytrezq
Copy link

ytrezq commented Jan 17, 2025

@Hermann-SW ok for seeing later why some of the example of the ebook don’t work with your code.

1 last short question anyway I need the response soon : when your code in nfroots returns 2 valid square it just returns $Y$ and $−Y$. When the modulus is composite, there’s at least 2 more numbers. Is it possible to tweak the algorithm so that it return a different possible square root than with your code ?

@catap
Copy link

catap commented May 29, 2025

Thanks for recovery this nice algorithm which is quite forgotten these days.

I've tried

$ m="Mod(861,1189)" gp -q < Kunerth.gp 
V=0
e=861*z^2 - 29/21
f=-1
  ***   at top-level: assert(issquare(w,&W))
  ***                 ^----------------------
  ***   in function assert: if(!(b),error(Str(s)))
  ***                               ^--------------
  ***   user error: 0
W=W
alpha=0
beta=0
xx=1189*x - 861
  ***   at top-level: nfr=nfroots(,xx)
  ***                     ^------------
  *** nfroots: incorrect type in nfrootsQ [not in Z[X]] (t_POL).
nfr=nfr
  ***   at top-level: foreach(nfr,X,Y=Mod(alpha*X+beta,b);if(lift(Y^
  ***                 ^----------------------------------------------
  *** foreach: incorrect type in foreach (t_POL).
all asserts OK

b=m.mod; c=lift(m); V=r=lift("sqrt"(Mod(-b,c)));
e=simplify(((c*z+r)^2±b)/c); f=±1; W=sqrt(polcoeff(e,0));
beta=-(V\W); alpha=W*(V+W*beta);
xx=alpha^2*x^2+(2*alpha*beta-f*b)*x+(beta^2-c);  nfr=nfroots(,xx);

∀X∈nfr: Y=Mod(alpha*X+beta,b);

and 123^2 = 861 (mod 1189)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment