source: mainline/uspace/app/sbi/src/bigint.c@ 38aaacc2

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 38aaacc2 was 23de644, checked in by Jiri Svoboda <jiri@…>, 15 years ago

Update SBI to rev. 174.

  • Property mode set to 100644
File size: 15.2 KB
Line 
1/*
2 * Copyright (c) 2010 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @file Big integers.
30 *
31 * Sysel type @c int should accomodate large numbers. This implementation
32 * is limited by the available memory and range of the @c size_t type used
33 * to index digits.
34 */
35
36#include <assert.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include "debug.h"
40#include "mytypes.h"
41
42#include "bigint.h"
43
44static void bigint_sign_comb(bool_t srf_a, bigint_t *a, bool_t srf_b,
45 bigint_t *b, bigint_t *dest);
46static void bigint_add_abs(bigint_t *a, bigint_t *b, bigint_t *dest);
47static void bigint_sub_abs(bigint_t *a, bigint_t *b, bigint_t *dest);
48static void bigint_shift_mul_dig(bigint_t *a, bigint_word_t b, size_t shift,
49 bigint_t *dest);
50
51static void bigint_alloc(bigint_t *bigint, size_t length);
52static void bigint_refine_len(bigint_t *bigint);
53
54/** Initialize bigint with value from small integer.
55 *
56 * Initializes a bigint structure with the provided small value.
57 *
58 * @param value Initial value (small int).
59 */
60void bigint_init(bigint_t *bigint, int value)
61{
62 size_t length;
63 size_t idx;
64 int t;
65
66#ifdef DEBUG_BIGINT_TRACE
67 printf("Initialize bigint with int value %d.\n", value);
68#endif
69
70 if (value < 0) {
71 bigint->negative = b_true;
72 value = -value;
73 } else {
74 bigint->negative = b_false;
75 }
76
77 /* Determine length. */
78 length = 0;
79 t = value;
80 while (t > 0) {
81 length += 1;
82 t = t / BIGINT_BASE;
83 }
84
85 /* Allocate digit array. */
86 bigint_alloc(bigint, length);
87
88 /* Compute digits. */
89 t = value;
90 for (idx = 0; idx < length; ++idx) {
91 bigint->digit[idx] = t % BIGINT_BASE;
92 t = t / BIGINT_BASE;
93 }
94}
95
96/** Shallow copy of integer.
97 *
98 * @param src Source.
99 * @param dest Destination.
100 */
101void bigint_shallow_copy(bigint_t *src, bigint_t *dest)
102{
103#ifdef DEBUG_BIGINT_TRACE
104 printf("Shallow copy of bigint.\n");
105#endif
106 dest->negative = src->negative;
107 dest->digit = src->digit;
108 dest->length = src->length;
109}
110/** Clone big integer.
111 *
112 * @param src Source.
113 * @param dest Destination.
114 */
115void bigint_clone(bigint_t *src, bigint_t *dest)
116{
117 size_t idx;
118
119#ifdef DEBUG_BIGINT_TRACE
120 printf("Clone bigint.\n");
121#endif
122 /* Copy sign. */
123 dest->negative = src->negative;
124
125 /* Allocate dest digit array. */
126 bigint_alloc(dest, src->length);
127
128 /* Copy digits. */
129 for (idx = 0; idx < dest->length; ++idx)
130 dest->digit[idx] = src->digit[idx];
131}
132
133/** Compute big integer with reversed sign.
134 *
135 * @param src Source.
136 * @param dest Destination.
137 */
138void bigint_reverse_sign(bigint_t *src, bigint_t *dest)
139{
140 size_t idx;
141
142#ifdef DEBUG_BIGINT_TRACE
143 printf("Reverse-sign copy of bigint.\n");
144#endif
145 /* Copy reversed sign. */
146 dest->negative = !src->negative;
147
148 /* Allocate dest digit array. */
149 bigint_alloc(dest, src->length);
150
151 /* Copy digits. */
152 for (idx = 0; idx < dest->length; ++idx)
153 dest->digit[idx] = src->digit[idx];
154}
155
156/** Destroy big integer.
157 *
158 * Any bigint that is initialized via bigint_init() or any other bigint
159 * function that constructs a new bigint value should be destroyed with
160 * this function to free memory associated with the bigint. It should
161 * also be used to destroy a bigint before it is reused.
162 *
163 * @param bigint The bigint to destroy.
164 */
165void bigint_destroy(bigint_t *bigint)
166{
167#ifdef DEBUG_BIGINT_TRACE
168 printf("Destroy bigint.\n");
169#endif
170 bigint->negative = b_false;
171
172 bigint->length = 0;
173
174 free(bigint->digit);
175 bigint->digit = NULL;
176}
177
178/** Get value of big integer.
179 *
180 * Allows to obtain the value of big integer, provided that it fits
181 * into a small integer.
182 *
183 * @param bigint Bigint to obtain value from.
184 * @param dval Place to store value.
185 * @return EOK on success, ELIMIT if bigint is too big to fit
186 * to @a dval.
187 */
188int bigint_get_value_int(bigint_t *bigint, int *dval)
189{
190 bigint_t vval, diff;
191 size_t idx;
192 int val;
193 bool_t zf;
194
195#ifdef DEBUG_BIGINT_TRACE
196 printf("Get int value of bigint.\n");
197#endif
198 val = 0;
199 for (idx = 0; idx < bigint->length; ++idx) {
200 val = val * BIGINT_BASE + bigint->digit[idx];
201 }
202
203 if (bigint->negative)
204 val = - val;
205
206 /* If the value did not fit @c val now contains garbage. Verify. */
207 bigint_init(&vval, val);
208
209 bigint_sub(bigint, &vval, &diff);
210 zf = bigint_is_zero(&diff);
211
212 bigint_destroy(&vval);
213 bigint_destroy(&diff);
214
215 /* If the difference is not zero, the verification failed. */
216 if (zf != b_true)
217 return EINVAL;
218
219 *dval = val;
220 return EOK;
221}
222
223/** Determine if bigint is zero.
224 *
225 * @param bigint Big integer.
226 * @return b_true if @a bigint is zero, b_false otherwise.
227 */
228bool_t bigint_is_zero(bigint_t *bigint)
229{
230#ifdef DEBUG_BIGINT_TRACE
231 printf("Determine if bigint is zero.\n");
232#endif
233 return (bigint->length == 0);
234}
235
236/** Determine if bigint is negative.
237 *
238 * @param bigint Big integer.
239 * @return b_true if @a bigint is negative, b_false otherwise.
240 */
241bool_t bigint_is_negative(bigint_t *bigint)
242{
243#ifdef DEBUG_BIGINT_TRACE
244 printf("Determine if bigint is negative\n");
245#endif
246 /* Verify that we did not accidentaly introduce a negative zero. */
247 assert(bigint->negative == b_false || bigint->length > 0);
248
249 return bigint->negative;
250}
251
252/** Divide bigint by (unsigned) digit.
253 *
254 * @param a Bigint dividend.
255 * @param b Divisor digit.
256 * @param quot Output bigint quotient.
257 * @param rem Output remainder digit.
258 */
259void bigint_div_digit(bigint_t *a, bigint_word_t b, bigint_t *quot,
260 bigint_word_t *rem)
261{
262 size_t lbound;
263 size_t idx;
264 bigint_dword_t da, db;
265 bigint_dword_t q, r;
266
267#ifdef DEBUG_BIGINT_TRACE
268 printf("Divide bigint by digit.\n");
269#endif
270 lbound = a->length;
271 bigint_alloc(quot, lbound);
272
273 quot->negative = a->negative;
274
275 r = 0;
276 idx = lbound;
277 while (idx > 0) {
278 --idx;
279
280 da = a->digit[idx] + r * BIGINT_BASE;
281 db = b;
282
283 q = da / db;
284 r = da % db;
285
286 quot->digit[idx] = q;
287 }
288
289 bigint_refine_len(quot);
290 *rem = r;
291}
292
293/** Add two big integers.
294 *
295 * The big integers @a a and @a b are added and the result is stored in
296 * @a dest.
297 *
298 * @param a First addend.
299 * @param b Second addend.
300 * @param dest Destination bigint.
301 */
302void bigint_add(bigint_t *a, bigint_t *b, bigint_t *dest)
303{
304#ifdef DEBUG_BIGINT_TRACE
305 printf("Add bigints.\n");
306#endif
307 bigint_sign_comb(b_false, a, b_false, b, dest);
308}
309
310/** Subtract two big integers.
311 *
312 * The big integers @a a and @a b are subtracted and the result is stored in
313 * @a dest.
314 *
315 * @param a Minuend.
316 * @param b Subtrahend.
317 * @param dest Destination bigint.
318 */
319void bigint_sub(bigint_t *a, bigint_t *b, bigint_t *dest)
320{
321#ifdef DEBUG_BIGINT_TRACE
322 printf("Subtract bigints.\n");
323#endif
324 bigint_sign_comb(b_false, a, b_true, b, dest);
325}
326
327/** Multiply two big integers.
328 *
329 * The big integers @a a and @a b are multiplied and the result is stored in
330 * @a dest.
331 *
332 * @param a First factor.
333 * @param b Second factor.
334 * @param dest Destination bigint.
335 */
336void bigint_mul(bigint_t *a, bigint_t *b, bigint_t *dest)
337{
338 size_t idx;
339 bigint_t dprod;
340 bigint_t sum;
341 bigint_t tmp;
342
343#ifdef DEBUG_BIGINT_TRACE
344 printf("Multiply bigints.\n");
345#endif
346 bigint_init(&sum, 0);
347 for (idx = 0; idx < b->length; ++idx) {
348 bigint_shift_mul_dig(a, b->digit[idx], idx, &dprod);
349 bigint_add(&dprod, &sum, &tmp);
350 bigint_destroy(&dprod);
351
352 bigint_destroy(&sum);
353 bigint_shallow_copy(&tmp, &sum);
354 }
355
356 if (b->negative)
357 sum.negative = !sum.negative;
358
359 bigint_shallow_copy(&sum, dest);
360}
361
362/** Print bigint to standard output. */
363void bigint_print(bigint_t *bigint)
364{
365 bigint_t val, tmp;
366 bigint_word_t rem;
367 size_t ndigits;
368 int *digits;
369 size_t idx;
370
371#ifdef DEBUG_BIGINT_TRACE
372 printf("Print bigint.\n");
373#endif
374 assert(BIGINT_BASE >= 10);
375
376 if (bigint_is_zero(bigint)) {
377 putchar('0');
378 return;
379 }
380
381 if (bigint->negative)
382 putchar('-');
383
384 /* Compute number of digits. */
385 ndigits = 0;
386 bigint_clone(bigint, &val);
387 while (bigint_is_zero(&val) != b_true) {
388 bigint_div_digit(&val, 10, &tmp, &rem);
389 bigint_destroy(&val);
390 bigint_shallow_copy(&tmp, &val);
391
392 ndigits += 1;
393 }
394 bigint_destroy(&val);
395
396 /* Store digits to array. */
397
398 digits = malloc(ndigits * sizeof(int));
399 if (digits == NULL) {
400 printf("Memory allocation failed.\n");
401 exit(1);
402 }
403
404 idx = 0;
405 bigint_clone(bigint, &val);
406 while (bigint_is_zero(&val) != b_true) {
407 bigint_div_digit(&val, 10, &tmp, &rem);
408 bigint_destroy(&val);
409 bigint_shallow_copy(&tmp, &val);
410
411 digits[idx++] = (int) rem;
412 }
413 bigint_destroy(&val);
414
415 for (idx = 0; idx < ndigits; ++idx)
416 printf("%u", digits[ndigits - 1 - idx]);
417
418 free(digits);
419}
420
421/** Compute sign combination of two big integers.
422 *
423 * Of the big integers @a a and @a b each is optionally sign-reversed and then
424 * they are added and the result is stored in @a dest.
425 *
426 * @param srf_a First sign reversal flag.
427 * @param a First bigint.
428 * @param srf_b Second sign reversal flag.
429 * @param b Second bigint.
430 * @param dest Destination bigint.
431 */
432static void bigint_sign_comb(bool_t srf_a, bigint_t *a, bool_t srf_b,
433 bigint_t *b, bigint_t *dest)
434{
435 bool_t neg_a, neg_b;
436
437#ifdef DEBUG_BIGINT_TRACE
438 printf("Signed combination of two bigints.\n");
439#endif
440 /* Compute resulting signs of combination elements. */
441 neg_a = (srf_a != a->negative);
442 neg_b = (srf_b != b->negative);
443
444 if (neg_a == neg_b) {
445 bigint_add_abs(a, b, dest);
446 dest->negative = neg_a;
447 } else {
448 bigint_sub_abs(a, b, dest);
449 dest->negative = (neg_a != dest->negative);
450 }
451}
452
453/** Add absolute values of two big integers.
454 *
455 * The absolute values of big integers @a a and @a b are added and the result
456 * is stored in @a dest.
457 *
458 * @param a First addend.
459 * @param b Second addend.
460 * @param dest Destination bigint.
461 */
462static void bigint_add_abs(bigint_t *a, bigint_t *b, bigint_t *dest)
463{
464 size_t lbound;
465 size_t idx;
466 bigint_dword_t da, db;
467 bigint_dword_t tmp;
468 bigint_word_t res, carry;
469
470#ifdef DEBUG_BIGINT_TRACE
471 printf("Add absolute values of bigints.\n");
472#endif
473 /* max(a->length, b->length) + 1 */
474 lbound = (a->length > b->length ? a->length : b->length) + 1;
475 dest->negative = b_false;
476
477 bigint_alloc(dest, lbound);
478 carry = 0;
479
480 for (idx = 0; idx < lbound; ++idx) {
481 da = idx < a->length ? a->digit[idx] : 0;
482 db = idx < b->length ? b->digit[idx] : 0;
483
484 tmp = da + db + (bigint_word_t) carry;
485
486 carry = (bigint_word_t) (tmp / BIGINT_BASE);
487 res = (bigint_word_t) (tmp % BIGINT_BASE);
488
489 dest->digit[idx] = res;
490 }
491
492 /* If our lbound is correct, carry must be zero. */
493 assert(carry == 0);
494
495 bigint_refine_len(dest);
496}
497
498/** Subtract absolute values of two big integers.
499 *
500 * The absolute values of big integers @a a and @a b are subtracted and the
501 * result is stored in @a dest.
502 *
503 * @param a Minuend.
504 * @param b Subtrahend.
505 * @param dest Destination bigint.
506 */
507static void bigint_sub_abs(bigint_t *a, bigint_t *b, bigint_t *dest)
508{
509 size_t lbound;
510 size_t idx;
511 bigint_dword_t da, db;
512 bigint_dword_t tmp;
513 bigint_word_t res, borrow;
514
515#ifdef DEBUG_BIGINT_TRACE
516 printf("Subtract absolute values of bigints.\n");
517#endif
518 /* max(a->length, b->length) */
519 lbound = a->length > b->length ? a->length : b->length;
520
521 bigint_alloc(dest, lbound);
522 borrow = 0;
523
524 for (idx = 0; idx < lbound; ++idx) {
525 da = idx < a->length ? a->digit[idx] : 0;
526 db = idx < b->length ? b->digit[idx] : 0;
527
528 if (da > db + borrow) {
529 tmp = da - db - borrow;
530 borrow = 0;
531 } else {
532 tmp = da + BIGINT_BASE - db - borrow;
533 borrow = 1;
534 }
535
536 res = (bigint_word_t) tmp;
537 dest->digit[idx] = res;
538 }
539
540 if (borrow != 0) {
541 /* We subtracted the greater number from the smaller. */
542 dest->negative = b_true;
543
544 /*
545 * Now we must complement the number to get the correct
546 * absolute value. We do this by subtracting from 10..0
547 * (0 repeated lbound-times).
548 */
549 borrow = 0;
550
551 for (idx = 0; idx < lbound; ++idx) {
552 da = 0;
553 db = dest->digit[idx];
554
555 if (da > db + borrow) {
556 tmp = da - db - borrow;
557 borrow = 0;
558 } else {
559 tmp = da + BIGINT_BASE - db - borrow;
560 borrow = 1;
561 }
562
563 res = (bigint_word_t) tmp;
564 dest->digit[idx] = res;
565 }
566
567 /* The last step is the '1'. */
568 assert(borrow == 1);
569 } else {
570 dest->negative = b_false;
571 }
572
573 bigint_refine_len(dest);
574}
575
576/** Multiply big integer by digit.
577 *
578 * @param a Bigint factor.
579 * @param b Digit factor.
580 * @param dest Destination bigint.
581 */
582static void bigint_shift_mul_dig(bigint_t *a, bigint_word_t b, size_t shift,
583 bigint_t *dest)
584{
585 size_t lbound;
586 size_t idx;
587
588 bigint_dword_t da, db;
589 bigint_dword_t tmp;
590 bigint_word_t res, carry;
591
592#ifdef DEBUG_BIGINT_TRACE
593 printf("Multiply bigint by digit.\n");
594#endif
595 /* Compute length bound and allocate. */
596 lbound = a->length + shift + 1;
597 bigint_alloc(dest, lbound);
598
599 /* Copy sign. */
600 dest->negative = a->negative;
601
602 for (idx = 0; idx < shift; ++idx)
603 dest->digit[idx] = 0;
604
605 carry = 0;
606 for (idx = 0; idx < lbound - shift; ++idx) {
607 da = idx < a->length ? a->digit[idx] : 0;
608 db = b;
609
610 tmp = (da * db) + (bigint_word_t) carry;
611
612 carry = (bigint_word_t) (tmp / BIGINT_BASE);
613 res = (bigint_word_t) (tmp % BIGINT_BASE);
614
615 dest->digit[shift + idx] = res;
616 }
617
618 /* If our lbound is correct, carry must be zero. */
619 assert(carry == 0);
620
621 bigint_refine_len(dest);
622}
623
624
625/** Allocate bigint of the given length.
626 *
627 * @param bigint Bigint whose digit array should be allocated.
628 * @param length Length of array (also set as bigint length).
629 */
630static void bigint_alloc(bigint_t *bigint, size_t length)
631{
632 size_t a_length;
633
634#ifdef DEBUG_BIGINT_TRACE
635 printf("Allocate bigint digit array.\n");
636#endif
637 /* malloc() sometimes cannot allocate blocks of zero size. */
638 if (length == 0)
639 a_length = 1;
640 else
641 a_length = length;
642
643 bigint->digit = malloc(a_length * sizeof(bigint_word_t));
644 if (bigint->digit == NULL) {
645 printf("Memory allocation failed.\n");
646 exit(1);
647 }
648
649 bigint->length = length;
650}
651
652/** Adjust length field of bigint to be exact.
653 *
654 * When bigint is allocated with bigint_alloc() its length can be
655 * imprecise (higher than actually number of non-zero digits).
656 * Then this function is used to lower @c length to the exact value.
657 */
658static void bigint_refine_len(bigint_t *bigint)
659{
660#ifdef DEBUG_BIGINT_TRACE
661 printf("Refine bigint length.\n");
662#endif
663 while (bigint->length > 0 && bigint->digit[bigint->length - 1] == 0)
664 bigint->length -= 1;
665
666 if (bigint->length == 0)
667 bigint->negative = b_false;
668}
Note: See TracBrowser for help on using the repository browser.