Testing latest pari + WASM + node.js... and it works?! Wow.
License: GPL3
ubuntu2004
/* Copyright (C) 2000-2005 The PARI group.12This file is part of the PARI/GP package.34PARI/GP is free software; you can redistribute it and/or modify it under the5terms of the GNU General Public License as published by the Free Software6Foundation; either version 2 of the License, or (at your option) any later7version. It is distributed in the hope that it will be useful, but WITHOUT8ANY WARRANTY WHATSOEVER.910Check the License for details. You should have received a copy of it, along11with the package; see the file 'COPYING'. If not, write to the Free Software12Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */13#include "pari.h"14#include "paripriv.h"15/*******************************************************************/16/* */17/* QUADRATIC POLYNOMIAL ASSOCIATED TO A DISCRIMINANT */18/* */19/*******************************************************************/2021void22check_quaddisc(GEN x, long *s, long *pr, const char *f)23{24long r;25if (typ(x) != t_INT) pari_err_TYPE(f,x);26*s = signe(x);27if (Z_issquare(x)) pari_err_DOMAIN(f,"issquare(disc)","=", gen_1,x);28r = mod4(x); if (*s < 0 && r) r = 4 - r;29if (r > 1) pari_err_DOMAIN(f,"disc % 4",">", gen_1,x);30if (pr) *pr = r;31}32void33check_quaddisc_real(GEN x, long *r, const char *f)34{35long sx; check_quaddisc(x, &sx, r, f);36if (sx < 0) pari_err_DOMAIN(f, "disc","<",gen_0,x);37}38void39check_quaddisc_imag(GEN x, long *r, const char *f)40{41long sx; check_quaddisc(x, &sx, r, f);42if (sx > 0) pari_err_DOMAIN(f, "disc",">",gen_0,x);43}4445/* X^2 + b X + c is the canonical quadratic t_POL of discriminant D.46* Dodd is nonzero iff D is odd */47static void48quadpoly_bc(GEN D, long Dodd, GEN *b, GEN *c)49{50if (Dodd)51{52pari_sp av = avma;53*b = gen_m1;54*c = gerepileuptoint(av, shifti(subui(1,D), -2));55}56else57{58*b = gen_0;59*c = shifti(D,-2); togglesign(*c);60}61}62/* X^2 - X - (D-1)/4 or X^2 - D/4 */63static GEN64quadpoly_ii(GEN D, long Dmod4)65{66GEN b, c, y = cgetg(5,t_POL);67y[1] = evalsigne(1) | evalvarn(0);68quadpoly_bc(D, Dmod4, &b,&c);69gel(y,2) = c;70gel(y,3) = b;71gel(y,4) = gen_1; return y;72}73GEN74quadpoly(GEN D)75{76long s, Dmod4;77check_quaddisc(D, &s, &Dmod4, "quadpoly");78return quadpoly_ii(D, Dmod4);79}80GEN /* no checks */81quadpoly_i(GEN D) { return quadpoly_ii(D, Mod4(D)); }8283GEN84quadpoly0(GEN x, long v)85{86GEN T = quadpoly(x);87if (v > 0) setvarn(T, v);88return T;89}9091GEN92quadgen(GEN x)93{ retmkquad(quadpoly(x), gen_0, gen_1); }9495GEN96quadgen0(GEN x, long v)97{98if (v==-1) v = fetch_user_var("w");99retmkquad(quadpoly0(x, v), gen_0, gen_1);100}101102/***********************************************************************/103/** **/104/** BINARY QUADRATIC FORMS **/105/** **/106/***********************************************************************/107static int108is_qfi(GEN q) { return typ(q)==t_QFB && qfb_is_qfi(q); }109110static GEN111check_qfbext(const char *fun, GEN x)112{113long t = typ(x);114if (t == t_QFB) return x;115if (t == t_VEC && lg(x)==3)116{117GEN q = gel(x,1);118if (!is_qfi(q) && typ(gel(x,2))==t_REAL) return q;119}120pari_err_TYPE(fun, x);121return NULL;/* LCOV_EXCL_LINE */122}123124static GEN125qfb3(GEN x, GEN y, GEN z)126{ retmkqfb(icopy(x), icopy(y), icopy(z), qfb_disc3(x,y,z)); }127128static int129qfb_equal(GEN x, GEN y)130{131return equalii(gel(x,1),gel(y,1))132&& equalii(gel(x,2),gel(y,2))133&& equalii(gel(x,3),gel(y,3));134}135136/* valid for t_QFB, qfr3, qfr5; shallow */137static GEN138qfb_inv(GEN x) {139GEN z = shallowcopy(x);140gel(z,2) = negi(gel(z,2));141return z;142}143/* valid for t_QFB, gerepile-safe */144static GEN145qfbinv(GEN x)146{ retmkqfb(icopy(gel(x,1)),negi(gel(x,2)),icopy(gel(x,3)), icopy(gel(x,4))); }147148GEN149Qfb0(GEN a, GEN b, GEN c)150{151GEN q, D;152if (!b)153{154if (c) pari_err_TYPE("Qfb",c);155if (typ(a) != t_VEC || lg(a) != 4) pari_err_TYPE("Qfb",a);156b = gel(a,2);157c = gel(a,3);158a = gel(a,1);159}160else if (!c)161pari_err_TYPE("Qfb",b);162if (typ(a)!=t_INT) pari_err_TYPE("Qfb",a);163if (typ(b)!=t_INT) pari_err_TYPE("Qfb",b);164if (typ(c)!=t_INT) pari_err_TYPE("Qfb",c);165q = qfb3(a, b, c); D = qfb_disc(q);166if (signe(D) < 0)167{ if (signe(a) < 0) pari_err_IMPL("negative definite t_QFB"); }168else if (Z_issquare(D)) pari_err_DOMAIN("Qfb","issquare(disc)","=", gen_1,q);169return q;170}171172/* composition */173static void174qfb_sqr(GEN z, GEN x)175{176GEN c, d1, x2, v1, v2, c3, m, p1, r;177178d1 = bezout(gel(x,2),gel(x,1),&x2, NULL); /* usually 1 */179c = gel(x,3);180m = mulii(c,x2);181if (equali1(d1))182v1 = v2 = gel(x,1);183else184{185v1 = diviiexact(gel(x,1),d1);186v2 = mulii(v1, gcdii(d1,c)); /* = v1 iff x primitive */187c = mulii(c, d1);188}189togglesign(m);190r = modii(m,v2);191p1 = mulii(r, v1);192c3 = addii(c, mulii(r,addii(gel(x,2),p1)));193gel(z,1) = mulii(v1,v2);194gel(z,2) = addii(gel(x,2), shifti(p1,1));195gel(z,3) = diviiexact(c3,v2);196}197/* z <- x * y */198static void199qfb_comp(GEN z, GEN x, GEN y)200{201GEN n, c, d, y1, v1, v2, c3, m, p1, r;202203if (x == y) { qfb_sqr(z,x); return; }204n = shifti(subii(gel(y,2),gel(x,2)), -1);205v1 = gel(x,1);206v2 = gel(y,1);207c = gel(y,3);208d = bezout(v2,v1,&y1,NULL);209if (equali1(d))210m = mulii(y1,n);211else212{213GEN s = subii(gel(y,2), n);214GEN x2, y2, d1 = bezout(s,d,&x2,&y2); /* x2 s + y2 (x1 v1 + y1 v2) = d1 */215if (!equali1(d1))216{217v1 = diviiexact(v1,d1);218v2 = diviiexact(v2,d1); /* gcd = 1 iff x or y primitive */219v1 = mulii(v1, gcdii(c,gcdii(gel(x,3),gcdii(d1,n))));220c = mulii(c, d1);221}222m = addii(mulii(mulii(y1,y2),n), mulii(gel(y,3),x2));223}224togglesign(m);225r = modii(m, v1);226p1 = mulii(r, v2);227c3 = addii(c, mulii(r,addii(gel(y,2),p1)));228gel(z,1) = mulii(v1,v2);229gel(z,2) = addii(gel(y,2), shifti(p1,1));230gel(z,3) = diviiexact(c3,v1);231}232233/* not meant to be efficient */234static GEN235qfb_comp_gen(GEN x, GEN y)236{237GEN d1 = qfb_disc(x), d2 = qfb_disc(y);238GEN a1 = gel(x,1), b1 = gel(x,2), c1 = gel(x,3), n1;239GEN a2 = gel(y,1), b2 = gel(y,2), c2 = gel(y,3), n2;240GEN cx = content(x), cy = content(y), A, B, C, D, U, m, m2;241242if (!is_pm1(cx))243{244a1 = diviiexact(a1, cx); b1 = diviiexact(b1, cx);245c1 = diviiexact(c1, cx); d1 = diviiexact(d1, sqri(cx));246}247if (!is_pm1(cy))248{249a2 = diviiexact(a2, cy); c2 = diviiexact(c2, cy);250b2 = diviiexact(b2, cy); d2 = diviiexact(d2, sqri(cy));251}252D = gcdii(d1, d2); if (signe(d1) < 0) setsigne(D, -1);253if (!Z_issquareall(diviiexact(d1, D), &n1) ||254!Z_issquareall(diviiexact(d2, D), &n2)) return NULL;255A = mulii(a1, n2);256B = mulii(a2, n1);257C = shifti(addii(mulii(b1, n2), mulii(b2, n1)), -1);258U = ZV_extgcd(mkvec3(A, B, C));259m = gel(U,1); U = gmael(U,2,3);260A = mulii(diviiexact(mulii(a1,b2),m), gel(U,1));261B = mulii(diviiexact(mulii(a2,b1),m), gel(U,2));262C = addii(mulii(b1,b2), mulii(D, mulii(n1,n2)));263C = mulii(diviiexact(shifti(C,-1), m), gel(U,3));264B = addii(A, addii(B, C));265m2 = sqri(m);266A = diviiexact(mulii(a1, a2), m2);267C = diviiexact(shifti(subii(sqri(B),D), -2), A);268cx = mulii(cx, cy);269if (!is_pm1(cx))270{271A = mulii(A, cx); B = mulii(B, cx);272C = mulii(C, cx); D = mulii(D, sqri(cx));273}274return mkqfb(A, B, C, D);275}276277static GEN redimag_av(pari_sp av, GEN q);278static GEN279qficomp0(GEN x, GEN y, int raw)280{281pari_sp av = avma;282GEN z = cgetg(5,t_QFB);283gel(z,4) = gel(x,4);284qfb_comp(z, x,y);285if (raw) return gerepilecopy(av,z);286return redimag_av(av, z);287}288static GEN redreal(GEN x);289static GEN290qfrcomp0(GEN x, GEN y, int raw)291{292pari_sp av = avma;293GEN dx = NULL, dy = NULL;294GEN z = cgetg(5,t_QFB);295if (typ(x)==t_VEC) { dx = gel(x,2); x = gel(x,1); }296if (typ(y)==t_VEC) { dy = gel(y,2); y = gel(y,1); }297gel(z,4) = gel(x,4);298qfb_comp(z, x,y);299if (dx) z = mkvec2(z, dy? addrr(dx, dy): dx); else if (dy) z = mkvec2(z, dy);300if (!raw) z = redreal(z);301return gerepilecopy(av, z);302}303/* same discriminant, no distance, no checks */304GEN305qfbcomp_i(GEN x, GEN y)306{ return qfb_is_qfi(x)? qficomp0(x,y,0): qfrcomp0(x,y,0); }307GEN308qfbcomp(GEN x, GEN y)309{310GEN qx = check_qfbext("qfbcomp", x);311GEN qy = check_qfbext("qfbcomp", y);312if (!equalii(gel(qx,4),gel(qy,4)))313{314pari_sp av = avma;315GEN z = qfb_comp_gen(qx, qy);316if (typ(x) == t_VEC || typ(y) == t_VEC)317pari_err_IMPL("Shanks's distance in general composition");318if (!z) pari_err_OP("*",x,y);319return gerepileupto(av, qfbred(z));320}321return qfb_is_qfi(qx)? qficomp0(x,y,0): qfrcomp0(x,y,0);322}323/* same discriminant, no distance, no checks */324GEN325qfbcompraw_i(GEN x, GEN y)326{ return qfb_is_qfi(x)? qficomp0(x,y,1): qfrcomp0(x,y,1); }327GEN328qfbcompraw(GEN x, GEN y)329{330GEN qx = check_qfbext("qfbcompraw", x);331GEN qy = check_qfbext("qfbcompraw", y);332if (!equalii(gel(qx,4),gel(qy,4)))333{334pari_sp av = avma;335GEN z = qfb_comp_gen(qx, qy);336if (typ(x) == t_VEC || typ(y) == t_VEC)337pari_err_IMPL("Shanks's distance in general composition");338if (!z) pari_err_OP("qfbcompraw",x,y);339return gerepilecopy(av, z);340}341if (!equalii(gel(qx,4),gel(qy,4))) pari_err_OP("qfbcompraw",x,y);342return qfb_is_qfi(qx)? qficomp0(x,y,1): qfrcomp0(x,y,1);343}344345static GEN346qfisqr0(GEN x, long raw)347{348pari_sp av = avma;349GEN z = cgetg(5,t_QFB);350gel(z,4) = gel(x,4);351qfb_sqr(z,x);352if (raw) return gerepilecopy(av,z);353return redimag_av(av, z);354}355static GEN356qfrsqr0(GEN x, long raw)357{358pari_sp av = avma;359GEN z = cgetg(5,t_QFB);360gel(z,4) = gel(x,4);361qfb_sqr(z,x);362if (!raw) z = redreal(z);363return gerepilecopy(av, z);364}365/* same discriminant, no distance, no checks */366GEN367qfbsqr_i(GEN x)368{ return qfb_is_qfi(x)? qfisqr0(x,0): qfrsqr0(x,0); }369GEN370qfbsqr(GEN x)371{372GEN qx = check_qfbext("qfbsqr", x);373return qfb_is_qfi(qx)? qfisqr0(x,0): qfrsqr0(x,0);374}375376static GEN377qfr_1_by_disc(GEN D)378{379GEN y = cgetg(5,t_QFB), isqrtD;380pari_sp av = avma;381long r;382383check_quaddisc_real(D, &r, "qfr_1_by_disc");384gel(y,1) = gen_1; isqrtD = sqrti(D);385if ((r & 1) != mod2(isqrtD)) /* we know isqrtD > 0 */386isqrtD = gerepileuptoint(av, subiu(isqrtD,1));387gel(y,2) = isqrtD; av = avma;388gel(y,3) = gerepileuptoint(av, shifti(subii(sqri(isqrtD), D),-2));389gel(y,4) = icopy(D); return y;390}391392static GEN393qfr_disc(GEN x)394{ return qfb_disc(typ(x)==t_VEC ? gel(x,1): x); }395396static GEN397qfr_1(GEN x)398{ return qfr_1_by_disc(qfr_disc(x)); }399400static void401qfr_1_fill(GEN y, struct qfr_data *S)402{403pari_sp av = avma;404GEN y2 = S->isqrtD;405gel(y,1) = gen_1;406if (mod2(S->D) != mod2(y2)) y2 = subiu(y,1);407gel(y,2) = y2; av = avma;408gel(y,3) = gerepileuptoint(av, shifti(subii(sqri(y2), S->D),-2));409}410static GEN411qfr5_1(struct qfr_data *S, long prec)412{413GEN y = cgetg(6, t_VEC);414qfr_1_fill(y, S);415gel(y,4) = gen_0;416gel(y,5) = real_1(prec); return y;417}418static GEN419qfr3_1(struct qfr_data *S)420{421GEN y = cgetg(4, t_VEC);422qfr_1_fill(y, S); return y;423}424425/* Assume D < 0 is the discriminant of a t_QFB */426static GEN427qfi_1_by_disc(GEN D)428{429GEN b,c, y = cgetg(5,t_QFB);430quadpoly_bc(D, mod2(D), &b,&c);431if (b == gen_m1) b = gen_1;432gel(y,1) = gen_1;433gel(y,2) = b;434gel(y,3) = c;435gel(y,4) = icopy(D); return y;436}437static GEN438qfi_1(GEN x)439{440if (typ(x) != t_QFB) pari_err_TYPE("qfi_1",x);441return qfi_1_by_disc(qfb_disc(x));442}443444GEN445qfb_1(GEN x) { return qfb_is_qfi(x) ? qfi_1(x): qfr_1(x); }446447static GEN448_qfimul(void *E, GEN x, GEN y) { (void) E; return qficomp0(x,y,0); }449static GEN450_qfisqr(void *E, GEN x) { (void) E; return qficomp0(x,x,0); }451static GEN452_qfimulraw(void *E, GEN x, GEN y) { (void) E; return qficomp0(x,y,1); }453static GEN454_qfisqrraw(void *E, GEN x) { (void) E; return qficomp0(x,x,1); }455456static GEN457qfipowraw(GEN x, long n)458{459pari_sp av = avma;460GEN y;461if (!n) return qfi_1(x);462if (n== 1) return gcopy(x);463if (n==-1) { x = gcopy(x); togglesign(gel(x,2)); return x; }464if (n < 0) x = qfb_inv(x);465y = gen_powu(x, labs(n), NULL, &_qfisqrraw, &_qfimulraw);466return gerepilecopy(av,y);467}468469static GEN470qfipow(GEN x, GEN n)471{472pari_sp av = avma;473GEN y;474long s = signe(n);475if (!s) return qfi_1(x);476if (s < 0) x = qfb_inv(x);477y = gen_pow(qfbred_i(x), n, NULL, &_qfisqr, &_qfimul);478return gerepilecopy(av,y);479}480481static long482parteucl(GEN L, GEN *d, GEN *v3, GEN *v, GEN *v2)483{484long z;485*v = gen_0; *v2 = gen_1;486for (z=0; abscmpii(*v3,L) > 0; z++)487{488GEN t3, t2 = subii(*v, mulii(truedvmdii(*d,*v3,&t3),*v2));489*v = *v2; *d = *v3; *v2 = t2; *v3 = t3;490}491return z;492}493494/* composition: Shanks' NUCOMP & NUDUPL */495/* L = floor((|d|/4)^(1/4)) */496GEN497nucomp(GEN x, GEN y, GEN L)498{499pari_sp av = avma;500long z;501GEN a, a1, a2, b2, b, d, d1, g, n, p1, q1, q2, s, u, u1, v, v2, v3, Q;502503if (x==y) return nudupl(x,L);504if (!is_qfi(x)) pari_err_TYPE("nucomp",x);505if (!is_qfi(y)) pari_err_TYPE("nucomp",y);506507if (abscmpii(gel(x,1),gel(y,1)) < 0) swap(x, y);508s = shifti(addii(gel(x,2),gel(y,2)), -1);509n = subii(gel(y,2), s);510a1 = gel(x,1);511a2 = gel(y,1); d = bezout(a2,a1,&u,&v);512if (equali1(d)) { a = negi(mulii(u,n)); d1 = d; }513else if (dvdii(s,d)) /* d | s */514{515a = negi(mulii(u,n)); d1 = d;516a1 = diviiexact(a1, d1);517a2 = diviiexact(a2, d1);518s = diviiexact(s, d1);519}520else521{522GEN p2, l;523d1 = bezout(s,d,&u1,NULL);524if (!equali1(d1))525{526a1 = diviiexact(a1,d1);527a2 = diviiexact(a2,d1);528s = diviiexact(s,d1);529d = diviiexact(d,d1);530}531p1 = remii(gel(x,3),d);532p2 = remii(gel(y,3),d);533l = modii(mulii(negi(u1), addii(mulii(u,p1),mulii(v,p2))), d);534a = subii(mulii(l,diviiexact(a1,d)), mulii(u,diviiexact(n,d)));535}536a = modii(a,a1); p1 = subii(a,a1); if (abscmpii(a,p1) > 0) a = p1;537d = a1; v3 = a; z = parteucl(L, &d,&v3, &v,&v2);538Q = cgetg(5,t_QFB);539if (!z) {540g = diviiexact(addii(mulii(v3,s),gel(y,3)), d);541b = a2;542b2 = gel(y,2);543v2 = d1;544gel(Q,1) = mulii(d,b);545} else {546GEN e, q3, q4;547if (z&1) { v3 = negi(v3); v2 = negi(v2); }548b = diviiexact(addii(mulii(a2,d), mulii(n,v)), a1);549e = diviiexact(addii(mulii(s,d),mulii(gel(y,3),v)), a1);550q3 = mulii(e,v2);551q4 = subii(q3,s);552b2 = addii(q3,q4);553g = diviiexact(q4,v);554if (!equali1(d1)) { v2 = mulii(d1,v2); v = mulii(d1,v); b2 = mulii(d1,b2); }555gel(Q,1) = addii(mulii(d,b), mulii(e,v));556}557q1 = mulii(b, v3);558q2 = addii(q1,n);559gel(Q,2) = addii(b2, z? addii(q1,q2): shifti(q1, 1));560gel(Q,3) = addii(mulii(v3,diviiexact(q2,d)), mulii(g,v2));561gel(Q,4) = gel(x,4);562return redimag_av(av, Q);563}564565GEN566nudupl(GEN x, GEN L)567{568pari_sp av = avma;569long z;570GEN u, v, d, d1, p1, a, b, c, a2, b2, c2, Q, v2, v3, g;571572if (!is_qfi(x)) pari_err_TYPE("nudupl",x);573a = gel(x,1);574b = gel(x,2);575d1 = bezout(b,a, &u,NULL);576if (!equali1(d1))577{578a = diviiexact(a, d1);579b = diviiexact(b, d1);580}581c = modii(negi(mulii(u,gel(x,3))), a);582p1 = subii(c,a); if (abscmpii(c,p1) > 0) c = p1;583d = a; v3 = c; z = parteucl(L, &d,&v3, &v,&v2);584a2 = sqri(d);585c2 = sqri(v3);586Q = cgetg(5,t_QFB);587if (!z) {588g = diviiexact(addii(mulii(v3,b),gel(x,3)), d);589b2 = gel(x,2);590v2 = d1;591gel(Q,1) = a2;592} else {593GEN e;594if (z&1) { v = negi(v); d = negi(d); }595e = diviiexact(addii(mulii(gel(x,3),v), mulii(b,d)), a);596g = diviiexact(subii(mulii(e,v2), b), v);597b2 = addii(mulii(e,v2), mulii(v,g));598if (!equali1(d1)) { b2 = mulii(d1,b2); v = mulii(d1,v); v2 = mulii(d1,v2); }599gel(Q,1) = addii(a2, mulii(e,v));600}601gel(Q,2) = addii(b2, subii(sqri(addii(d,v3)), addii(a2,c2)));602gel(Q,3) = addii(c2, mulii(g,v2));603gel(Q,4) = gel(x,4);604return redimag_av(av, Q);605}606607static GEN608mul_nucomp(void *l, GEN x, GEN y) { return nucomp(x, y, (GEN)l); }609static GEN610mul_nudupl(void *l, GEN x) { return nudupl(x, (GEN)l); }611GEN612nupow(GEN x, GEN n, GEN L)613{614pari_sp av;615GEN y, D;616617if (typ(n) != t_INT) pari_err_TYPE("nupow",n);618if (!is_qfi(x)) pari_err_TYPE("nupow",x);619if (gequal1(n)) return gcopy(x);620av = avma;621D = qfb_disc(x);622y = qfi_1_by_disc(D);623if (!signe(n)) return y;624if (!L) L = sqrtnint(absi_shallow(D), 4);625y = gen_pow_i(x, n, (void*)L, &mul_nudupl, &mul_nucomp);626if (signe(n) < 0627&& !absequalii(gel(y,1),gel(y,2))628&& !absequalii(gel(y,1),gel(y,3))) togglesign(gel(y,2));629return gerepilecopy(av, y);630}631632/* Reduction */633634/* assume a > 0. Write b = q*2a + r, with -a < r <= a */635static GEN636dvmdii_round(GEN b, GEN a, GEN *r)637{638GEN a2 = shifti(a, 1), q = dvmdii(b, a2, r);639if (signe(b) >= 0) {640if (abscmpii(*r, a) > 0) { q = addiu(q, 1); *r = subii(*r, a2); }641} else { /* r <= 0 */642if (abscmpii(*r, a) >= 0){ q = subiu(q, 1); *r = addii(*r, a2); }643}644return q;645}646/* Assume 0 < a <= LONG_MAX. Ensure no overflow */647static long648dvmdsu_round(long b, ulong a, long *r)649{650ulong a2 = a << 1, q, ub, ur;651if (b >= 0) {652ub = b;653q = ub / a2;654ur = ub % a2;655if (ur > a) { ur -= a; q++; *r = (long)ur; *r -= (long)a; }656else *r = (long)ur;657return (long)q;658} else { /* r <= 0 */659ub = (ulong)-b; /* |b| */660q = ub / a2;661ur = ub % a2;662if (ur >= a) { ur -= a; q++; *r = (long)ur; *r = (long)a - *r; }663else *r = -(long)ur;664return -(long)q;665}666}667/* reduce b mod 2*a. Update b,c */668static void669REDB(GEN a, GEN *b, GEN *c)670{671GEN r, q = dvmdii_round(*b, a, &r);672if (!signe(q)) return;673*c = subii(*c, mulii(q, shifti(addii(*b, r),-1)));674*b = r;675}676/* Assume a > 0. Reduce b mod 2*a. Update b,c */677static void678sREDB(ulong a, long *b, ulong *c)679{680long r, q;681ulong uz;682if (a > LONG_MAX) return; /* b already reduced */683q = dvmdsu_round(*b, a, &r);684if (q == 0) return;685/* Final (a,r,c2) satisfies |r| <= |b| hence c2 <= c, c2 = c - q*z,686* where z = (b+r) / 2, representable as long, has the same sign as q. */687if (*b < 0)688{ /* uz = -z >= 0, q < 0 */689if (r >= 0) /* different signs=>no overflow, exact division */690uz = (ulong)-((*b + r)>>1);691else692{693ulong ub = (ulong)-*b, ur = (ulong)-r;694uz = (ub + ur) >> 1;695}696*c -= (-q) * uz; /* c -= qz */697}698else699{ /* uz = z >= 0, q > 0 */700if (r <= 0)701uz = (*b + r)>>1;702else703{704ulong ub = (ulong)*b, ur = (ulong)r;705uz = ((ub + ur) >> 1);706}707*c -= q * uz; /* c -= qz */708}709*b = r;710}711static void712REDBU(GEN a, GEN *b, GEN *c, GEN u1, GEN *u2)713{ /* REDB(a,b,c) */714GEN r, q = dvmdii_round(*b, a, &r);715*c = subii(*c, mulii(q, shifti(addii(*b, r),-1)));716*b = r;717*u2 = subii(*u2, mulii(q, u1));718}719720/* q t_QFB, return reduced representative and set base change U in Sl2(Z) */721GEN722redimagsl2(GEN q, GEN *U)723{724pari_sp av = avma;725GEN z, u1,u2,v1,v2,Q;726GEN a = gel(q,1), b = gel(q,2), c = gel(q,3);727long cmp;728u1 = gen_1; u2 = gen_0;729cmp = abscmpii(a, b);730if (cmp < 0)731REDBU(a,&b,&c, u1,&u2);732else if (cmp == 0 && signe(b) < 0)733{ /* b = -a */734b = negi(b);735u2 = gen_1;736}737for(;;)738{739cmp = abscmpii(a, c); if (cmp <= 0) break;740swap(a,c); b = negi(b);741z = u1; u1 = u2; u2 = negi(z);742REDBU(a,&b,&c, u1,&u2);743if (gc_needed(av, 1)) {744if (DEBUGMEM>1) pari_warn(warnmem, "redimagsl2");745gerepileall(av, 5, &a,&b,&c, &u1,&u2);746}747}748if (cmp == 0 && signe(b) < 0)749{750b = negi(b);751z = u1; u1 = u2; u2 = negi(z);752}753/* Let q = (A,B,C). q o [u1,u2; v1,v2] = Q implies754* [v1,v2] = (1/C) [(b-B)/2 u1 - a u2, c u1 - (b+B)/2 u2] */755z = shifti(subii(b, gel(q,2)), -1);756v1 = subii(mulii(z, u1), mulii(a, u2)); v1 = diviiexact(v1, gel(q,3));757z = subii(z, b);758v2 = addii(mulii(z, u2), mulii(c, u1)); v2 = diviiexact(v2, gel(q,3));759*U = mkmat2(mkcol2(u1,v1), mkcol2(u2,v2));760Q = lg(q)==5 ? mkqfb(a,b,c,gel(q,4)): mkvec3(a,b,c);761gerepileall(av, 2, &Q, U);762return Q;763}764765static GEN766setq_b0(ulong a, ulong c, GEN D)767{ retmkqfb(utoipos(a), gen_0, utoipos(c), icopy(D)); }768/* assume |sb| = 1 */769static GEN770setq(ulong a, ulong b, ulong c, long sb, GEN D)771{ retmkqfb(utoipos(a), sb==1? utoipos(b): utoineg(b), utoipos(c), icopy(D)); }772/* 0 < a, c < 2^BIL, b = 0 */773static GEN774redimag_1_b0(ulong a, ulong c, GEN D)775{ return (a <= c)? setq_b0(a, c, D): setq_b0(c, a, D); }776777/* 0 < a, c < 2^BIL: single word affair */778static GEN779redimag_1(pari_sp av, GEN a, GEN b, GEN c, GEN D)780{781ulong ua, ub, uc;782long sb;783for(;;)784{ /* at most twice */785long lb = lgefint(b); /* <= 3 after first loop */786if (lb == 2) return redimag_1_b0(a[2],c[2], D);787if (lb == 3 && uel(b,2) <= (ulong)LONG_MAX) break;788REDB(a,&b,&c);789if (uel(a,2) <= uel(c,2))790{ /* lg(b) <= 3 but may be too large for itos */791long s = signe(b);792set_avma(av);793if (!s) return redimag_1_b0(a[2], c[2], D);794if (a[2] == c[2]) s = 1;795return setq(a[2], b[2], c[2], s, D);796}797swap(a,c); b = negi(b);798}799/* b != 0 */800set_avma(av);801ua = a[2];802ub = sb = b[2]; if (signe(b) < 0) sb = -sb;803uc = c[2];804if (ua < ub)805sREDB(ua, &sb, &uc);806else if (ua == ub && sb < 0) sb = (long)ub;807while(ua > uc)808{809lswap(ua,uc); sb = -sb;810sREDB(ua, &sb, &uc);811}812if (!sb) return setq_b0(ua, uc, D);813else814{815long s = 1;816if (sb < 0)817{818sb = -sb;819if (ua != uc) s = -1;820}821return setq(ua, sb, uc, s, D);822}823}824825static GEN826redimag_av(pari_sp av, GEN q)827{828GEN a = gel(q,1), b = gel(q,2), c = gel(q,3), D = gel(q,4);829long cmp, lc = lgefint(c);830831if (lgefint(a) == 3 && lc == 3) return redimag_1(av, a, b, c, D);832cmp = abscmpii(a, b);833if (cmp < 0)834REDB(a,&b,&c);835else if (cmp == 0 && signe(b) < 0)836b = negi(b);837for(;;)838{839cmp = abscmpii(a, c); if (cmp <= 0) break;840lc = lgefint(a); /* lg(future c): we swap a & c next */841if (lc == 3) return redimag_1(av, a, b, c, D);842swap(a,c); b = negi(b); /* apply rho */843REDB(a,&b,&c);844}845if (cmp == 0 && signe(b) < 0) b = negi(b);846return gerepilecopy(av, mkqfb(a, b, c, D));847}848static GEN849redimag(GEN q) { return redimag_av(avma, q); }850851static GEN852rhoimag(GEN x)853{854pari_sp av = avma;855GEN a = gel(x,1), b = gel(x,2), c = gel(x,3);856int fl = abscmpii(a, c);857if (fl <= 0)858{859int fg = abscmpii(a, b);860if (fg >= 0)861{862x = gcopy(x);863if ((!fl || !fg) && signe(gel(x,2)) < 0) setsigne(gel(x,2), 1);864return x;865}866}867swap(a,c); b = negi(b);868REDB(a, &b, &c);869return gerepilecopy(av, mkqfb(a,b,c, qfb_disc(x)));870}871872/* qfr3 / qfr5 */873874/* t_QFB are unusable: D, sqrtD, isqrtD are recomputed all the time and the875* logarithmic Shanks's distance is costly and hard to control.876* qfr3 / qfr5 routines take a container of t_INTs (e.g a t_VEC) as argument,877* at least 3 (resp. 5) components [it is a feature that they do not check the878* precise type or length of the input]. They return a vector of length 3879* (resp. 5). A qfr3 [a,b,c] contains the form coeffs, in a qfr5 [a,b,c, e,d]880* the t_INT e is a binary exponent, d a t_REAL, coding the distance in881* multiplicative form: the true distance is obtained from qfr5_dist.882* All other qfr routines are obsolete (inefficient) wrappers */883884/* static functions are not stack-clean. Unless mentionned otherwise, public885* functions are. */886887#define EMAX 22888static void889fix_expo(GEN x)890{891if (expo(gel(x,5)) >= (1L << EMAX)) {892gel(x,4) = addiu(gel(x,4), 1);893shiftr_inplace(gel(x,5), - (1L << EMAX));894}895}896897/* (1/2) log (d * 2^{e * 2^EMAX}). Not stack clean if e != 0 */898GEN899qfr5_dist(GEN e, GEN d, long prec)900{901GEN t = logr_abs(d);902if (signe(e)) {903GEN u = mulir(e, mplog2(prec));904shiftr_inplace(u, EMAX); t = addrr(t, u);905}906shiftr_inplace(t, -1); return t;907}908909static void910rho_get_BC(GEN *B, GEN *C, GEN b, GEN c, struct qfr_data *S)911{912GEN t, u;913u = shifti(c,1);914t = (abscmpii(S->isqrtD,c) >= 0)? S->isqrtD: c;915u = remii(addii_sign(t,1, b,signe(b)), u);916*B = addii_sign(t, 1, u, -signe(u)); /* |t| - (|t|+b) % |2c| */917if (*B == gen_0)918{ u = shifti(S->D, -2); setsigne(u, -1); }919else920u = shifti(addii_sign(sqri(*B),1, S->D,-1), -2);921*C = diviiexact(u, c); /* = (B^2-D)/4c */922}923/* Not stack-clean */924GEN925qfr3_rho(GEN x, struct qfr_data *S)926{927GEN B, C, b = gel(x,2), c = gel(x,3);928rho_get_BC(&B, &C, b, c, S);929return mkvec3(c,B,C);930}931/* Not stack-clean */932GEN933qfr5_rho(GEN x, struct qfr_data *S)934{935GEN B, C, y, b = gel(x,2), c = gel(x,3);936long sb = signe(b);937938rho_get_BC(&B, &C, b, c, S);939y = mkvec5(c,B,C, gel(x,4), gel(x,5));940if (sb) {941GEN t = subii(sqri(b), S->D);942if (sb < 0)943t = divir(t, sqrr(subir(b,S->sqrtD)));944else945t = divri(sqrr(addir(b,S->sqrtD)), t);946/* t = (b + sqrt(D)) / (b - sqrt(D)), evaluated stably */947gel(y,5) = mulrr(t, gel(y,5)); fix_expo(y);948}949return y;950}951952/* Not stack-clean */953GEN954qfr_to_qfr5(GEN x, long prec)955{ return mkvec5(gel(x,1),gel(x,2),gel(x,3),gen_0,real_1(prec)); }956957/* d0 = initial distance, x = [a,b,c, expo(d), d], d = exp(2*distance) */958GEN959qfr5_to_qfr(GEN x, GEN D, GEN d0)960{961if (d0)962{963GEN n = gel(x,4), d = absr(gel(x,5));964if (signe(n))965{966n = addis(shifti(n, EMAX), expo(d));967setexpo(d, 0); d = logr_abs(d);968if (signe(n)) d = addrr(d, mulir(n, mplog2(lg(d0))));969shiftr_inplace(d, -1);970d0 = addrr(d0, d);971}972else if (!gequal1(d)) /* avoid loss of precision */973{974d = logr_abs(d);975shiftr_inplace(d, -1);976d0 = addrr(d0, d);977}978}979x = qfr3_to_qfr(x, D);980return d0 ? mkvec2(x,d0): x;981}982983/* Not stack-clean */984GEN985qfr3_to_qfr(GEN x, GEN d) { retmkqfb(gel(x,1), gel(x,2), gel(x,3), d); }986987static int988ab_isreduced(GEN a, GEN b, GEN isqrtD)989{990if (signe(b) > 0 && abscmpii(b, isqrtD) <= 0)991{992GEN t = addii_sign(isqrtD,1, shifti(a,1),-1);993long l = abscmpii(b, t); /* compare |b| and |floor(sqrt(D)) - |2a|| */994if (l > 0 || (l == 0 && signe(t) < 0)) return 1;995}996return 0;997}998999INLINE int1000qfr_isreduced(GEN x, GEN isqrtD)1001{1002return ab_isreduced(gel(x,1),gel(x,2),isqrtD);1003}10041005/* Not stack-clean */1006GEN1007qfr5_red(GEN x, struct qfr_data *S)1008{1009pari_sp av = avma;1010while (!qfr_isreduced(x, S->isqrtD))1011{1012x = qfr5_rho(x, S);1013if (gc_needed(av,2))1014{1015if (DEBUGMEM>1) pari_warn(warnmem,"qfr5_red");1016x = gerepilecopy(av, x);1017}1018}1019return x;1020}1021/* Not stack-clean */1022GEN1023qfr3_red(GEN x, struct qfr_data *S)1024{1025pari_sp av = avma;1026while (!qfr_isreduced(x, S->isqrtD))1027{1028x = qfr3_rho(x, S);1029if (gc_needed(av,2))1030{1031if (DEBUGMEM>1) pari_warn(warnmem,"qfr3_red");1032x = gerepilecopy(av, x);1033}1034}1035return x;1036}10371038void1039qfr_data_init(GEN D, long prec, struct qfr_data *S)1040{1041S->D = D;1042S->sqrtD = sqrtr(itor(S->D,prec));1043S->isqrtD = truncr(S->sqrtD);1044}10451046static GEN1047qfr5_init(GEN x, GEN d, struct qfr_data *S)1048{1049long prec = realprec(d), l = -expo(d);1050if (l < BITS_IN_LONG) l = BITS_IN_LONG;1051prec = maxss(prec, nbits2prec(l));1052S->D = qfb_disc(x);1053x = qfr_to_qfr5(x,prec);1054if (!S->sqrtD) S->sqrtD = sqrtr(itor(S->D,prec));1055else if (typ(S->sqrtD) != t_REAL) pari_err_TYPE("qfr_init",S->sqrtD);10561057if (!S->isqrtD)1058{1059pari_sp av=avma;1060long e;1061S->isqrtD = gcvtoi(S->sqrtD,&e);1062if (e>-2) { set_avma(av); S->isqrtD = sqrti(S->D); }1063}1064else if (typ(S->isqrtD) != t_INT) pari_err_TYPE("qfr_init",S->isqrtD);1065return x;1066}1067static GEN1068qfr3_init(GEN x, struct qfr_data *S)1069{1070S->D = qfb_disc(x);1071if (!S->isqrtD) S->isqrtD = sqrti(S->D);1072else if (typ(S->isqrtD) != t_INT) pari_err_TYPE("qfr_init",S->isqrtD);1073return x;1074}10751076#define qf_NOD 21077#define qf_STEP 110781079static GEN1080redreal_i(GEN x, long flag, GEN isqrtD, GEN sqrtD)1081{1082struct qfr_data S;1083GEN d = NULL, y;1084if (typ(x)==t_VEC) { d = gel(x,2); x = gel(x,1); } else flag |= qf_NOD;1085S.sqrtD = sqrtD;1086S.isqrtD = isqrtD;1087y = (flag & qf_NOD)? qfr3_init(x, &S): qfr5_init(x, d, &S);1088switch(flag) {1089case 0: y = qfr5_red(y,&S); break;1090case qf_NOD: y = qfr3_red(y,&S); break;1091case qf_STEP: y = qfr5_rho(y,&S); break;1092case qf_STEP|qf_NOD: y = qfr3_rho(y,&S); break;1093default: pari_err_FLAG("qfbred");1094}1095return qfr5_to_qfr(y, qfb_disc(x), d);1096}1097static GEN1098redreal(GEN x) { return redreal_i(x,0,NULL,NULL); }10991100GEN1101qfbred0(GEN x, long flag, GEN isqrtD, GEN sqrtD)1102{1103pari_sp av;1104GEN q = check_qfbext("qfbred",x);1105if (qfb_is_qfi(q)) return (flag & qf_STEP)? rhoimag(x): redimag(x);1106if (typ(x)==t_QFB) flag |= qf_NOD;1107else flag &= ~qf_NOD;1108av = avma;1109return gerepilecopy(av, redreal_i(x,flag,isqrtD,sqrtD));1110}1111/* t_QFB */1112GEN1113qfbred_i(GEN x) { return qfb_is_qfi(x)? redimag(x): redreal(x); }1114GEN1115qfbred(GEN x) { return qfbred0(x, 0, NULL, NULL); }11161117/* Not stack-clean */1118GEN1119qfr5_compraw(GEN x, GEN y)1120{1121GEN z = cgetg(6,t_VEC); qfb_comp(z,x,y);1122if (x == y)1123{1124gel(z,4) = shifti(gel(x,4),1);1125gel(z,5) = sqrr(gel(x,5));1126}1127else1128{1129gel(z,4) = addii(gel(x,4),gel(y,4));1130gel(z,5) = mulrr(gel(x,5),gel(y,5));1131}1132fix_expo(z); return z;1133}1134GEN1135qfr5_comp(GEN x, GEN y, struct qfr_data *S)1136{ return qfr5_red(qfr5_compraw(x, y), S); }1137/* Not stack-clean */1138GEN1139qfr3_compraw(GEN x, GEN y)1140{1141GEN z = cgetg(4,t_VEC); qfb_comp(z,x,y);1142return z;1143}1144GEN1145qfr3_comp(GEN x, GEN y, struct qfr_data *S)1146{ return qfr3_red(qfr3_compraw(x,y), S); }11471148/* m > 0. Not stack-clean */1149static GEN1150qfr5_powraw(GEN x, long m)1151{1152GEN y = NULL;1153for (; m; m >>= 1)1154{1155if (m&1) y = y? qfr5_compraw(y,x): x;1156if (m == 1) break;1157x = qfr5_compraw(x,x);1158}1159return y;1160}11611162/* return x^n. Not stack-clean */1163GEN1164qfr5_pow(GEN x, GEN n, struct qfr_data *S)1165{1166GEN y = NULL;1167long i, m, s = signe(n);1168if (!s) return qfr5_1(S, lg(gel(x,5)));1169if (s < 0) x = qfb_inv(x);1170for (i=lgefint(n)-1; i>1; i--)1171{1172m = n[i];1173for (; m; m>>=1)1174{1175if (m&1) y = y? qfr5_comp(y,x,S): x;1176if (m == 1 && i == 2) break;1177x = qfr5_comp(x,x,S);1178}1179}1180return y;1181}1182/* m > 0; return x^m. Not stack-clean */1183static GEN1184qfr3_powraw(GEN x, long m)1185{1186GEN y = NULL;1187for (; m; m>>=1)1188{1189if (m&1) y = y? qfr3_compraw(y,x): x;1190if (m == 1) break;1191x = qfr3_compraw(x,x);1192}1193return y;1194}1195/* return x^n. Not stack-clean */1196GEN1197qfr3_pow(GEN x, GEN n, struct qfr_data *S)1198{1199GEN y = NULL;1200long i, m, s = signe(n);1201if (!s) return qfr3_1(S);1202if (s < 0) x = qfb_inv(x);1203for (i=lgefint(n)-1; i>1; i--)1204{1205m = n[i];1206for (; m; m>>=1)1207{1208if (m&1) y = y? qfr3_comp(y,x,S): x;1209if (m == 1 && i == 2) break;1210x = qfr3_comp(x,x,S);1211}1212}1213return y;1214}12151216static GEN1217qfrinvraw(GEN x)1218{1219if (typ(x) == t_VEC) retmkvec2(qfbinv(gel(x,1)), negr(gel(x,2)));1220return qfbinv(x);1221}1222static GEN1223qfrpowraw(GEN x, long n)1224{1225struct qfr_data S = { NULL, NULL, NULL };1226pari_sp av = avma;1227if (!n) return qfr_1(x);1228if (n==1) return gcopy(x);1229if (n==-1) return qfrinvraw(x);1230if (typ(x)==t_QFB)1231{1232GEN D = qfb_disc(x);1233if (n < 0) { x = qfb_inv(x); n = -n; }1234x = qfr3_powraw(x, n);1235x = qfr3_to_qfr(x, D);1236}1237else1238{1239GEN d0 = gel(x,2);1240x = gel(x,1);1241if (n < 0) { x = qfb_inv(x); n = -n; }1242x = qfr5_init(x, d0, &S);1243if (labs(n) != 1) x = qfr5_powraw(x, n);1244x = qfr5_to_qfr(x, S.D, mulrs(d0,n));1245}1246return gerepilecopy(av, x);1247}1248static GEN1249qfrpow(GEN x, GEN n)1250{1251struct qfr_data S = { NULL, NULL, NULL };1252long s = signe(n);1253pari_sp av = avma;1254if (!s) return qfr_1(x);1255if (typ(x)==t_QFB)1256{1257if (s < 0) x = qfb_inv(x);1258x = qfr3_init(x, &S);1259x = is_pm1(n)? qfr3_red(x, &S): qfr3_pow(x, n, &S);1260x = qfr3_to_qfr(x, S.D);1261}1262else1263{1264GEN d0 = gel(x,2);1265x = gel(x,1);1266if (s < 0) x = qfb_inv(x);1267x = qfr5_init(x, d0, &S);1268x = is_pm1(n)? qfr5_red(x, &S): qfr5_pow(x, n, &S);1269x = qfr5_to_qfr(x, S.D, mulri(d0,n));1270}1271return gerepilecopy(av, x);1272}1273GEN1274qfbpowraw(GEN x, long n)1275{1276GEN q = check_qfbext("qfbpowraw",x);1277return qfb_is_qfi(q)? qfipowraw(x,n): qfrpowraw(x,n);1278}1279/* same discriminant, no distance, no checks */1280GEN1281qfbpow_i(GEN x, GEN n) { return qfb_is_qfi(x)? qfipow(x,n): qfrpow(x,n); }1282GEN1283qfbpow(GEN x, GEN n)1284{1285GEN q = check_qfbext("qfbpow",x);1286return qfb_is_qfi(q)? qfipow(x,n): qfrpow(x,n);1287}12881289/* Prime forms attached to prime ideals of degree 1 */12901291/* assume x != 0 a t_INT, p > 01292* Return a t_QFB, but discriminant sign is not checked: can be used for1293* real forms as well */1294GEN1295primeform_u(GEN x, ulong p)1296{1297GEN c, y = cgetg(5, t_QFB);1298pari_sp av = avma;1299ulong b;1300long s;13011302s = mod8(x); if (signe(x) < 0 && s) s = 8-s;1303/* 2 or 3 mod 4 */1304if (s & 2) pari_err_DOMAIN("primeform", "disc % 4", ">",gen_1, x);1305if (p == 2) {1306switch(s) {1307case 0: b = 0; break;1308case 1: b = 1; break;1309case 4: b = 2; break;1310default: pari_err_SQRTN("primeform", mkintmod(x,utoi(p)) );1311b = 0; /* -Wall */1312}1313c = shifti(subsi(s,x), -3);1314} else {1315b = Fl_sqrt(umodiu(x,p), p);1316if (b == ~0UL) pari_err_SQRTN("primeform", mkintmod(x,utoi(p)) );1317/* mod(b) != mod2(x) ? */1318if ((b ^ s) & 1) b = p - b;1319c = diviuexact(shifti(subii(sqru(b), x), -2), p);1320}1321gel(y,3) = gerepileuptoint(av, c);1322gel(y,4) = icopy(x);1323gel(y,2) = utoi(b);1324gel(y,1) = utoipos(p); return y;1325}13261327/* special case: p = 1 return unit form */1328GEN1329primeform(GEN x, GEN p)1330{1331const char *f = "primeform";1332pari_sp av;1333long s, sx = signe(x), sp = signe(p);1334GEN y, b, absp;13351336if (typ(x) != t_INT) pari_err_TYPE(f,x);1337if (typ(p) != t_INT) pari_err_TYPE(f,p);1338if (!sp) pari_err_DOMAIN(f,"p","=",gen_0,p);1339if (!sx) pari_err_DOMAIN(f,"D","=",gen_0,x);1340if (lgefint(p) == 3)1341{1342ulong pp = p[2];1343if (pp == 1) {1344if (sx < 0) {1345long r;1346if (sp < 0) pari_err_IMPL("negative definite t_QFB");1347r = mod4(x);1348if (r && r != 3) pari_err_DOMAIN(f,"disc % 4",">", gen_1,x);1349return qfi_1_by_disc(x);1350}1351y = qfr_1_by_disc(x);1352if (sp < 0) { gel(y,1) = gen_m1; togglesign(gel(y,3)); }1353return y;1354}1355y = primeform_u(x, pp);1356if (sx < 0) {1357if (sp < 0) pari_err_IMPL("negative definite t_QFB");1358return y;1359}1360if (sp < 0) { togglesign(gel(y,1)); togglesign(gel(y,3)); }1361return gcopy( qfr3_to_qfr(y, x) );1362}1363s = mod8(x);1364if (sx < 0)1365{1366if (sp < 0) pari_err_IMPL("negative definite t_QFB");1367if (s) s = 8-s;1368}1369y = cgetg(5, t_QFB);1370/* 2 or 3 mod 4 */1371if (s & 2) pari_err_DOMAIN(f, "disc % 4", ">",gen_1, x);1372absp = absi_shallow(p); av = avma;1373b = Fp_sqrt(x, absp); if (!b) pari_err_SQRTN(f, mkintmod(x,absp));1374s &= 1; /* s = x mod 2 */1375/* mod(b) != mod2(x) ? [Warning: we may have b == 0] */1376if ((!signe(b) && s) || mod2(b) != s) b = gerepileuptoint(av, subii(absp,b));13771378av = avma;1379gel(y,3) = gerepileuptoint(av, diviiexact(shifti(subii(sqri(b), x), -2), p));1380gel(y,4) = icopy(x);1381gel(y,2) = b;1382gel(y,1) = icopy(p);1383return y;1384}13851386static GEN1387normforms(GEN D, GEN fa)1388{1389long i, j, k, lB, aN, sa;1390GEN a, L, V, B, N, N2;1391int D_odd = mpodd(D);1392a = typ(fa) == t_INT ? fa: typ(fa) == t_VEC? gel(fa,1): factorback(fa);1393sa = signe(a);1394if (sa==0 || (signe(D)<0 && sa<0)) return NULL;1395V = D_odd? Zn_quad_roots(fa, gen_1, shifti(subsi(1, D), -2))1396: Zn_quad_roots(fa, gen_0, negi(shifti(D, -2)));1397if (!V) return NULL;1398N = gel(V,1); B = gel(V,2); lB = lg(B);1399N2 = shifti(N,1);1400aN = itou(diviiexact(a, N)); /* |a|/N */1401L = cgetg((lB-1)*aN+1, t_VEC);1402for (k = 1, i = 1; i < lB; i++)1403{1404GEN b = shifti(gel(B,i), 1), c, C;1405if (D_odd) b = addiu(b , 1);1406c = diviiexact(shifti(subii(sqri(b), D), -2), a);1407for (j = 0;; b = addii(b, N2))1408{1409gel(L, k++) = mkqfb(a, b, c, D);1410if (++j == aN) break;1411C = addii(b, N); if (aN > 1) C = diviuexact(C, aN);1412c = sa > 0? addii(c, C): subii(c, C);1413}1414}1415return L;1416}14171418/* Let M and N in SL2(Z), return (N*M^-1)[,1] */1419static GEN1420SL2_div_mul_e1(GEN N, GEN M)1421{1422GEN b = gcoeff(M,2,1), d = gcoeff(M,2,2);1423GEN p = subii(mulii(gcoeff(N,1,1), d), mulii(gcoeff(N,1,2), b));1424GEN q = subii(mulii(gcoeff(N,2,1), d), mulii(gcoeff(N,2,2), b));1425return mkvec2(p,q);1426}14271428/* Let M and N in SL2(Z), return (N*[1,0;0,-1]*M^-1)[,1] */1429static GEN1430SL2_swap_div_mul_e1(GEN N, GEN M)1431{1432GEN b = gcoeff(M,2,1), d = gcoeff(M,2,2);1433GEN p = addii(mulii(gcoeff(N,1,1), d), mulii(gcoeff(N,1,2), b));1434GEN q = addii(mulii(gcoeff(N,2,1), d), mulii(gcoeff(N,2,2), b));1435return mkvec2(p,q);1436}14371438/* Test equality modulo GL2 of two reduced forms */1439static int1440GL2_qfb_equal(GEN a, GEN b)1441{1442return equalii(gel(a,1),gel(b,1))1443&& absequalii(gel(a,2),gel(b,2))1444&& equalii(gel(a,3),gel(b,3));1445}14461447static GEN1448qfisolve_normform(GEN Q, GEN P)1449{1450GEN a = gel(Q,1), N = gel(Q,2);1451GEN M, b = redimagsl2(P, &M);1452if (!GL2_qfb_equal(a,b) || signe(gel(a,2))!=signe(gel(b,2)))1453return NULL;1454return SL2_div_mul_e1(N,M);1455}14561457static GEN1458qfbsolve_cornacchia(GEN c, GEN p, int swap)1459{1460pari_sp av = avma;1461GEN M, N;1462if (kronecker(negi(c), p) < 0 || !cornacchia(c, p, &M,&N))1463{ set_avma(av); return gen_0; }1464return gerepilecopy(av, swap? mkvec2(N,M): mkvec2(M,N));1465}14661467GEN1468qfisolvep(GEN Q, GEN p)1469{1470GEN M, N, x,y, a,b,c, d;1471pari_sp av = avma;1472if (!signe(gel(Q,2)))1473{1474a = gel(Q,1);1475c = gel(Q,3); /* if principal form, use faster cornacchia */1476if (equali1(a)) return qfbsolve_cornacchia(c, p, 0);1477if (equali1(c)) return qfbsolve_cornacchia(a, p, 1);1478}1479d = qfb_disc(Q); if (kronecker(d,p) < 0) return gen_0;1480a = redimagsl2(Q, &N);1481if (equali1(gel(a,1))) /* principal form */1482{1483long r;1484if (!signe(gel(a,2)))1485{1486a = qfbsolve_cornacchia(gel(a,3), p, 0);1487if (a == gen_0) { set_avma(av); return gen_0; }1488a = ZM_ZC_mul(N, a);1489a[0] = evaltyp(t_VEC) | _evallg(3); /* transpose */1490return gerepileupto(av, a);1491}1492/* x^2 + xy + ((1-d)/4)y^2 = p <==> (2x + y)^2 - d y^2 = 4p */1493if (!cornacchia2(negi(d), p, &x, &y)) { set_avma(av); return gen_0; }1494x = divis_rem(subii(x,y), 2, &r); if (r) { set_avma(av); return gen_0; }1495a = ZM_ZC_mul(N, mkvec2(x,y));1496a[0] = evaltyp(t_VEC) | _evallg(3); /* transpose */1497return gerepileupto(av, a);1498}1499b = redimagsl2(primeform(d, p), &M);1500if (!GL2_qfb_equal(a,b)) { set_avma(av); return gen_0; }1501if (signe(gel(a,2))==signe(gel(b,2)))1502x = SL2_div_mul_e1(N,M);1503else1504x = SL2_swap_div_mul_e1(N,M);1505return gerepilecopy(av, x);1506}15071508static GEN1509redrealsl2step(GEN A, GEN rd)1510{1511GEN N, V = gel(A,1), M = gel(A,2);1512GEN a = gel(V,1), b = gel(V,2), c = gel(V,3), d = qfb_disc(V);1513GEN C = absi_shallow(c);1514GEN t = addii(b, gmax_shallow(rd, C));1515GEN r, q = truedvmdii(t, shifti(C,1), &r);1516b = subii(t, addii(r,b));1517a = c;1518c = truedivii(subii(sqri(b), d), shifti(c,2));1519if (signe(a) < 0) togglesign(q);1520N = mkmat2(gel(M,2),1521mkcol2(subii(mulii(q, gcoeff(M, 1, 2)), gcoeff(M, 1, 1)),1522subii(mulii(q, gcoeff(M, 2, 2)), gcoeff(M, 2, 1))));1523return mkvec2(mkqfb(a,b,c,gel(V,4)), N);1524}15251526static GEN1527redrealsl2(GEN V, GEN rd)1528{1529pari_sp ltop = avma;1530GEN M, u1, u2, v1, v2;1531GEN a = gel(V,1), b = gel(V,2), c = gel(V,3), d = qfb_disc(V);1532u1 = v2 = gen_1; v1 = u2 = gen_0;1533while (!ab_isreduced(a,b,rd))1534{1535GEN C = mpabs_shallow(c);1536GEN t = addii(b, gmax_shallow(rd,C));1537GEN r, q = truedvmdii(t, shifti(C,1), &r);1538b = subii(t, addii(r,b));1539a = c;1540c = truedivii(subii(sqri(b), d), shifti(c,2));1541if (signe(a) < 0) togglesign(q);1542r = u1; u1 = v1; v1 = subii(mulii(q, v1), r);1543r = u2; u2 = v2; v2 = subii(mulii(q, v2), r);1544if (gc_needed(ltop, 1))1545{1546if (DEBUGMEM>1) pari_warn(warnmem,"redrealsl2");1547gerepileall(ltop, 7, &a,&b,&c,&u1,&u2,&v1,&v2);1548}1549}1550M = mkmat2(mkcol2(u1,u2), mkcol2(v1,v2));1551return gerepilecopy(ltop, mkvec2(lg(V)==5?mkqfb(a,b,c,gel(V,4))1552:mkvec3(a,b,c), M));1553}15541555GEN1556qfbredsl2(GEN q, GEN isD)1557{1558pari_sp av;1559if (typ(q) != t_QFB) pari_err_TYPE("qfbredsl2",q);1560if (qfb_is_qfi(q))1561{1562GEN v = cgetg(3,t_VEC);1563if (isD) pari_err_TYPE("qfbredsl2", isD);1564gel(v,1) = redimagsl2(q, &gel(v,2)); return v;1565}1566av = avma;1567if (!isD) isD = sqrti(qfb_disc(q));1568else if (typ(isD) != t_INT) pari_err_TYPE("qfbredsl2",isD);1569return gerepileupto(av, redrealsl2(q, isD));1570}15711572static GEN1573qfrsolve_normform(GEN N, GEN Ps, GEN rd)1574{1575pari_sp av = avma, btop;1576GEN M = N, P = redrealsl2(Ps, rd), Q = P;15771578btop = avma;1579for(;;)1580{1581if (qfb_equal(gel(M,1), gel(P,1)))1582return gerepilecopy(av, SL2_div_mul_e1(gel(M,2),gel(P,2)));1583if (qfb_equal(gel(N,1), gel(Q,1)))1584return gerepilecopy(av, SL2_div_mul_e1(gel(N,2),gel(Q,2)));1585M = redrealsl2step(M, rd);1586if (qfb_equal(gel(M,1), gel(N,1))) return gc_NULL(av);1587Q = redrealsl2step(Q, rd);1588if (qfb_equal(gel(P,1), gel(Q,1))) return gc_NULL(av);1589if (gc_needed(btop, 1)) gerepileall(btop, 2, &M, &Q);1590}1591}15921593GEN1594qfrsolvep(GEN Q, GEN p)1595{1596pari_sp av = avma;1597GEN N, x, rd, d = qfb_disc(Q);15981599if (kronecker(d, p) < 0) return gc_const(av, gen_0);1600rd = sqrti(d);1601N = redrealsl2(Q, rd);1602x = qfrsolve_normform(N, primeform(d, p), rd);1603return x? gerepileupto(av, x): gc_const(av, gen_0);1604}16051606static GEN1607qfsolve_normform(GEN Q, GEN f, GEN rd)1608{ return rd? qfrsolve_normform(Q, f, rd): qfisolve_normform(Q, f); }1609static GEN1610qfbsolve_primitive_i(GEN Q, GEN rd, GEN *Qr, GEN fa, long all)1611{1612GEN x, W, F = normforms(qfb_disc(Q), fa);1613long i, j, l;1614if (!F) return NULL;1615if (!*Qr) *Qr = qfbredsl2(Q, rd);1616l = lg(F); W = all? cgetg(l, t_VEC): NULL;1617for (j = i = 1; i < l; i++)1618if ((x = qfsolve_normform(*Qr, gel(F,i), rd)))1619{1620if (!all) return x;1621gel(W,j++) = x;1622}1623if (j == 1) return NULL;1624setlg(W,j); return W;1625}16261627static GEN1628qfb_initrd(GEN Q) { GEN d = qfb_disc(Q); return signe(d) > 0? sqrti(d): NULL; }1629static GEN1630qfbsolve_primitive(GEN Q, GEN fa, long all)1631{1632GEN x, Qr = NULL, rdQ = qfb_initrd(Q);1633x = qfbsolve_primitive_i(Q, rdQ, &Qr, fa, all);1634if (!x) return cgetg(1, t_VEC);1635return x;1636}16371638/* f / g^2 */1639static GEN1640famat_divsqr(GEN f, GEN g)1641{ return famat_reduce(famat_div_shallow(f, famat_pows_shallow(g,2))); }1642static GEN1643qfbsolve_all(GEN Q, GEN n, long all)1644{1645GEN W, Qr = NULL, fa = factorint(n, 0), rdQ = qfb_initrd(Q);1646GEN D = divisors_factored(mkmat2(gel(fa,1), gshift(gel(fa,2),-1)));1647long i, j, l = lg(D);1648W = all? cgetg(l, t_VEC): NULL;1649for (i = j = 1; i < l; i++)1650{1651GEN w, d = gel(D,i), FA = i == 1? fa: famat_divsqr(fa, gel(d,2));1652if ((w = qfbsolve_primitive_i(Q, rdQ, &Qr, FA, all)))1653{1654if (i != 1) w = RgV_Rg_mul(w, gel(d,1));1655if (!all) return w;1656gel(W,j++) = w;1657}1658}1659if (j == 1) return cgetg(1, t_VEC);1660setlg(W,j); return shallowconcat1(W);1661}16621663GEN1664qfbsolve(GEN Q, GEN n, long fl)1665{1666pari_sp av = avma;1667if (typ(Q) != t_QFB) pari_err_TYPE("qfbsolve",Q);1668if (fl < 0 || fl > 3) pari_err_FLAG("qfbsolve");1669return gerepilecopy(av, (fl & 2)? qfbsolve_all(Q, n, fl & 1)1670: qfbsolve_primitive(Q, n, fl & 1));1671}16721673/* 1 if there exists x,y such that x^2 + dy^2 = p [prime], 0 otherwise */1674long1675cornacchia(GEN d, GEN p, GEN *px, GEN *py)1676{1677pari_sp av = avma;1678GEN a, b, c, r;16791680if (typ(d) != t_INT) pari_err_TYPE("cornacchia", d);1681if (typ(p) != t_INT) pari_err_TYPE("cornacchia", p);1682if (signe(d) <= 0) pari_err_DOMAIN("cornacchia", "d","<=",gen_0,d);1683*px = *py = gen_0;1684b = subii(p, d);1685if (signe(b) < 0) return 0;1686if (signe(b) == 0) { *py = gen_1; return gc_long(av,1); }1687b = Fp_sqrt(b, p); /* sqrt(-d) */1688if (!b) return gc_long(av,0);1689b = gmael(halfgcdii(p, b), 2, 2);1690a = subii(p, sqri(b));1691c = dvmdii(a, d, &r);1692if (r != gen_0 || !Z_issquareall(c, &c)) return gc_long(av,0);1693set_avma(av);1694*px = icopy(b);1695*py = icopy(c); return 1;1696}16971698static GEN1699lastqi(GEN Q)1700{1701GEN s = gcoeff(Q,1,1), q = gcoeff(Q,1,2), p = absi_shallow(gcoeff(Q,2,2));1702if (signe(q)==0) return gen_0;1703if (signe(s)==0) return p;1704if (is_pm1(q)) return subiu(p,1);1705return divii(p, absi_shallow(q));1706}17071708static long1709cornacchia2_helper(long av, GEN d, GEN p, GEN b, GEN px4, GEN *px, GEN *py)1710{1711GEN M, Q, V, a, c, r;1712if (!signe(b)) { /* d = p,2p,3p,4p */1713set_avma(av);1714if (absequalii(d, px4)){ *py = gen_1; return 1; }1715if (absequalii(d, p)) { *py = gen_2; return 1; }1716return 0;1717}1718if (mod2(b) != mod2(d)) b = subii(p,b);1719M = halfgcdii(shifti(p,1), b); Q = gel(M,1); V = gel(M, 2);1720b = addii(mulii(gel(V,1), lastqi(Q)),gel(V,2));1721if (cmpii(sqri(b),px4) > 0) b = gel(V,1);1722if (cmpii(sqri(b),px4) > 0) b = gel(V,2);1723a = subii(px4, sqri(b));1724c = dvmdii(a, d, &r);1725if (r != gen_0 || !Z_issquareall(c, &c)) return gc_long(av,0);1726set_avma(av);1727*px = icopy(b);1728*py = icopy(c); return 1;1729}17301731/* 1 if there exists x,y such that x^2 + dy^2 = 4p [p prime], 0 otherwise */1732long1733cornacchia2(GEN d, GEN p, GEN *px, GEN *py)1734{1735pari_sp av = avma;1736GEN b, px4;1737long k;17381739if (typ(d) != t_INT) pari_err_TYPE("cornacchia2", d);1740if (typ(p) != t_INT) pari_err_TYPE("cornacchia2", p);1741if (signe(d) <= 0) pari_err_DOMAIN("cornacchia2", "d","<=",gen_0,d);1742*px = *py = gen_0;1743k = mod4(d);1744if (k == 1 || k == 2) pari_err_DOMAIN("cornacchia2","-d mod 4", ">",gen_1,d);1745px4 = shifti(p,2);1746if (abscmpii(px4, d) < 0) return gc_long(av,0);1747if (absequaliu(p, 2))1748{1749set_avma(av);1750switch (itou_or_0(d)) {1751case 4: *px = gen_2; break;1752case 7: *px = gen_1; break;1753default: return 0;1754}1755*py = gen_1; return 1;1756}1757b = Fp_sqrt(negi(d), p);1758if (!b) return gc_long(av,0);1759return cornacchia2_helper(av, d, p, b, px4, px, py);1760}17611762/* 1 if there exists x,y such that x^2 + dy^2 = 4p [p prime], 0 otherwise */1763long1764cornacchia2_sqrt(GEN d, GEN p, GEN b, GEN *px, GEN *py)1765{1766pari_sp av = avma;1767GEN px4;1768*px = *py = gen_0;1769px4 = shifti(p,2);1770if (abscmpii(px4, d) < 0) return gc_long(av,0);1771return cornacchia2_helper(av, d, p, b, px4, px, py);1772}177317741775