bigint.c

00001 /*
00002  * Copyright (c) 2010 Jiri Svoboda
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * - Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  * - Redistributions in binary form must reproduce the above copyright
00012  *   notice, this list of conditions and the following disclaimer in the
00013  *   documentation and/or other materials provided with the distribution.
00014  * - The name of the author may not be used to endorse or promote products
00015  *   derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00019  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00020  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00022  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00023  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00024  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00026  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027  */
00028 
00036 #include <assert.h>
00037 #include <stdio.h>
00038 #include <stdlib.h>
00039 #include "debug.h"
00040 #include "mytypes.h"
00041 
00042 #include "bigint.h"
00043 
00044 static void bigint_sign_comb(bool_t srf_a, bigint_t *a, bool_t srf_b,
00045     bigint_t *b, bigint_t *dest);
00046 static void bigint_add_abs(bigint_t *a, bigint_t *b, bigint_t *dest);
00047 static void bigint_sub_abs(bigint_t *a, bigint_t *b, bigint_t *dest);
00048 static void bigint_shift_mul_dig(bigint_t *a, bigint_word_t b, size_t shift,
00049     bigint_t *dest);
00050 
00051 static void bigint_alloc(bigint_t *bigint, size_t length);
00052 static void bigint_refine_len(bigint_t *bigint);
00053 
00060 void bigint_init(bigint_t *bigint, int value)
00061 {
00062         size_t length;
00063         size_t idx;
00064         int t;
00065 
00066 #ifdef DEBUG_BIGINT_TRACE
00067         printf("Initialize bigint with int value %d.\n", value);
00068 #endif
00069 
00070         if (value < 0) {
00071                 bigint->negative = b_true;
00072                 value = -value;
00073         } else {
00074                 bigint->negative = b_false;
00075         }
00076 
00077         /* Determine length. */
00078         length = 0;
00079         t = value;
00080         while (t > 0) {
00081                 length += 1;
00082                 t = t / BIGINT_BASE;
00083         }
00084 
00085         /* Allocate digit array. */
00086         bigint_alloc(bigint, length);
00087 
00088         /* Compute digits. */
00089         t = value;
00090         for (idx = 0; idx < length; ++idx) {
00091                 bigint->digit[idx] = t % BIGINT_BASE;
00092                 t = t / BIGINT_BASE;
00093         }
00094 }
00095 
00101 void bigint_shallow_copy(bigint_t *src, bigint_t *dest)
00102 {
00103 #ifdef DEBUG_BIGINT_TRACE
00104         printf("Shallow copy of bigint.\n");
00105 #endif
00106         dest->negative = src->negative;
00107         dest->digit = src->digit;
00108         dest->length = src->length;
00109 }
00115 void bigint_clone(bigint_t *src, bigint_t *dest)
00116 {
00117         size_t idx;
00118 
00119 #ifdef DEBUG_BIGINT_TRACE
00120         printf("Clone bigint.\n");
00121 #endif
00122         /* Copy sign. */
00123         dest->negative = src->negative;
00124 
00125         /* Allocate dest digit array. */
00126         bigint_alloc(dest, src->length);
00127 
00128         /* Copy digits. */
00129         for (idx = 0; idx < dest->length; ++idx)
00130                 dest->digit[idx] = src->digit[idx];
00131 }
00132 
00138 void bigint_reverse_sign(bigint_t *src, bigint_t *dest)
00139 {
00140         size_t idx;
00141 
00142 #ifdef DEBUG_BIGINT_TRACE
00143         printf("Reverse-sign copy of bigint.\n");
00144 #endif
00145         /* Copy reversed sign. */
00146         dest->negative = !src->negative;
00147 
00148         /* Allocate dest digit array. */
00149         bigint_alloc(dest, src->length);
00150 
00151         /* Copy digits. */
00152         for (idx = 0; idx < dest->length; ++idx)
00153                 dest->digit[idx] = src->digit[idx];
00154 }
00155 
00165 void bigint_destroy(bigint_t *bigint)
00166 {
00167 #ifdef DEBUG_BIGINT_TRACE
00168         printf("Destroy bigint.\n");
00169 #endif
00170         bigint->negative = b_false;
00171 
00172         bigint->length = 0;
00173 
00174         free(bigint->digit);
00175         bigint->digit = NULL;
00176 }
00177 
00188 int bigint_get_value_int(bigint_t *bigint, int *dval)
00189 {
00190         bigint_t vval, diff;
00191         size_t idx;
00192         int val;
00193         bool_t zf;
00194 
00195 #ifdef DEBUG_BIGINT_TRACE
00196         printf("Get int value of bigint.\n");
00197 #endif
00198         val = 0;
00199         for (idx = 0; idx < bigint->length; ++idx) {
00200                 val = val * BIGINT_BASE + bigint->digit[idx];
00201         }
00202 
00203         if (bigint->negative)
00204                 val = - val;
00205 
00206         /* If the value did not fit @c val now contains garbage. Verify. */
00207         bigint_init(&vval, val);
00208 
00209         bigint_sub(bigint, &vval, &diff);
00210         zf = bigint_is_zero(&diff);
00211 
00212         bigint_destroy(&vval);
00213         bigint_destroy(&diff);
00214 
00215         /* If the difference is not zero, the verification failed. */
00216         if (zf != b_true)
00217                 return EINVAL;
00218 
00219         *dval = val;
00220         return EOK;
00221 }
00222 
00228 bool_t bigint_is_zero(bigint_t *bigint)
00229 {
00230 #ifdef DEBUG_BIGINT_TRACE
00231         printf("Determine if bigint is zero.\n");
00232 #endif
00233         return (bigint->length == 0);
00234 }
00235 
00241 bool_t bigint_is_negative(bigint_t *bigint)
00242 {
00243 #ifdef DEBUG_BIGINT_TRACE
00244         printf("Determine if bigint is negative\n");
00245 #endif
00246         /* Verify that we did not accidentaly introduce a negative zero. */
00247         assert(bigint->negative == b_false || bigint->length > 0);
00248 
00249         return bigint->negative;
00250 }
00251 
00259 void bigint_div_digit(bigint_t *a, bigint_word_t b, bigint_t *quot,
00260     bigint_word_t *rem)
00261 {
00262         size_t lbound;
00263         size_t idx;
00264         bigint_dword_t da, db;
00265         bigint_dword_t q, r;
00266 
00267 #ifdef DEBUG_BIGINT_TRACE
00268         printf("Divide bigint by digit.\n");
00269 #endif
00270         lbound = a->length;
00271         bigint_alloc(quot, lbound);
00272 
00273         quot->negative = a->negative;
00274 
00275         r = 0;
00276         idx = lbound;
00277         while (idx > 0) {
00278                 --idx;
00279 
00280                 da = a->digit[idx] + r * BIGINT_BASE;
00281                 db = b;
00282 
00283                 q = da / db;
00284                 r = da % db;
00285 
00286                 quot->digit[idx] = q;
00287         }
00288 
00289         bigint_refine_len(quot);
00290         *rem = r;
00291 }
00292 
00302 void bigint_add(bigint_t *a, bigint_t *b, bigint_t *dest)
00303 {
00304 #ifdef DEBUG_BIGINT_TRACE
00305         printf("Add bigints.\n");
00306 #endif
00307         bigint_sign_comb(b_false, a, b_false, b, dest);
00308 }
00309 
00319 void bigint_sub(bigint_t *a, bigint_t *b, bigint_t *dest)
00320 {
00321 #ifdef DEBUG_BIGINT_TRACE
00322         printf("Subtract bigints.\n");
00323 #endif
00324         bigint_sign_comb(b_false, a, b_true, b, dest);
00325 }
00326 
00336 void bigint_mul(bigint_t *a, bigint_t *b, bigint_t *dest)
00337 {
00338         size_t idx;
00339         bigint_t dprod;
00340         bigint_t sum;
00341         bigint_t tmp;
00342 
00343 #ifdef DEBUG_BIGINT_TRACE
00344         printf("Multiply bigints.\n");
00345 #endif
00346         bigint_init(&sum, 0);
00347         for (idx = 0; idx < b->length; ++idx) {
00348                 bigint_shift_mul_dig(a, b->digit[idx], idx, &dprod);
00349                 bigint_add(&dprod, &sum, &tmp);
00350                 bigint_destroy(&dprod);
00351 
00352                 bigint_destroy(&sum);
00353                 bigint_shallow_copy(&tmp, &sum);
00354         }
00355 
00356         if (b->negative)
00357                 sum.negative = !sum.negative;
00358 
00359         bigint_shallow_copy(&sum, dest);
00360 }
00361 
00367 void bigint_get_as_string(bigint_t *bigint, char **dptr)
00368 {
00369         static const char digits[] = { '0', '1', '2', '3', '4', '5', '6',
00370             '7', '8', '9' };
00371 
00372         bigint_t val, tmp;
00373         bigint_word_t rem;
00374         size_t nchars;
00375         char *str;
00376         size_t idx;
00377 
00378 #ifdef DEBUG_BIGINT_TRACE
00379         printf("Convert bigint to string.\n");
00380 #endif
00381         assert(BIGINT_BASE >= 10);
00382 
00383         /* Compute number of characters. */
00384         nchars = 0;
00385 
00386         if (bigint_is_zero(bigint) || bigint->negative)
00387                 nchars += 1; /* '0' or '-' */
00388 
00389         bigint_clone(bigint, &val);
00390         while (bigint_is_zero(&val) != b_true) {
00391                 bigint_div_digit(&val, 10, &tmp, &rem);
00392                 bigint_destroy(&val);
00393                 bigint_shallow_copy(&tmp, &val);
00394 
00395                 nchars += 1;
00396         }
00397         bigint_destroy(&val);
00398 
00399         /* Store characters to array. */
00400 
00401         str = malloc(nchars * sizeof(char) + 1);
00402         if (str == NULL) {
00403                 printf("Memory allocation failed.\n");
00404                 exit(1);
00405         }
00406 
00407         if (bigint_is_zero(bigint)) {
00408                 str[0] = '0';
00409         } else if (bigint->negative) {
00410                 str[0] = '-';
00411         }
00412 
00413         idx = 1;
00414         bigint_clone(bigint, &val);
00415         while (bigint_is_zero(&val) != b_true) {
00416                 bigint_div_digit(&val, 10, &tmp, &rem);
00417                 bigint_destroy(&val);
00418                 bigint_shallow_copy(&tmp, &val);
00419 
00420                 str[nchars - idx] = digits[(int) rem];
00421                 ++idx;
00422         }
00423 
00424         bigint_destroy(&val);
00425         str[nchars] = '\0';
00426         *dptr = str;
00427 }
00428 
00433 void bigint_print(bigint_t *bigint)
00434 {
00435         char *str;
00436 
00437 #ifdef DEBUG_BIGINT_TRACE
00438         printf("Print bigint.\n");
00439 #endif
00440         bigint_get_as_string(bigint, &str);
00441         printf("%s", str);
00442         free(str);
00443 }
00444 
00456 static void bigint_sign_comb(bool_t srf_a, bigint_t *a, bool_t srf_b,
00457     bigint_t *b, bigint_t *dest)
00458 {
00459         bool_t neg_a, neg_b;
00460 
00461 #ifdef DEBUG_BIGINT_TRACE
00462         printf("Signed combination of two bigints.\n");
00463 #endif
00464         /* Compute resulting signs of combination elements. */
00465         neg_a = (srf_a != a->negative);
00466         neg_b = (srf_b != b->negative);
00467 
00468         if (neg_a == neg_b) {
00469                 bigint_add_abs(a, b, dest);
00470                 dest->negative = neg_a;
00471         } else {
00472                 bigint_sub_abs(a, b, dest);
00473                 dest->negative = (neg_a != dest->negative);
00474         }
00475 }
00476 
00486 static void bigint_add_abs(bigint_t *a, bigint_t *b, bigint_t *dest)
00487 {
00488         size_t lbound;
00489         size_t idx;
00490         bigint_dword_t da, db;
00491         bigint_dword_t tmp;
00492         bigint_word_t res, carry;
00493 
00494 #ifdef DEBUG_BIGINT_TRACE
00495         printf("Add absolute values of bigints.\n");
00496 #endif
00497         /* max(a->length, b->length) + 1 */
00498         lbound = (a->length > b->length ? a->length : b->length) + 1;
00499         dest->negative = b_false;
00500 
00501         bigint_alloc(dest, lbound);
00502         carry = 0;
00503 
00504         for (idx = 0; idx < lbound; ++idx) {
00505                 da = idx < a->length ? a->digit[idx] : 0;
00506                 db = idx < b->length ? b->digit[idx] : 0;
00507 
00508                 tmp = da + db + (bigint_word_t) carry;
00509 
00510                 carry = (bigint_word_t) (tmp / BIGINT_BASE);
00511                 res = (bigint_word_t) (tmp % BIGINT_BASE);
00512 
00513                 dest->digit[idx] = res;
00514         }
00515 
00516         /* If our lbound is correct, carry must be zero. */
00517         assert(carry == 0);
00518 
00519         bigint_refine_len(dest);
00520 }
00521 
00531 static void bigint_sub_abs(bigint_t *a, bigint_t *b, bigint_t *dest)
00532 {
00533         size_t lbound;
00534         size_t idx;
00535         bigint_dword_t da, db;
00536         bigint_dword_t tmp;
00537         bigint_word_t res, borrow;
00538 
00539 #ifdef DEBUG_BIGINT_TRACE
00540         printf("Subtract absolute values of bigints.\n");
00541 #endif
00542         /* max(a->length, b->length) */
00543         lbound = a->length > b->length ? a->length : b->length;
00544 
00545         bigint_alloc(dest, lbound);
00546         borrow = 0;
00547 
00548         for (idx = 0; idx < lbound; ++idx) {
00549                 da = idx < a->length ? a->digit[idx] : 0;
00550                 db = idx < b->length ? b->digit[idx] : 0;
00551 
00552                 if (da > db + borrow) {
00553                         tmp = da - db - borrow;
00554                         borrow = 0;
00555                 } else {
00556                         tmp = da + BIGINT_BASE - db - borrow;
00557                         borrow = 1;
00558                 }
00559 
00560                 res = (bigint_word_t) tmp;
00561                 dest->digit[idx] = res;
00562         }
00563 
00564         if (borrow != 0) {
00565                 /* We subtracted the greater number from the smaller. */
00566                 dest->negative = b_true;
00567 
00568                 /*
00569                  * Now we must complement the number to get the correct
00570                  * absolute value. We do this by subtracting from 10..0
00571                  * (0 repeated lbound-times).
00572                  */
00573                 borrow = 0;
00574 
00575                 for (idx = 0; idx < lbound; ++idx) {
00576                         da = 0;
00577                         db = dest->digit[idx];
00578 
00579                         if (da > db + borrow) {
00580                                 tmp = da - db - borrow;
00581                                 borrow = 0;
00582                         } else {
00583                                 tmp = da + BIGINT_BASE - db - borrow;
00584                                 borrow = 1;
00585                         }
00586 
00587                         res = (bigint_word_t) tmp;
00588                         dest->digit[idx] = res;
00589                 }
00590 
00591                 /* The last step is the '1'. */
00592                 assert(borrow == 1);
00593         } else {
00594                 dest->negative = b_false;
00595         }
00596 
00597         bigint_refine_len(dest);
00598 }
00599 
00606 static void bigint_shift_mul_dig(bigint_t *a, bigint_word_t b, size_t shift,
00607     bigint_t *dest)
00608 {
00609         size_t lbound;
00610         size_t idx;
00611 
00612         bigint_dword_t da, db;
00613         bigint_dword_t tmp;
00614         bigint_word_t res, carry;
00615 
00616 #ifdef DEBUG_BIGINT_TRACE
00617         printf("Multiply bigint by digit.\n");
00618 #endif
00619         /* Compute length bound and allocate. */
00620         lbound = a->length + shift + 1;
00621         bigint_alloc(dest, lbound);
00622 
00623         /* Copy sign. */
00624         dest->negative = a->negative;
00625 
00626         for (idx = 0; idx < shift; ++idx)
00627                 dest->digit[idx] = 0;
00628 
00629         carry = 0;
00630         for (idx = 0; idx < lbound - shift; ++idx) {
00631                 da = idx < a->length ? a->digit[idx] : 0;
00632                 db = b;
00633 
00634                 tmp = (da * db) + (bigint_word_t) carry;
00635 
00636                 carry = (bigint_word_t) (tmp / BIGINT_BASE);
00637                 res = (bigint_word_t) (tmp % BIGINT_BASE);
00638 
00639                 dest->digit[shift + idx] = res;
00640         }
00641 
00642         /* If our lbound is correct, carry must be zero. */
00643         assert(carry == 0);
00644 
00645         bigint_refine_len(dest);
00646 }
00647 
00648 
00654 static void bigint_alloc(bigint_t *bigint, size_t length)
00655 {
00656         size_t a_length;
00657 
00658 #ifdef DEBUG_BIGINT_TRACE
00659         printf("Allocate bigint digit array.\n");
00660 #endif
00661         /* malloc() sometimes cannot allocate blocks of zero size. */
00662         if (length == 0)
00663                 a_length = 1;
00664         else
00665                 a_length = length;
00666 
00667         bigint->digit = malloc(a_length * sizeof(bigint_word_t));
00668         if (bigint->digit == NULL) {
00669                 printf("Memory allocation failed.\n");
00670                 exit(1);
00671         }
00672 
00673         bigint->length = length;
00674 }
00675 
00682 static void bigint_refine_len(bigint_t *bigint)
00683 {
00684 #ifdef DEBUG_BIGINT_TRACE
00685         printf("Refine bigint length.\n");
00686 #endif
00687         while (bigint->length > 0 && bigint->digit[bigint->length - 1] == 0)
00688                 bigint->length -= 1;
00689 
00690         if (bigint->length == 0)
00691                 bigint->negative = b_false;
00692 }

Generated on Thu Jun 2 07:45:42 2011 for HelenOS/USB by  doxygen 1.4.7