bigar.cpp

Go to the documentation of this file.
00001 /*****************************************************************************
00002     
00003     bigar.cpp -- see the bigar.hpp file for description.
00004 
00005     This file is a part of the Arageli library.
00006     
00007     Copyright (C) Nikolai Yu. Zolotykh, 1999 -- 2005
00008     University of Nizhni Novgorod, Russia
00009 
00010     Updated by Agafonov E.A. 2000
00011 
00012 *****************************************************************************/
00013 
00014 #include "config.hpp"
00015 
00016 #include <cstddef>
00017 
00018 #include "exception.hpp"
00019 #include "bigar.hpp"
00020 
00021 
00022 namespace Arageli { namespace _Internal
00023 {
00024 
00025 
00026 // divides two integers and returns both the quotient and the remainder
00027 // NOTE. Inline specificator makes this function is internal liked.
00028 inline void divide
00029 (
00030     digit& quotient, digit& remainder,
00031     doubledigit numerator, extendeddigit denominator
00032 )
00033 {
00034     quotient = digit(numerator / (doubledigit)denominator);
00035     remainder = digit(numerator % (doubledigit)denominator);
00036 }
00037 
00038 
00039 std::size_t do_big_int_to_bdn (digit* a, digit* b, std::size_t n, digit bdn_radix)
00040 {
00041     // BASE to bdn_radix convertion
00042     // BASE > bdn_radix
00043 
00044     // input:  a[n-1],...,a[0], n
00045     // output: b[m-1],...,b[0], m
00046     // returns m
00047     // a[n-1],...,a[0] are not const
00048 
00049     ARAGELI_ASSERT_1(BASE > bdn_radix);
00050     
00051     std::size_t m = 0;
00052 
00053     while (n > 0)
00054     {
00055         // division
00056         digit carry = 0;
00057         for(std::size_t j = n; j > 0; j--)
00058         {
00059             doubledigit tmp = doubledigit(carry) * BASE + a[j - 1];
00060             carry = digit(tmp % bdn_radix);
00061             a[j - 1] = digit(tmp / bdn_radix);
00062         }
00063 
00064         b[m] = carry;
00065         m++;
00066 
00067         if(!a[n - 1])
00068         n--;
00069     }
00070 
00071     return m;
00072 }
00073 
00074 
00075 std::size_t do_bdn_to_big_int (digit* a, digit* b, std::size_t n, digit bdn_radix)
00076 {
00077     // bdn_radix to BASE convertion
00078     // BASE > bdn_radix
00079 
00080     // input:  a[n-1],...,a[0], n
00081     // output: b[m-1],...,b[0], m
00082     // returns m
00083     // a[n-1],...,a[0] are not const
00084 
00085     ARAGELI_ASSERT_1(BASE > bdn_radix);
00086 
00087     std::size_t m = 0;
00088     for(std::size_t i = n; i > 0; i--)
00089     {
00090         // (b[m-1],...,b[0]) = (b[m-1],...,b[0]) * bdn_radix + a[i - 1]
00091         digit carry = a[i - 1];
00092 
00093         for(std::size_t j = 0; j < m; j++)
00094         {
00095             doubledigit tmp = doubledigit(b[j]) * bdn_radix + carry;
00096             carry = digit(tmp / BASE);
00097             b[j] = digit(tmp % BASE);
00098         }
00099 
00100         if(carry)
00101         {
00102             b[m] = carry; // new digit
00103             m++;
00104         }
00105     }
00106 
00107     return m;
00108 }
00109 
00110 
00111 #ifdef ARAGELI_DISABLE_PARTICULAR_COMPILER_WARNINGS
00112     #pragma warning (push)
00113     #pragma warning (disable : 4244)
00114 #endif
00115 
00116 
00117 std::size_t do_add (digit* p1, const digit* p2, std::size_t m, std::size_t n)
00118 {
00119     // m - length of p1
00120     // n - length of p2
00121     // m must be >= n
00122 
00123     // actually, length of p1 must be m+1 - reserved for carry
00124     // do_add returns amount of main digits (m without carry or m+1 with carry) 
00125 
00126     ARAGELI_ASSERT_1(m >= n);
00127 
00128     bit carry = 0;
00129 
00130     // 1. loop for 0 to n-1 - add digits of p2 to p1
00131 
00132     for(std::size_t i = 0; i < n; i++)
00133     {
00134         doubledigit tmp = doubledigit(p1[i]) + p2[i] + carry;
00135         p1[i] = digit(tmp % BASE);
00136         carry = digit(tmp / BASE);
00137     }
00138 
00139     // 2. loop for add carry
00140 
00141     for(std::size_t i = n; i < m; i++)
00142     {
00143         doubledigit tmp = doubledigit(p1[i]) + carry;
00144         p1[i] = digit(tmp % BASE);
00145         carry = digit(tmp / BASE);
00146         if(carry == 0) break;
00147     }
00148 
00149     if(carry)
00150     {
00151         p1[m] = 1;
00152         return m + 1;
00153     }
00154     else return m;
00155 }
00156 
00157 
00158 #ifdef ARAGELI_DISABLE_PARTICULAR_COMPILER_WARNINGS
00159     #pragma warning (pop)
00160 #endif
00161 
00162 
00163 int do_sub (digit* p1, const digit* p2, std::size_t m, std::size_t n)
00164 {
00165     // m - length of p1
00166     // n - length of p2
00167     // m must be >= n
00168     //
00169     // do_sub returns borrow (0 or 1)
00170 
00171     ARAGELI_ASSERT_1(m >= n);
00172 
00173     bit borrow = 0;
00174 
00175     // 1. loop for 0 to n-1 - digits of p2 sub from digits of p1
00176 
00177     for(std::size_t i = 0; i < n; i++)
00178     {
00179         if(extendeddigit(p1[i]) >= extendeddigit(p2[i]) + borrow)
00180         {
00181             p1[i] = p1[i] - p2[i] - borrow;
00182             borrow = 0;
00183         }
00184         else
00185         {
00186             p1[i] = digit(BASE + p1[i] - p2[i] - borrow);
00187             borrow = 1;
00188         }
00189     }
00190 
00191     if(borrow == 0)return 0; // no borrow
00192 
00193     // 2. loop for borrow
00194 
00195     for(std::size_t i = n; i < m; i++)
00196     {
00197         if(p1[i] >= borrow)
00198         {
00199             p1[i] = p1[i] - borrow;
00200             return 0; // borrow = 0;
00201         }
00202         else
00203         {
00204             p1[i] = digit(BASE + p1[i] - borrow);
00205             borrow = 1;
00206         }
00207     }
00208     
00209     return 1; // borrow was accuped -> p2 > p1
00210 }
00211 
00212 
00213 std::size_t do_optimize (const digit* a, std::size_t n)
00214 {
00215     // returns real length of a,
00216     // i.e. the number of digits except leading zeros.
00217 
00218     for(std::size_t i = n; i > 0; i--)
00219         if(a[i - 1])return i;
00220 
00221     return 0;
00222 }
00223 
00224 
00225 std::size_t do_mult (const digit* u, const digit* v, digit* w, std::size_t m, std::size_t n)
00226 {
00227     //  multiplicate u to v
00228     //  m - length of u
00229     //  n - length of v
00230 
00231     //  result will be stored in w
00232     //  the length of w = m + n  or  m + n - 1.
00233     //  returns the major digit w[m + n - 1]
00234 
00235     for(std::size_t i = 0; i < m; i++)
00236         w[i] = 0;
00237 
00238     for(std::size_t j = 0; j < n; j++) // v[j]
00239     {
00240         digit carry = 0;
00241         
00242         for(std::size_t i = 0; i < m; i++)  // u[i]
00243         {
00244             doubledigit tmp = doubledigit(u[i]) * v[j] + w[i + j] + carry;
00245             w[i + j] = digit(tmp % BASE);
00246             carry = digit(tmp / BASE);
00247         }
00248 
00249         w[j + m] = carry;
00250     }
00251 
00252     return w[m + n - 1];
00253 }
00254 
00255 
00256 digit do_divide_by_digit (const digit* a, digit* p, std::size_t n, digit d)
00257 {
00258     // p = a / d;
00259     // returns a % d
00260     // the real length of p = m, if p[m - 1] != 0
00261     //                      = m - 1, if p[m - 1] = 0
00262 
00263     // if d == 1 ...
00264 
00265     digit carry = 0;
00266 
00267     for(std::size_t i = n; i > 0; i--)
00268     {
00269         doubledigit tmp = doubledigit(carry) * BASE + a[i - 1];
00270         p[i - 1] = digit(tmp / d);
00271         carry = digit(tmp % d);
00272     }
00273 
00274     return carry;
00275 }
00276 
00277 
00278 std::size_t do_divide (digit* u, digit* v, digit* q, std::size_t m, std::size_t n)
00279 {
00280     // D1 Normalization
00281 
00282     digit d = digit(BASE / (extendeddigit(v[n-1]) + 1));
00283 
00284     // (v[n-1], v[n-2],..., v[0]) = d * (v[n-1], v[n-2],..., v[0])
00285     
00286     digit carry = 0;
00287     for(std::size_t j = 0; j < n; j++)
00288     {
00289         doubledigit tmp = doubledigit(v[j]) * d + carry;
00290         carry = digit(tmp/BASE);
00291         v[j] = digit(tmp%BASE);
00292     }
00293 
00294     // (u[m], u[m-1],..., u[0]) = d * (u[m-1], u[m-2],..., u[0])
00295     
00296     carry = 0;
00297     for(std::size_t j = 0; j < m; j++)
00298     {
00299         doubledigit tmp = doubledigit(u[j]) * d + carry;
00300         carry = digit(tmp / BASE);
00301         u[j] = digit(tmp % BASE);
00302     }
00303     u[m] = carry;
00304 
00305     // D2
00306     // D3
00307     
00308     for(std::size_t j = m; j >= n; j--)
00309     {
00310         // main loop: one iteration in the loop
00311         // makes one digit in q
00312 
00313         digit q_hat, r_hat; // approximation for current digit
00314         // in the quotient and residue
00315         int overflow = 0;
00316 
00317         if(u[j] >= v[n-1])
00318         {
00319             q_hat = digit(BASE - 1);
00320             if(doubledigit(v[n-1]) + u[j-1] >= BASE)
00321                 overflow = 1;
00322             else
00323                 r_hat = v[n-1] + u[j-1];
00324         }
00325         else
00326             divide(q_hat, r_hat, doubledigit(u[j]) * BASE + u[j - 1], v[n - 1]);
00327 
00328         if(!overflow)
00329             while
00330             (
00331                 doubledigit(v[n - 2]) * q_hat >
00332                 doubledigit(r_hat) * BASE + u[j - 2]
00333             )
00334             { // loop at most two times
00335                 q_hat--;
00336                 if(doubledigit(r_hat) + v[n - 1] >= BASE)
00337                     break; // if overflow, break
00338                 r_hat += v[n-1];
00339             }
00340 
00341         // D4 Multiply and subtract
00342         // (u[j],...,u[j-n]) = (u[j],...,u[j-n]) - (v[n-1],...,v[0]) * q_hat
00343         // with borrow if it is required
00344 
00345         digit carry = 0;
00346         for(std::size_t i = 0; i < n; i++)
00347         {
00348             digit higher, lower;
00349 
00350             //#ifdef __WATCOMC__
00352             //higher = ((doubledigit)v[i]*q_hat + carry)/BASE;
00353             //lower = ((doubledigit)v[i]*q_hat + carry)%BASE;
00354             //#else
00355 
00356             divide(higher, lower, doubledigit(v[i]) * q_hat + carry, BASE);
00357 
00358             //#endif
00359 
00360             if(u[j + i - n] >= lower)
00361                 u[j + i - n] -= lower;
00362             else
00363             {
00364                 higher++;
00365                 u[j + i - n] = digit(BASE - lower + u[j + i - n]);
00366             }
00367 
00368             carry = higher;
00369         }
00370 
00371         int borrow;
00372         if(u[j] >= carry)
00373         {
00374             borrow = 0;
00375             u[j] -= carry;
00376         }
00377         else
00378         {
00379             borrow = 1;
00380             u[j] = digit(BASE - carry + u[j]);
00381         }
00382 
00383         // D5
00384 
00385         if(borrow)
00386         {
00387             // D6 addition
00388             // It is very rare event
00389             // (u[j],...,u[j-n]) = (u[j],...,u[j-n]) + (v[n-1],...,v[0])
00390             // we don't consider the highest borrow
00391             
00392             q_hat--;
00393             digit carry = 0;
00394             for(std::size_t i = 0; i < n; i++)
00395             {
00396                 divide
00397                 (
00398                     carry, u[j + i - n],
00399                     doubledigit(u[j + i - n]) + v[i] + carry,
00400                     extendeddigit(BASE)
00401                 );
00402             }
00403         }
00404 
00405         // The quotient digit:
00406         q[j - n] = q_hat;
00407     }
00408 
00409     // D8 denormalization
00410     // (u[n-1],...,u[0]) = (u[n-1],...,u[0]) / d
00411 
00412     carry = 0;
00413     for(std::size_t j = n; j > 0; j--)
00414         divide(u[j - 1], carry, doubledigit(carry) * BASE + u[j - 1], d);
00415 
00416     // optimize u --- residue
00417     std::size_t u_len = n;
00418     
00419     for(std::size_t j = n; j > 0; j--)
00420         if(u[j - 1])break;
00421         else u_len--;
00422 
00423     return u_len;
00424 }
00425 
00426 
00427 }}

Generated on Thu Aug 31 17:38:04 2006 for Arageli by  doxygen 1.4.7