Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

Changeset 3829

Show
Ignore:
Timestamp:
07/31/08 03:43:58 (4 months ago)
Author:
Don Clugston
Message:

Added higher-level sub and integer division. Made asm functions private.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/tango/math/impl/BiguintCore.d

    r3825 r3829  
    1010version(GNU) { 
    1111    // GDC lies about its X86 support 
    12 public import tango.math.impl.BignumNoAsm;     
     12private import tango.math.impl.BignumNoAsm;     
    1313} else version(D_InlineAsm_X86) {  
    14 public import tango.math.impl.BignumX86; 
     14private import tango.math.impl.BignumX86; 
    1515} else { 
    16 public import tango.math.impl.BignumNoAsm; 
    17 
    18  
    19 private: 
    20 void itoaZeroPadded(char[] output, uint value, int radix = 10) { 
    21     int x = output.length - 1; 
    22     for( ; x>=0; --x) { 
    23         output[x]= value % radix + '0'; 
    24         value /= radix; 
    25     } 
    26 
    27  
    28 void toHexZeroPadded(char[] output, uint value) { 
    29     int x = output.length - 1; 
    30     const char [] hexDigits = "0123456789ABCDEF"; 
    31     for( ; x>=0; --x) {         
    32         output[x] = hexDigits[value & 0xF]; 
    33         value >>>= 4;         
    34     } 
     16private import tango.math.impl.BignumNoAsm; 
    3517} 
    3618 
    3719public: 
    38      
    39 // Returns the highest value of i for which left[i]!=right[i], 
    40 // or 0 if left[]==right[] 
    41 int lastDifferentDigit(uint [] left, uint [] right) 
    42 { 
    43     assert(left.length == right.length); 
    44     for (int i=left.length; i>0; --i) { 
    45         if (left[i]!=right[i]) return i; 
    46     } 
    47     return 0; 
    48 } 
    49  
    5020// Converts a big uint to a hexadecimal string. 
    5121// 
     
    185155} 
    186156 
    187 private: 
    188 // classic 'schoolbook' multiplication. 
    189 void mulSimple(uint[] result, uint [] left, uint[] right) 
    190 in {     
    191     assert(result.length == left.length + right.length); 
    192     assert(right.length>1); 
    193 } 
    194 body { 
    195     result[left.length] = multibyteMul(result[0..left.length], left, right[0], 0); 
    196     if (right.length>1) 
    197         multibyteMultiplyAccumulate(result[1..$], left, right[1..$]); 
    198 } 
    199  
    200 // add two uints of possibly different lengths. Result must be as long 
    201 // as the larger length. 
    202 uint addSimple(uint [] result, uint [] left, uint [] right) 
    203 in { 
    204     assert(result.length == left.length); 
    205     assert(left.length >= right.length); 
    206     assert(right.length>0); 
    207 } 
    208 body { 
    209     uint carry = multibyteAddSub!('+')(result[0..right.length], 
    210             left[0..right.length], right, 0); 
    211     if (right.length < left.length) { 
    212         result[right.length..left.length] = left[right.length .. $];             
    213         carry = multibyteIncrementAssign!('+')(result[right.length..$], carry); 
    214     } //else if (result.length==left.length+1) { result[$-1] = carry; carry=0; } 
    215     return carry; 
    216 } 
    217  
    218 /*  result must be larger than right. 
    219 */ 
    220 void simpleSubAssign(uint [] result, uint [] right) 
    221 { 
    222     assert(result.length > right.length); 
    223     uint c = multibyteAddSub!('-')(result[0..right.length], result[0..right.length], right, 0);  
    224     if (c) c = multibyteIncrementAssign!('-')(result[right.length .. $], c); 
    225     assert(c==0); 
    226 } 
    227  
    228 void simpleAddAssign(uint [] result, uint [] right) 
    229 { 
    230    assert(result.length > right.length); 
    231    uint c = multibyteAddSub!('+')(result[0..right.length], result[0..right.length], right, 0); 
    232    if (c) c = multibyteIncrementAssign!('+')(result[right.length .. $], c); 
    233    assert(c==0); 
    234 } 
    235  
    236 // Limits for when to switch between multiplication algorithms. 
    237 const int CACHELIMIT = 8000;   // Half the size of the data cache. 
    238  
    239 /* Determine how much space is required for the temporaries 
    240  * when performing a Karatsuba multiplication.  
    241  */ 
    242 uint karatsubaRequiredBuffSize(uint xlen) 
    243 { 
    244     return xlen <= KARATSUBALIMIT ? 0 : xlen * 2; 
    245 } 
    246  
    247 /* Sets result = x*y, using Karatsuba multiplication. 
    248 * Valid only for balanced multiplies, and x must be longer than y. 
    249 * ie 2*y.length > x.length >= y.length. 
    250 * Params: 
    251 * scratchbuff      An array long enough to store all the temporaries. Will be destroyed. 
    252 */ 
    253 void mulKaratsuba(uint [] result, uint [] x, uint[] y, uint [] scratchbuff) 
    254 { 
    255     assert(result.length == x.length + y.length); 
    256     assert(x.length >= y.length); 
    257     if (x.length <= KARATSUBALIMIT) { 
    258         return mulSimple(result, x, y); 
    259     }     
    260     // Karatsuba multiply uses the following result: 
    261     // (Nx1 + x0)*(Ny1 + y0) = (N*N) x1y1 + x0y0 + N * mid 
    262     // where mid = (x1+x0)*(y1+y0) - x1y1 - x0y0 
    263     // requiring 3 multiplies of length N, instead of 4. 
    264  
    265     // half length, round up. 
    266     uint half = (x.length >> 1) + (x.length & 1); 
    267     assert(y.length>half, "Asymmetric Karatsuba"); 
    268      
    269     uint [] x0 = x[0 .. half]; 
    270     uint [] x1 = x[half .. $];     
    271     uint [] y0 = y[0 .. half]; 
    272     uint [] y1 = y[half .. $]; 
    273     uint [] xsum = result[0 .. half]; // initially use result to store temporaries 
    274     uint [] ysum = result[half .. half*2]; 
    275     uint [] mid = scratchbuff[0 .. half*2+1]; 
    276     uint [] newscratchbuff = scratchbuff[half*2+2 .. $]; 
    277     uint [] resultLow = result[0 .. x0.length + y0.length]; 
    278     uint [] resultHigh = result[x0.length + y0.length .. $]; 
    279          
    280     // Add the high and low parts of x and y. 
    281     // This will generate carries of either 0 or 1. 
    282     uint carry_x = addSimple(xsum, x0, x1); 
    283     uint carry_y = addSimple(ysum, y0, y1); 
    284      
    285     mulKaratsuba(mid[0..half*2], xsum, ysum, newscratchbuff); 
    286     mid[half*2] = carry_x & carry_y; 
    287     if (carry_x)  simpleAddAssign(mid[half..$], ysum); 
    288     if (carry_y)  simpleAddAssign(mid[half..$], xsum); 
    289      
    290     // Low half of result gets x0 * y0. High half gets x1 * y1 
    291     
    292     mulKaratsuba(resultLow, x0, y0, newscratchbuff); 
    293     mid.simpleSubAssign(resultLow); 
    294     mulKaratsuba(resultHigh, x1, y1, newscratchbuff); 
    295     mid.simpleSubAssign(resultHigh); 
    296      
    297     // result += MID 
    298     result[half..$].simpleAddAssign(mid); 
    299 } 
    300  
    301157public: 
     158 
     159/** General unsigned subtraction routine for bigints. 
     160 *  Sets result = x - y. If the result is negative, negative will be true. 
     161 * 
     162 *  The length of y must not be larger than the length of x. 
     163 */ 
     164uint [] biguintSubtract(uint[] x, uint[] y, bool *negative) 
     165{ 
     166    if (x.length == y.length) { 
     167        // There's a possibility of cancellation, if x and y are almost equal. 
     168        int last = lastDifferentDigit(x, y); 
     169        uint [] result = new uint[last+1]; 
     170        if (x[last] < y[last]) { // we know result is negative 
     171            multibyteAddSub!('-')(result[0..last+1], y[0..last+1], x[0..last+1], 0); 
     172            *negative = true; 
     173        } else { // positive or zero result 
     174            multibyteAddSub!('-')(result[0..last+1], x[0..last+1], y[0..last+1], 0); 
     175            *negative = false; 
     176        } 
     177        return result; 
     178    } 
     179    // Lengths are different 
     180    uint [] large, small; 
     181    if (x.length < y.length) { 
     182        *negative = true; 
     183        large = y; small = x; 
     184//        return biguintSubDifferentLength(y, x); 
     185    } else { 
     186        *negative = false; 
     187        large = x; small = y; 
     188  //      return biguintSubDifferentLength(x, y); 
     189    } 
     190    // result.length will be equal to larger length, or could decrease by 1. 
     191     
     192    uint [] result = new uint[large.length]; 
     193    uint carry = multibyteAddSub!('-')(result[0..small.length], large[0..small.length], small, 0); 
     194    result[small.length..$-1] = large[small.length..$]; 
     195    if (carry) { 
     196        multibyteIncrementAssign!('-')(result[small.length..$-1], carry); 
     197        if (result[$-1]==0) return result[0..$-1]; 
     198    } 
     199    return result; 
     200} 
     201 
     202// return a+b 
     203uint [] biguintAdd(uint[] a, uint [] b) { 
     204    uint [] x, y; 
     205    if (a.length<b.length) { x = b; y = a; } else {x = a; y = b; } 
     206    // now we know x.length > y.length 
     207    // create result. add 1 in case it overflows 
     208    uint [] result = new uint[x.length + 1]; 
     209     
     210    uint carry = multibyteAddSub!('+')(result[0..y.length], x[0..y.length], y, 0); 
     211    if (x.length != y.length){ 
     212        result[y.length..$-1]= x[y.length..$]; 
     213        carry  = multibyteIncrementAssign!('+')(result[y.length..$-1], carry); 
     214    } 
     215    if (carry) { 
     216        result[$-1] = carry; 
     217        return result; 
     218    } else return result[0..$-1]; 
     219} 
     220 
     221 
     222/** return x+y 
     223 */ 
     224uint [] biguintAddInt(uint[] x, ulong y) 
     225{ 
     226    uint hi = cast(uint)(y >>> 32); 
     227    uint lo = cast(uint)(y& 0xFFFF_FFFF); 
     228    uint len = x.length; 
     229    if (x.length < 2 && hi!=0) ++len; 
     230    uint [] result = new uint[len+1]; 
     231    result[0..x.length] = x[];  
     232    if (x.length < 2 && hi!=0) { result[1]=hi; hi=0; } 
     233    uint carry = multibyteIncrementAssign!('+')(result[0..$-1], lo); 
     234    if (hi!=0) carry += multibyteIncrementAssign!('+')(result[1..$-1], hi); 
     235    if (carry) { 
     236        result[$-1] = carry; 
     237        return result; 
     238    } else return result[0..$-1]; 
     239} 
    302240     
    303241/** General unsigned multiply routine for bigints. 
     
    373311            done += y.length; 
    374312        } 
    375        //delete scratchbuff; 
     313        delete scratchbuff; 
    376314    } else { 
    377315        // Balanced. Use Karatsuba directly. 
    378316        uint [] scratchbuff = new uint[karatsubaRequiredBuffSize(x.length)]; 
    379317        mulKaratsuba(result, x, y, scratchbuff); 
    380        // delete scratchbuff; 
    381     } 
    382 
     318        delete scratchbuff; 
     319    } 
     320
     321 
     322// return x/y 
     323uint[] biguintDivInt(uint [] x, uint y) { 
     324    uint [] result = new uint[x.length]; 
     325    if ((y&(-y))==y) { 
     326        // perfect power of 2 
     327        uint b = 0; 
     328        for (;y!=0; y>>=1) { 
     329            ++b; 
     330        } 
     331        multibyteShr(result, x, b); 
     332    } else { 
     333        result[] = x[]; 
     334        uint rem = multibyteDivAssign(result, y, 0); 
     335    } 
     336    if (result[$-1]==0 && result.length>1) { 
     337        return result[0..$-1]; 
     338    } else return result; 
     339
     340 
     341 
     342private: 
     343// ------------------------ 
     344// These in-place functions are only for internal use; they are incompatible 
     345// with COW. 
     346 
     347// Classic 'schoolbook' multiplication. 
     348void mulSimple(uint[] result, uint [] left, uint[] right) 
     349in {     
     350    assert(result.length == left.length + right.length); 
     351    assert(right.length>1); 
     352
     353body { 
     354    result[left.length] = multibyteMul(result[0..left.length], left, right[0], 0); 
     355    if (right.length>1) 
     356        multibyteMultiplyAccumulate(result[1..$], left, right[1..$]); 
     357
     358 
     359 
     360// add two uints of possibly different lengths. Result must be as long 
     361// as the larger length. 
     362uint addSimple(uint [] result, uint [] left, uint [] right) 
     363in { 
     364    assert(result.length == left.length); 
     365    assert(left.length >= right.length); 
     366    assert(right.length>0); 
     367
     368body { 
     369    uint carry = multibyteAddSub!('+')(result[0..right.length], 
     370            left[0..right.length], right, 0); 
     371    if (right.length < left.length) { 
     372        result[right.length..left.length] = left[right.length .. $];             
     373        carry = multibyteIncrementAssign!('+')(result[right.length..$], carry); 
     374    } //else if (result.length==left.length+1) { result[$-1] = carry; carry=0; } 
     375    return carry; 
     376
     377 
     378/*  result must be larger than right. 
     379*/ 
     380void simpleSubAssign(uint [] result, uint [] right) 
     381
     382    assert(result.length > right.length); 
     383    uint c = multibyteAddSub!('-')(result[0..right.length], result[0..right.length], right, 0);  
     384    if (c) c = multibyteIncrementAssign!('-')(result[right.length .. $], c); 
     385    assert(c==0); 
     386
     387 
     388 
     389void simpleAddAssign(uint [] result, uint [] right) 
     390
     391   assert(result.length > right.length); 
     392   uint c = multibyteAddSub!('+')(result[0..right.length], result[0..right.length], right, 0); 
     393   if (c) c = multibyteIncrementAssign!('+')(result[right.length .. $], c); 
     394   assert(c==0); 
     395
     396 
     397// Limits for when to switch between multiplication algorithms. 
     398const int CACHELIMIT = 8000;   // Half the size of the data cache. 
     399 
     400/* Determine how much space is required for the temporaries 
     401 * when performing a Karatsuba multiplication.  
     402 */ 
     403uint karatsubaRequiredBuffSize(uint xlen) 
     404
     405    return xlen <= KARATSUBALIMIT ? 0 : xlen * 2; 
     406
     407 
     408/* Sets result = x*y, using Karatsuba multiplication. 
     409* Valid only for balanced multiplies, and x must be longer than y. 
     410* ie 2*y.length > x.length >= y.length. 
     411* Params: 
     412* scratchbuff      An array long enough to store all the temporaries. Will be destroyed. 
     413*/ 
     414void mulKaratsuba(uint [] result, uint [] x, uint[] y, uint [] scratchbuff) 
     415
     416    assert(result.length == x.length + y.length); 
     417    assert(x.length >= y.length); 
     418    if (x.length <= KARATSUBALIMIT) { 
     419        return mulSimple(result, x, y); 
     420    }     
     421    // Karatsuba multiply uses the following result: 
     422    // (Nx1 + x0)*(Ny1 + y0) = (N*N) x1y1 + x0y0 + N * mid 
     423    // where mid = (x1+x0)*(y1+y0) - x1y1 - x0y0 
     424    // requiring 3 multiplies of length N, instead of 4. 
     425 
     426    // half length, round up. 
     427    uint half = (x.length >> 1) + (x.length & 1); 
     428    assert(y.length>half, "Asymmetric Karatsuba"); 
     429     
     430    uint [] x0 = x[0 .. half]; 
     431    uint [] x1 = x[half .. $];     
     432    uint [] y0 = y[0 .. half]; 
     433    uint [] y1 = y[half .. $]; 
     434    uint [] xsum = result[0 .. half]; // initially use result to store temporaries 
     435    uint [] ysum = result[half .. half*2]; 
     436    uint [] mid = scratchbuff[0 .. half*2+1]; 
     437    uint [] newscratchbuff = scratchbuff[half*2+2 .. $]; 
     438    uint [] resultLow = result[0 .. x0.length + y0.length]; 
     439    uint [] resultHigh = result[x0.length + y0.length .. $]; 
     440         
     441    // Add the high and low parts of x and y. 
     442    // This will generate carries of either 0 or 1. 
     443    uint carry_x = addSimple(xsum, x0, x1); 
     444    uint carry_y = addSimple(ysum, y0, y1); 
     445     
     446    mulKaratsuba(mid[0..half*2], xsum, ysum, newscratchbuff); 
     447    mid[half*2] = carry_x & carry_y; 
     448    if (carry_x)  simpleAddAssign(mid[half..$], ysum); 
     449    if (carry_y)  simpleAddAssign(mid[half..$], xsum); 
     450     
     451    // Low half of result gets x0 * y0. High half gets x1 * y1 
     452    
     453    mulKaratsuba(resultLow, x0, y0, newscratchbuff); 
     454    mid.simpleSubAssign(resultLow); 
     455    mulKaratsuba(resultHigh, x1, y1, newscratchbuff); 
     456    mid.simpleSubAssign(resultHigh); 
     457     
     458    // result += MID 
     459    result[half..$].simpleAddAssign(mid); 
     460
     461 
     462private: 
     463void itoaZeroPadded(char[] output, uint value, int radix = 10) { 
     464    int x = output.length - 1; 
     465    for( ; x>=0; --x) { 
     466        output[x]= value % radix + '0'; 
     467        value /= radix; 
     468    } 
     469
     470 
     471void toHexZeroPadded(char[] output, uint value) { 
     472    int x = output.length - 1; 
     473    const char [] hexDigits = "0123456789ABCDEF"; 
     474    for( ; x>=0; --x) {         
     475        output[x] = hexDigits[value & 0xF]; 
     476        value >>>= 4;         
     477    } 
     478
     479 
     480private: 
     481     
     482// Returns the highest value of i for which left[i]!=right[i], 
     483// or 0 if left[]==right[] 
     484int lastDifferentDigit(uint [] left, uint [] right) 
     485
     486    assert(left.length == right.length); 
     487    for (int i=left.length-1; i>0; --i) { 
     488        if (left[i]!=right[i]) return i; 
     489    } 
     490    return 0; 
     491
     492