Testing latest pari + WASM + node.js... and it works?! Wow.
License: GPL3
ubuntu2004
#line 2 "../src/kernel/none/ratlift.c"1/* Copyright (C) 2003 The PARI group.23This file is part of the PARI/GP package.45PARI/GP is free software; you can redistribute it and/or modify it under the6terms of the GNU General Public License as published by the Free Software7Foundation; either version 2 of the License, or (at your option) any later8version. It is distributed in the hope that it will be useful, but WITHOUT9ANY WARRANTY WHATSOEVER.1011Check the License for details. You should have received a copy of it, along12with the package; see the file 'COPYING'. If not, write to the Free Software13Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */1415/*==========================================================16* Fp_ratlift(GEN x, GEN m, GEN *a, GEN *b, GEN amax, GEN bmax)17*==========================================================18* Reconstruct rational number from its residue x mod m19* Given t_INT x, m, amax>=0, bmax>0 such that20* 0 <= x < m; 2*amax*bmax < m21* attempts to find t_INT a, b such that22* (1) a = b*x (mod m)23* (2) |a| <= amax, 0 < b <= bmax24* (3) gcd(m, b) = gcd(a, b)25* If unsuccessful, it will return 0 and leave a,b unchanged (and26* caller may deduce no such a,b exist). If successful, sets a,b27* and returns 1. If there exist a,b satisfying (1), (2), and28* (3') gcd(m, b) = 129* then they are uniquely determined subject to (1),(2) and30* (3'') gcd(a, b) = 1,31* and will be returned by the routine. (The caller may wish to32* check gcd(a,b)==1, either directly or based on known prime33* divisors of m, depending on the application.)34* Reference:35@article {MR97c:11116,36AUTHOR = {Collins, George E. and Encarnaci{\'o}n, Mark J.},37TITLE = {Efficient rational number reconstruction},38JOURNAL = {J. Symbolic Comput.},39VOLUME = {20},40YEAR = {1995},41NUMBER = {3},42PAGES = {287--297},43}44* Preprint available from:45* ftp://ftp.risc.uni-linz.ac.at/pub/techreports/1994/94-64.ps.gz */46static ulong47get_vmax(GEN r, long lb, long lbb)48{49long lr = lb - lgefint(r);50ulong vmax;51if (lr > 1) /* still more than a word's worth to go */52vmax = ULONG_MAX; /* (cannot in fact happen) */53else54{ /* take difference of bit lengths */55long lbr = bfffo(*int_MSW(r));56lr = lr*BITS_IN_LONG - lbb + lbr;57if ((ulong)lr > BITS_IN_LONG)58vmax = ULONG_MAX;59else if (lr == 0)60vmax = 1UL;61else62vmax = 1UL << (lr-1); /* pessimistic but faster than a division */63}64return vmax;65}6667/* Assume x,m,amax >= 0,bmax > 0 are t_INTs, 0 <= x < m, 2 amax * bmax < m */68int69Fp_ratlift(GEN x, GEN m, GEN amax, GEN bmax, GEN *a, GEN *b)70{71GEN d, d1, v, v1, q, r;72pari_sp av = avma, av1;73long lb, lbb, s, s0;74ulong vmax;75ulong xu, xu1, xv, xv1; /* Lehmer stage recurrence matrix */76int lhmres; /* Lehmer stage return value */7778/* special cases x=0 and/or amax=0 */79if (!signe(x)) { *a = gen_0; *b = gen_1; return 1; }80if (!signe(amax)) return 0;81/* assert: m > x > 0, amax > 0 */8283/* check whether a=x, b=1 is a solution */84if (cmpii(x,amax) <= 0) { *a = icopy(x); *b = gen_1; return 1; }8586/* There is no special case for single-word numbers since this is87* mainly meant to be used with large moduli. */88(void)new_chunk(lgefint(bmax) + lgefint(amax)); /* room for a,b */89d = m; d1 = x;90v = gen_0; v1 = gen_1;91/* assert d1 > amax, v1 <= bmax here */92lb = lgefint(bmax);93lbb = bfffo(*int_MSW(bmax));94s = 1;95av1 = avma;9697/* General case: Euclidean division chain starting with m div x, and98* with bounds on the sequence of convergents' denoms v_j.99* Just to be different from what invmod and bezout are doing, we work100* here with the all-nonnegative matrices [u,u1;v,v1]=prod_j([0,1;1,q_j]).101* Loop invariants:102* (a) (sign)*[-v,v1]*x = [d,d1] (mod m) (componentwise)103* (sign initially +1, changes with each Euclidean step)104* so [a,b] will be obtained in the form [-+d,v] or [+-d1,v1];105* this congruence is a consequence of106*107* (b) [x,m]~ = [u,u1;v,v1]*[d1,d]~,108* where u,u1 is the usual numerator sequence starting with 1,0109* instead of 0,1 (just multiply the eqn on the left by the inverse110* matrix, which is det*[v1,-u1;-v,u], where "det" is the same as the111* "(sign)" in (a)). From m = v*d1 + v1*d and112*113* (c) d > d1 >= 0, 0 <= v < v1,114* we have d >= m/(2*v1), so while v1 remains smaller than m/(2*amax),115* the pair [-(sign)*d,v] satisfies (1) but violates (2) (d > amax).116* Conversely, v1 > bmax indicates that no further solutions will be117* forthcoming; [-(sign)*d,v] will be the last, and first, candidate.118* Thus there's at most one point in the chain division where a solution119* can live: v < bmax, v1 >= m/(2*amax) > bmax, and this is acceptable120* iff in fact d <= amax (e.g. m=221, x=34 or 35, amax=bmax=10 fail on121* this count while x=32,33,36,37 succeed). However, a division may leave122* a zero residue before we ever reach this point (consider m=210, x=35,123* amax=bmax=10), and our caller may find that gcd(d,v) > 1 (Examples:124* keep m=210 and consider any of x=29,31,32,33,34,36,37,38,39,40,41).125* Furthermore, at the start of the loop body we have in fact126*127* (c') 0 <= v < v1 <= bmax, d > d1 > amax >= 0,128* (and are never done already).129*130* Main loop is similar to those of invmod() and bezout(), except for131* having to determine appropriate vmax bounds, and checking termination132* conditions. The signe(d1) condition is only for paranoia */133while (lgefint(d) > 3 && signe(d1))134{135/* determine vmax for lgcdii so as to ensure v won't overshoot.136* If v+v1 > bmax, the next step would take v1 beyond the limit, so137* since [+-d1,v1] is not a solution, we give up. Otherwise if v+v1138* is way shorter than bmax, use vmax=MAXULUNG. Otherwise, set vmax139* to a crude lower approximation of bmax/(v+v1), or to 1, which will140* allow the inner loop to do one step */141r = addii(v,v1);142if (cmpii(r,bmax) > 0) return gc_long(av, 0); /* done, not found */143vmax = get_vmax(r, lb, lbb);144/* do a Lehmer-Jebelean round */145lhmres = lgcdii((ulong *)d, (ulong *)d1, &xu, &xu1, &xv, &xv1, vmax);146if (lhmres) /* check progress */147{ /* apply matrix */148if (lhmres == 1 || lhmres == -1)149{150s = -s;151if (xv1 == 1)152{ /* re-use v+v1 computed above */153v = v1; v1 = r;154r = subii(d,d1); d = d1; d1 = r;155}156else157{158r = subii(d, mului(xv1,d1)); d = d1; d1 = r;159r = addii(v, mului(xv1,v1)); v = v1; v1 = r;160}161}162else163{164r = subii(muliu(d,xu), muliu(d1,xv));165d1 = subii(muliu(d,xu1), muliu(d1,xv1)); d = r;166r = addii(muliu(v,xu), muliu(v1,xv));167v1 = addii(muliu(v,xu1), muliu(v1,xv1)); v = r;168if (lhmres&1) { togglesign(d); s = -s; } else togglesign(d1);169}170/* check whether we're done. Assert v <= bmax here. Examine v1:171* if v1 > bmax, check d and return 0 or 1 depending on the outcome;172* if v1 <= bmax, check d1 and return 1 if d1 <= amax, otherwise proceed*/173if (cmpii(v1,bmax) > 0)174{175set_avma(av);176if (cmpii(d,amax) > 0) return 0; /* done, not found */177/* done, found */178*a = icopy(d); setsigne(*a,-s);179*b = icopy(v); return 1;180}181if (cmpii(d1,amax) <= 0)182{ /* done, found */183set_avma(av);184if (signe(d1)) { *a = icopy(d1); setsigne(*a,s); } else *a = gen_0;185*b = icopy(v1); return 1;186}187} /* lhmres != 0 */188189if (lhmres <= 0 && signe(d1))190{191q = dvmdii(d,d1,&r);192d = d1; d1 = r;193r = addii(v, mulii(q,v1));194v = v1; v1 = r;195s = -s;196/* check whether we are done now. Since we weren't before the div, it197* suffices to examine v1 and d1 -- the new d (former d1) cannot cut it */198if (cmpii(v1,bmax) > 0) return gc_long(av, 0); /* done, not found */199if (cmpii(d1,amax) <= 0) /* done, found */200{201set_avma(av);202if (signe(d1)) { *a = icopy(d1); setsigne(*a,s); } else *a = gen_0;203*b = icopy(v1); return 1;204}205}206207if (gc_needed(av,1))208{209if(DEBUGMEM>1) pari_warn(warnmem,"ratlift");210gerepileall(av1, 4, &d, &d1, &v, &v1);211}212} /* end while */213214/* Postprocessing - final sprint. Since we usually underestimate vmax,215* this function needs a loop here instead of a simple conditional.216* Note we can only get here when amax fits into one word (which will217* typically not be the case!). The condition is bogus -- d1 is never218* zero at the start of the loop. There will be at most a few iterations,219* so we don't bother collecting garbage */220while (signe(d1))221{222/* Assertions: lgefint(d)==lgefint(d1)==3.223* Moreover, we aren't done already, or we would have returned by now.224* Recompute vmax */225r = addii(v,v1);226if (cmpii(r,bmax) > 0) return gc_long(av, 0); /* done, not found */227vmax = get_vmax(r, lb, lbb);228/* single-word "Lehmer", discarding the gcd or whatever it returns */229(void)rgcduu((ulong)*int_MSW(d), (ulong)*int_MSW(d1), vmax, &xu, &xu1, &xv, &xv1, &s0);230if (xv1 == 1) /* avoid multiplications */231{ /* re-use r = v+v1 computed above */232v = v1; v1 = r;233r = subii(d,d1); d = d1; d1 = r;234s = -s;235}236else if (xu == 0) /* and xv==1, xu1==1, xv1 > 1 */237{238r = subii(d, mului(xv1,d1)); d = d1; d1 = r;239r = addii(v, mului(xv1,v1)); v = v1; v1 = r;240s = -s;241}242else243{244r = subii(muliu(d,xu), muliu(d1,xv));245d1 = subii(muliu(d,xu1), muliu(d1,xv1)); d = r;246r = addii(muliu(v,xu), muliu(v1,xv));247v1 = addii(muliu(v,xu1), muliu(v1,xv1)); v = r;248if (s0 < 0) { togglesign(d); s = -s; } else togglesign(d1);249}250/* check whether we're done, as above. Assert v <= bmax.251* if v1 > bmax, check d and return 0 or 1 depending on the outcome;252* if v1 <= bmax, check d1 and return 1 if d1 <= amax, otherwise proceed.253*/254if (cmpii(v1,bmax) > 0)255{256set_avma(av);257if (cmpii(d,amax) > 0) return 0; /* done, not found */258/* done, found */259*a = icopy(d); setsigne(*a,-s);260*b = icopy(v); return 1;261}262if (cmpii(d1,amax) <= 0)263{ /* done, found */264set_avma(av);265if (signe(d1)) { *a = icopy(d1); setsigne(*a,s); } else *a = gen_0;266*b = icopy(v1); return 1;267}268} /* while */269270/* We have run into d1 == 0 before returning. This cannot happen */271pari_err_BUG("ratlift failed to catch d1 == 0");272return 0; /* LCOV_EXCL_LINE */273}274275276