// cl_UDS_sqrt(). // General includes. #include "cl_sysdep.h" // Specification. #include "cl_DS.h" // Implementation. #include "cl_low.h" #include "cln/abort.h" namespace cln { // We observe the following timings: // Time for square root of a_len = 2*N by b_len = N digits, // OS: Linux 2.2, intDsize==32, OS: TRU64/4.0, intDsize==64, // Machine: P-III/450MHz Machine: EV5/300MHz: // N standard Newton standard Newton // 30 0.00002 0.00009 0.00011 0.00027 // 100 0.00012 0.00052 0.00057 0.0017 // 300 0.00087 0.0031 0.0037 0.0091 // 1000 0.0089 0.020 0.037 0.069 // 3000 0.087 0.11 <-(~3200) 0.30 0.28 <- (~2750) // 10000 1.27 0.55 3.5 1.3 // 30000 12.7 1.35 31.1 3.4 // Newton faster for 3200= 3200); } #else // Use the old default values from CLN version <= 1.0.3 as a crude estimate. // Time for square root of a_len = 2*N by b_len = N digits, // on a i486 33 MHz running Linux: // N standard Newton // 10 0.00022 0.00132 // 25 0.00082 0.0047 // 50 0.0026 0.0130 // 100 0.0095 0.038 // 250 0.057 0.154 // 500 0.22 0.46 // 1000 0.90 1.39 // 2500 6.0 4.6 // 5000 24.1 10.7 // 10000 98 23.2 // -----> Newton faster for 1570 <= N <= 1790 and for N >= 2100. static inline cl_boolean cl_recipsqrt_suitable (uintL n) { return (cl_boolean)(n >= 2100); } #endif // Bildet zu einer Unsigned Digit sequence a die Wurzel // (genauer: Gaußklammer aus Wurzel aus a). // squarep = cl_UDS_sqrt(a_MSDptr,a_len,a_LSDptr, &b); // > a_MSDptr/a_len/a_LSDptr: eine UDS // < NUDS b: Gaußklammer der Wurzel aus a // < squarep: cl_true falls a = b^2, cl_false falls b^2 < a < (b+1)^2. // Methode: // erst A normalisieren. A=0 --> B=0, fertig. // Wähle n so, daß beta^(2n-2) <= A < beta^(2n). // Wähle s (0<=s<16) so, daß beta^(2n)/4 <= A*2^(2s) < beta^(2n). // Setze A:=A*2^(2s) und kopiere dabei A. Suche B=floor(sqrt(A)). // Mache Platz für B=[0,b[n-1],...,b[0]], (mit einem Nulldigit Platz davor, // da dort nicht B, sondern 2*B abgespeichert werden wird). // Auf den Plätzen [a[2n-1],...,a[2n-2j]] wird die Differenz // [a[2n-1],...,a[2n-2j]] - [b[n-1],...,b[n-j]] ^ 2 abgespeichert. // Bestimme b[n-1] = floor(sqrt(a[2n-1]*beta+a[2n-2])) mit Heron/Newton: // {x:=beta als vorheriger Anfangswert, dann:} // x := floor((beta+a[2n-1])/2) // wiederhole: d:=floor((a[2n-1]*beta+a[2n-2])/x). // Falls d=beta/4 ist b[n-1]>=beta/2.} // Erniedrige [a[2n-1],a[2n-2]] um b[n-1]^2. // Für j=1,...,n: // {Hier [b[n-1],...,b[n-j]] = floor(sqrt(altes [a[2n-1],...,a[2n-2j]])), // in [a[2n-1],...,a[2n-2j]] steht jetzt der Rest // [a[2n-1],...,a[2n-2j]] - [b[n-1],...,b[n-j]]^2, er ist >=0 und // und <= 2 * [b[n-1],...,b[n-j]], belegt daher höchstens j Digits und 1 Bit. // Daher sind nur [a[2n-j],...,a[2n-2j]] von Belang.} // Für j= 0). // Im einzelnen: // b* := min(beta-1,floor([a[2n-j],a[2n-j-1],a[2n-j-2]]/(2*b[n-1]))), // [a[2n-j],...,a[2n-2j-1]] wie angegeben erniedigen. // Solange die Differenz <0 ist, setze b* := b* - 1 und // erhöhe [a[2n-j],...,a[2n-2j-1]] um 2 * [b[n-1],...,b[n-j]]. // Erniedrige [a[2n-j],...,a[2n-2j-2]] um b* ^ 2. // Tritt dabei ein negativer Carry auf, // so setze b* := b* - 1, // setze b[n-j-1] := b* (im Speicher um 1 Bit nach links verschoben), // erhöhe [a[2n-j],...,a[2n-2j-2]] um 2*[b[n-1],...,b[n-j-1]]+1. // Sonst setze b[n-j-1] := b* (im Speicher um 1 Bit nach links verschoben). // Nächstes j. // Für j=n: // Falls [a[n],...,a[0]] = [0,...,0], ist die Wurzel exakt, sonst nicht. // Ergebnis ist [b[n-1],...,b[0]] * 2^(-s), schiebe also im Speicher // [b[n],...,b[0]] um s+1 Bits nach rechts. // Das Ergebnis ist eine NUDS der Länge n. cl_boolean cl_UDS_sqrt (const uintD* a_MSDptr, uintC a_len, const uintD* a_LSDptr, DS* b_) { // A normalisieren: while ((a_len>0) && (mspref(a_MSDptr,0)==0)) { msshrink(a_MSDptr); a_len--; } if (a_len==0) // A=0 -> B := NUDS 0 { b_->LSDptr = b_->MSDptr; b_->len = 0; return cl_true; } CL_ALLOCA_STACK; // n und s bestimmen: var uintC n = ceiling(a_len,2); // a_len = 2n oder 2n-1, n>0. var uintL s; { var uintD msd = mspref(a_MSDptr,0); // a[2n] bzw. a[2n-1] #if 0 s = 0; while /* ((msd & (bit(intDsize-1)|bit(intDsize-2))) ==0) */ (((sintD)msd >= 0) && ((sintD)(msd<<1) >= 0)) { msd = msd<<2; s++; } #else integerlengthD(msd, s = intDsize - ); s = s>>1; #endif } // Noch ist s nur modulo intDsize/2 bestimmt. // A um 2s Bits nach links verschoben kopieren: var uintD* new_a_MSDptr; { var uintD* new_a_LSDptr; num_stack_alloc(2*(uintL)n,new_a_MSDptr=,new_a_LSDptr=); // 2n Digits Platz belegen {var uintL shiftcount = 2*s; if (!((a_len & bit(0)) ==0)) // a_len ungerade? { s += intDsize/2; lsprefnext(new_a_LSDptr) = 0; } // ja -> ein Nulldigit einschieben if (shiftcount==0) { copy_loop_lsp(a_LSDptr,new_a_LSDptr,a_len); } else { shiftleftcopy_loop_lsp(a_LSDptr,new_a_LSDptr,a_len,shiftcount); } }} #define a_MSDptr new_a_MSDptr // Nun ist A = a_MSDptr/2n/.. if (cl_recipsqrt_suitable(n)) { // C := 1/sqrt(A) und dann D := A*C näherungsweise errechnen. // D evtl. korrigieren, liefert B. var uintD* c_MSDptr; var uintD* c_LSDptr; var uintD* d_MSDptr; var uintD* d_LSDptr; var uintD* d2_MSDptr; num_stack_alloc(n+2, c_MSDptr=,c_LSDptr=); num_stack_alloc(2*n+3, d_MSDptr=,d_LSDptr=); num_stack_alloc(2*n, d2_MSDptr=,); // 1/4 <= a < 1. cl_UDS_recipsqrt(a_MSDptr,2*n,c_MSDptr,n); // 1 <= c <= 2, | 1/sqrt(a) - c | < 1/2*beta^-n. cl_UDS_mul(a_MSDptr mspop (n+1),n+1,c_LSDptr,n+2,d_LSDptr); // 1/4 <= d < 2, | sqrt(a) - d | < beta^-n. if (mspref(d_MSDptr,0) > 0) { dec_loop_lsp(d_MSDptr mspop (n+1),n+1); if (mspref(d_MSDptr,0) > 0) cl_abort(); } // D is our guess for B. Square to see how much we have to correct. cl_UDS_mul_square(d_MSDptr mspop (1+n),n,d2_MSDptr mspop 2*n); // Store D. b_->LSDptr = copy_loop_msp(d_MSDptr mspop 1,b_->MSDptr,n); b_->len = n; // Store 2*D in place of D. if (shift1left_loop_lsp(d_MSDptr mspop (1+n),n)) mspref(d_MSDptr,0) = 1; // Compare D^2 against A. if (subfrom_loop_lsp(d2_MSDptr mspop 2*n,a_MSDptr mspop 2*n,2*n)) // guessed too high, decrement D { dec_loop_lsp(b_->LSDptr,n); dec_loop_lsp(d_MSDptr mspop (1+n),1+n); // store 2*D+1 if (!addto_loop_lsp(d_MSDptr mspop (1+n),a_MSDptr mspop 2*n,1+n)) cl_abort(); if (!inc_loop_lsp(a_MSDptr mspop (n-1),n-1)) cl_abort(); } else if (test_loop_msp(a_MSDptr,n-1)) // guessed way too low cl_abort(); else if (compare_loop_msp(a_MSDptr mspop (n-1),d_MSDptr,1+n) > 0) // guessed too low, increment D { inc_loop_lsp(b_->LSDptr,n); mspref(d_MSDptr,n) |= bit(0); // store 2*D-1 subfrom_loop_lsp(d_MSDptr mspop (1+n),a_MSDptr mspop 2*n,1+n); inc_loop_lsp(d_MSDptr mspop (1+n),1+n); // store 2*D if (compare_loop_msp(a_MSDptr mspop (n-1),d_MSDptr,1+n) > 0) cl_abort(); } else // guessed ok {} // Schiebe b um s Bits nach rechts: if (s > 0) shiftright_loop_msp(b_->MSDptr,n,s); // Teste, ob alle a[n],...,a[0]=0 sind: if (test_loop_msp(a_MSDptr mspop (n-1),n+1)) return cl_false; else return cl_true; // ja -> Wurzel exakt } // Platz für B belegen: { var uintD* b_MSDptr = b_->MSDptr mspop -1; // ab hier n+1 Digits Platz var uintD b_msd; // B = [0,b[n-1],...,b[0]] = b_MSDptr/n+1/.. // Bestimmung von b[n-1]: { var uintD a_msd = mspref(a_MSDptr,0); // a[2n-1] var uintD a_2msd = mspref(a_MSDptr,1); // a[2n-2] #if HAVE_DD var uintDD a_msdd = highlowDD(a_msd,a_2msd); // a[2n-1]*beta+a[2n-2] #endif // Anfangswert: x := floor((beta + a[2n-1])/2) var uintD x = floor(a_msd,2) | bit(intDsize-1); loop // Heron-Iterationsschleife { var uintD d; // Dividiere d := floor((a[2n-1]*beta+a[2n-2])/x) : if (a_msd>=x) break; // Überlauf -> d>=beta -> fertig #if HAVE_DD divuD(a_msdd,x, d=,); #else divuD(a_msd,a_2msd,x, d=,); #endif if (d >= x) break; // d>=x -> fertig // Nächste Iteration: x := floor((x+d)/2) // (Da die Folge der x bekanntlich monoton fallend ist // und bei b[n-1] >= beta/2 endet, muß x >= beta/2 werden, // d.h. x+d>=beta.) #if HAVE_DD x = (uintD)(floor((uintDD)x + (uintDD)d, 2)); #else x = floor((uintD)(x+d),2) | bit(intDsize-1); #endif } // x = b[n-1] fertig berechnet. b_msd = x; // Quadrieren und von [a[2n-1],a[2n-2]] abziehen: #if HAVE_DD a_msdd -= muluD(x,x); mspref(a_MSDptr,0) = highD(a_msdd); mspref(a_MSDptr,1) = lowD(a_msdd); #else {var uintD x2hi; var uintD x2lo; muluD(x,x, x2hi=,x2lo=); mspref(a_MSDptr,0) = a_msd - x2hi; if (a_2msd < x2lo) { mspref(a_MSDptr,0) -= 1; } mspref(a_MSDptr,1) = a_2msd - x2lo; } #endif mspref(b_MSDptr,0) = 1; mspref(b_MSDptr,1) = x<<1; // b[n-1] ablegen } {var uintC j = 0; var uintD* a_mptr = a_MSDptr mspop 0; var uintD* a_lptr = a_MSDptr mspop 2; var uintD* b_ptr = b_MSDptr mspop 2; // Wurzel-Hauptschleife until (++j == n) // j=1,...,n { // b_MSDptr = Pointer auf b[n], b_ptr = Pointer hinter b[n-j]. // a_mptr = Pointer auf a[2n-j], a_lptr = Pointer hinter a[2n-2j]. // Bestimme b* : var uintD b_stern; { var uintD a_1d = mspref(a_mptr,0); // a[2n-j], =0 oder =1 var uintD a_2d = mspref(a_mptr,1); // a[2n-j-1] var uintD a_3d = mspref(a_mptr,2); // a[2n-j-2] // a[2n-j]*beta^2+a[2n-j-1]*beta+a[2n-j-2] durch 2 dividieren, // dann durch b_msd = b[n-1] dividieren: #if HAVE_DD var uintDD a_123dd = highlowDD(a_2d,a_3d); a_123dd = a_123dd>>1; if (!(a_1d==0)) { a_123dd |= bit(2*intDsize-1); } if (highD(a_123dd) >= b_msd) { b_stern = bitm(intDsize)-1; } // bei Überlauf: beta-1 else { divuD(a_123dd,b_msd, b_stern=,); } #else a_3d = a_3d>>1; if (!((a_2d & bit(0)) ==0)) { a_3d |= bit(intDsize-1); } a_2d = a_2d>>1; if (!(a_1d==0)) { a_2d |= bit(intDsize-1); } if (a_2d >= b_msd) { b_stern = bitm(intDsize)-1; } // bei Überlauf: beta-1 else { divuD(a_2d,a_3d,b_msd, b_stern=,); } #endif } // b_stern = b* in der ersten Schätzung. a_lptr = a_lptr mspop 1; // Pointer hinter a[2n-2j-1] // Subtraktion [a[2n-j],...,a[2n-2j-1]] -= b* * [b[n],b[n-1],...,b[n-j]] : { var uintD carry = mulusub_loop_lsp(b_stern,b_ptr,a_lptr,j+1); if (mspref(a_mptr,0) >= carry) { mspref(a_mptr,0) -= carry; } else { mspref(a_mptr,0) -= carry; // a[2n-j] wird <0 // negativer Übertrag -> b* nach unten korrigieren: loop { b_stern = b_stern-1; // b* := b* - 1 // erhöhe [a[2n-j],...,a[2n-2j-1]] um [b[n],...,b[n-j]]: if (!(( addto_loop_lsp(b_ptr,a_lptr,j+1) ==0))) if ((mspref(a_mptr,0) += 1) ==0) // Übertrag zu a[2n-j] break; // macht a[2n-j] wieder >=0 -> Subtraktionsergebnis >=0 } } } // b_stern = b* in der zweiten Schätzung. a_mptr = a_mptr mspop 1; // Pointer auf a[2n-j-1] a_lptr = a_lptr mspop 1; // Pointer hinter a[2n-2j-2] // Ziehe b* ^ 2 von [a[2n-j],...,a[2n-2j-2]] ab: #if HAVE_DD { var uintDD b_stern_2 = muluD(b_stern,b_stern); var uintDD a_12dd = highlowDD(lspref(a_lptr,1),lspref(a_lptr,0)); // a[2n-2j-1]*beta+a[2n-2j-2] var uintDD a_12dd_new = a_12dd - b_stern_2; lspref(a_lptr,1) = highD(a_12dd_new); lspref(a_lptr,0) = lowD(a_12dd_new); if (a_12dd >= b_stern_2) goto b_stern_ok; } #else { var uintD b_stern_2_hi; var uintD b_stern_2_lo; muluD(b_stern,b_stern, b_stern_2_hi=,b_stern_2_lo=); {var uintD a_1d = lspref(a_lptr,1); // a[2n-2j-1] var uintD a_2d = lspref(a_lptr,0); // a[2n-2j-2] var uintD a_1d_new = a_1d - b_stern_2_hi; var uintD a_2d_new = a_2d - b_stern_2_lo; if (a_2d < b_stern_2_lo) { a_1d_new -= 1; } lspref(a_lptr,1) = a_1d_new; lspref(a_lptr,0) = a_2d_new; if ((a_1d > b_stern_2_hi) || ((a_1d == b_stern_2_hi) && (a_2d >= b_stern_2_lo)) ) goto b_stern_ok; }} #endif if (TRUE) { // muß noch [a[2n-j],...,a[2n-2j]] um 1 erniedrigen: if ( dec_loop_lsp(a_lptr lspop 2,j+1) ==0) goto b_stern_ok; // Subtraktion von b*^2 lieferte negativen Carry b_stern = b_stern-1; // b* := b* - 1 // erhöhe [a[2n-j-1],...,a[2n-2j-2]] um [b[n],...,b[n-j],0] + 2 * b* + 1 if ((sintD)b_stern < 0) { mspref(b_ptr,-1) |= bit(0); } // höchstes Bit von b* in b[n-j] ablegen mspref(b_ptr,0) = (uintD)(b_stern<<1)+1; // niedrige Bits von b* und eine 1 als b[n-j-1] ablegen addto_loop_lsp(b_ptr mspop 1,a_lptr,j+2); // (a[2n-j] wird nicht mehr gebraucht.) mspref(b_ptr,0) -= 1; // niedrige Bits von b* in b[n-j-1] ablegen b_ptr = b_ptr mspop 1; } else b_stern_ok: { // b* als b[n-j-1] ablegen: if ((sintD)b_stern < 0) { mspref(b_ptr,-1) |= bit(0); } // höchstes Bit von b* in b[n-j] ablegen mspref(b_ptr,0) = (uintD)(b_stern<<1); // niedrige Bits von b* als b[n-j-1] ablegen b_ptr = b_ptr mspop 1; } } // b_MSDptr = Pointer auf b[n], b_ptr = Pointer hinter b[0]. // a_mptr = Pointer auf a[n]. // Schiebe [b[n],...,b[0]] um s+1 Bits nach rechts: if (s == intDsize-1) { lsshrink(b_ptr); } else { shiftright_loop_msp(b_MSDptr,n+1,s+1); msshrink(b_MSDptr); } // b = b_MSDptr/n/b_ptr ist fertig, eine NUDS. b_->MSDptr = b_MSDptr; b_->len = n; b_->LSDptr = b_ptr; // Teste, ob alle a[n],...,a[0]=0 sind: if (test_loop_msp(a_mptr,n+1)) { return cl_false; } else { return cl_true; } // ja -> Wurzel exakt }} } // Bit complexity (N := a_len): O(M(N)). } // namespace cln