📄 bigd.c
字号:
/* $Id: bigd.c $ */
/******************** SHORT COPYRIGHT NOTICE**************************
This source code is part of the BigDigits multiple-precision
arithmetic library Version 2.1 originally written by David Ireland,
copyright (c) 2001-6 D.I. Management Services Pty Limited, all rights
reserved. It is provided "as is" with no warranties. You may use
this software under the terms of the full copyright notice
"bigdigitsCopyright.txt" that should have been included with this
library or can be obtained from <www.di-mgt.com.au/bigdigits.html>.
This notice must always be retained in any copy.
******************* END OF COPYRIGHT NOTICE***************************/
/*
Last updated:
$Date: 2006-08-23 11:13:00 $
$Revision: 2.1.0 $
$Author: dai $
*/
/* BIGD "bd" wrapper functions around BigDigits "mp" functions */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "bigd.h"
#include "bigdigits.h"
/* Required for opaque pointers */
#define T BIGD
struct T
{
DIGIT_T *digits; /* Ptr to array of digits, least sig. first */
size_t ndigits; /* No of non-zero significant digits */
size_t maxdigits; /* Max size allocated */
};
/*
All these functions MUST make sure that there are always
enough digits before doing anything, and SHOULD reset <ndigits>
afterwards to reflect the final significant size.
<maxdigits> is the size allocated.
<ndigits> may be zero.
<ndigits> may be too long if MS digits compute to zero so
consider it a limit on significant digits, not gospel.
It is an error to pass a NULL BIGD parameter except to bdFree.
*/
#ifdef _DEBUG
static int debug = 0; /* <= change this to > 0 for console debugging */
#else
static int debug = 0; /* <= ALWAYS ZERO */
#endif
/* Useful definitions */
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef max
#define max(a,b) (((a) > (b)) ? (a) : (b))
#endif
#ifndef min
#define min(a,b) (((a) < (b)) ? (a) : (b))
#endif
T bdNew(void)
{
struct T *p;
p = calloc(1, (long)sizeof(*p));
if (!p)
{
mpFail("bdNew: Failed to calloc memory.");
}
copyright_notice();
/* set up with single zero digit */
p->digits = mpAlloc(1);
p->digits[0] = 0;
p->ndigits = 0;
p->maxdigits = 1;
return p;
}
void bdFree(T *p)
/* Zeroise and free memory. Set ptr to NULL. */
{
T bd = *p;
if (*p)
{
/* Zeroise them all, just in case */
if (bd->digits)
{
mpSetZero(bd->digits, bd->maxdigits);
free(bd->digits);
}
bd->maxdigits = 0;
bd->ndigits = 0;
free(*p);
}
*p = NULL;
}
static int bd_resize(T b, size_t newsize)
{
/*
Internal fn to re-size a BIGD structure before a calc.
Use carefully!
1. If growing, it allocs more digits and increases maxdigits
2. If shrinking, it decreases ndigits and zeroises the excess.
3. It does not increase b->ndigits; that's up to you later.
4. It does not release excess digits; use bdFree.
In other words, it's like middle-aged spread:
you go from a 32" waist to a 38 but can never go backwards.
Be careful doing the following:-
n = new_size_we_expect;
bd_resize(b, n);
mpFunctionOfSorts(b->digits, n);
b->ndigits = mpSizeof(b->digits, b->ndigits); // NO!
b->ndigits may be set too short
Better:
n = new_size_we_expect;
bd_resize(b, n);
mpFunctionOfSorts(b->digits, n);
b->ndigits = mpSizeof(b->digits, n); // Yes.
*/
size_t i;
/* Check just in case NULL */
assert(b);
/* If we are shrinking, clear high digits */
if (newsize < b->ndigits)
{
for (i = newsize; i < b->ndigits; i++)
b->digits[i] = 0;
b->ndigits = newsize;
return 0;
}
/* We need more room */
if (b->maxdigits < newsize)
{
/* Increase size of digit array */
b->digits = (DIGIT_T *)realloc(b->digits, newsize * sizeof(DIGIT_T));
/* Check for failure */
if (!b->digits)
{
mpFail("bd_resize: Failed to realloc memory.");
}
b->maxdigits = newsize; /* Remember new max */
}
/* Make sure new digits are zero */
for (i = b->ndigits; i < newsize; i++)
b->digits[i] = 0;
return 0;
}
size_t bdConvFromOctets(T b, const unsigned char *c, size_t nbytes)
/* Converts nbytes octets into big digit b, resizing if necessary */
{
size_t ndigits, n;
assert(b);
ndigits = (nbytes + OCTETS_PER_DIGIT - 1) / OCTETS_PER_DIGIT;
bd_resize(b, ndigits);
n = mpConvFromOctets(b->digits, ndigits, c, nbytes);
b->ndigits = mpSizeof(b->digits, n);
return n;
}
size_t bdConvToOctets(T b, unsigned char *c, size_t nbytes)
/* Convert big digit b into string of octets, in big-endian order,
padding to nbytes or truncating if necessary.
Returns # significant bytes.
If c is NULL or nbytes == 0 then just return required size.
*/
{
size_t noctets, nbits, n;
assert(b);
nbits = mpBitLength(b->digits, b->ndigits);
noctets = (nbits + 7) / 8;
if (!c || 0 == nbytes)
{
return noctets;
}
n = mpConvToOctets(b->digits, b->ndigits, c, nbytes);
return noctets;
}
size_t bdConvFromHex(T b, const char *s)
/* Converts a hex string into big digit b */
{
size_t ndigits, n;
assert(b);
/* Revision [2006-02-21] */
/* EDIT: ndigits = (strlen(s) / 2 + OCTETS_PER_DIGIT - 1) / OCTETS_PER_DIGIT; */
ndigits = ((strlen(s) + 1) / 2 + OCTETS_PER_DIGIT - 1) / OCTETS_PER_DIGIT;
bd_resize(b, ndigits);
n = mpConvFromHex(b->digits, ndigits, s);
b->ndigits = mpSizeof(b->digits, n);
return n;
}
size_t bdConvToHex(T b, char *s, size_t smax)
{
assert(b);
return mpConvToHex(b->digits, b->ndigits, s, smax);
}
size_t bdConvFromDecimal(BIGD b, const char *s)
{
size_t ndigits, n;
assert(b);
/* approx size but never too small */
ndigits = (strlen(s) / 2 + OCTETS_PER_DIGIT) / OCTETS_PER_DIGIT;
bd_resize(b, ndigits);
n = mpConvFromDecimal(b->digits, ndigits, s);
b->ndigits = n;
return n;
}
size_t bdConvToDecimal(BIGD b, char *s, size_t smax)
{
assert(b);
return mpConvToDecimal(b->digits, b->ndigits, s, smax);
}
int bdSetShort(T b, bdigit_t value)
/* Converts value into a (single-digit) big digit b */
{
assert(b);
bd_resize(b, 1);
b->digits[0] = (DIGIT_T)value;
b->ndigits = (value ? 1 : 0);
return 0;
}
size_t bdBitLength(T b)
/* Returns base-1 index to most significant bit in b */
{
assert(b);
return mpBitLength(b->digits, b->ndigits);
}
size_t bdSizeof(T b)
/* Returns number of significant non-zero bytes in b */
{
assert(b);
return mpSizeof(b->digits, b->ndigits);
}
/* Print function for bigdigit_t structures */
void bdPrint(T p, size_t flags)
{
size_t n;
assert(p);
n = p->ndigits;
if (n == 0) n = 1;
if (flags & BD_PRINT_TRIM) /* Trim leading zeroes */
{
if (flags & BD_PRINT_NL) /* add newlines */
mpPrintTrimNL(p->digits, n);
else
mpPrintTrim(p->digits, n);
}
else
{
if (flags & BD_PRINT_NL) /* add newlines */
mpPrintNL(p->digits, n);
else
mpPrint(p->digits, n);
}
}
int bdIsEqual(T a, T b)
{
/* Returns true if a == b, else false */
size_t n, na, nb;
assert(a && b);
/* We can't trust ndigits */
na = mpSizeof(a->digits, a->ndigits);
nb = mpSizeof(b->digits, b->ndigits);
if (na != nb)
return FALSE;
if (na == 0 && nb == 0)
return TRUE;
/* Otherwise we have equal lengths */
n = na;
while (n--)
{
if (a->digits[n] != b->digits[n])
return FALSE;
}
return TRUE;
}
int bdIsZero(T a)
/* Returns true if a == 0, else false */
{
assert(a);
return mpIsZero(a->digits, a->ndigits);
}
int bdShortCmp(T a, bdigit_t d)
/* Returns sign of (a-d) */
{
assert(a);
return mpShortCmp(a->digits, d, a->ndigits);
}
int bdCompare(T a, T b)
/* Returns sign of (a-b) */
{
size_t n, na, nb;
assert(a && b);
if (a->ndigits != b->ndigits)
{
na = mpSizeof(a->digits, a->ndigits);
nb = mpSizeof(b->digits, b->ndigits);
if (na > nb) return 1;
if (na < nb) return -1;
n = na;
}
else
n = a->ndigits;
return mpCompare(a->digits, b->digits, n);
}
int bdIsEven(T a)
{
assert(a);
return ISEVEN(a->digits[0]);
}
int bdIsOdd(T a)
{
assert(a);
return ISODD(a->digits[0]);
}
int bdSetEqual(T a, T b)
/* Sets a = b */
{
assert(a && b);
bd_resize(a, b->ndigits);
mpSetEqual(a->digits, b->digits, b->ndigits);
a->ndigits = b->ndigits;
return 0;
}
int bdSetZero(T a)
/* Sets a = 0 */
{
assert(a);
mpSetZero(a->digits, a->ndigits);
a->ndigits = 0;
return 0;
}
int bdShortAdd(T w, T u, bdigit_t d)
/* Compute w = u + d,
returns 1 if we had a carry */
{
DIGIT_T carry;
size_t dig_size = max(u->ndigits, 1);
assert(w && u);
bd_resize(w, dig_size + 1);
carry = mpShortAdd(w->digits, u->digits, d, dig_size);
/* Cope with overflow */
if (carry)
{
w->digits[dig_size] = carry;
w->ndigits = dig_size + 1;
}
else
w->ndigits = dig_size;
return carry;
}
int bdAdd(T w, T u, T v)
/* Compute w = u + v, w#v*/
{
size_t dig_size;
DIGIT_T carry;
assert(w && u && v);
/* Check for cheaper option */
if (v->ndigits == 1)
return bdShortAdd(w, u, v->digits[0]);
/* Make sure u and v are the same size */
dig_size = max(u->ndigits, v->ndigits);
bd_resize(v, dig_size);
bd_resize(u, dig_size);
/* Now make sure w is big enough for sum (incl carry) */
bd_resize(w, dig_size + 1);
/* Finally, do the business */
carry = mpAdd(w->digits, u->digits, v->digits, dig_size);
/* Make sure we've set the right size for w */
if (carry)
{
w->digits[dig_size] = carry;
w->ndigits = dig_size + 1;
}
else
w->ndigits = mpSizeof(w->digits, dig_size);
return carry;
}
int bdAddEx(T w, T u, T v)
/* Compute w = u + v (safe) */
{
DIGIT_T carry;
T ww;
assert(w && u && v);
/* Use temp */
ww = bdNew();
bdSetEqual(ww, w);
carry = bdAdd(ww, u, v);
bdSetEqual(w, ww);
bdFree(&ww);
return carry;
}
int bdShortSub(T w, T u, bdigit_t d)
/* Compute w = u - d, return borrow */
{
DIGIT_T borrow;
size_t dig_size = max(u->ndigits, 1);
assert(w && u);
bd_resize(w, dig_size);
borrow = mpShortSub(w->digits, u->digits, d, dig_size);
w->ndigits = dig_size;
return borrow;
}
int bdSubtract(T w, T u, T v)
/* Compute w = u - v, return borrow, w#v */
{
size_t dig_size;
DIGIT_T borrow;
assert(w && u && v);
/* Check for cheaper option */
if (v->ndigits == 1)
return bdShortSub(w, u, v->digits[0]);
/* Make sure u and v are the same size */
dig_size = max(u->ndigits, v->ndigits);
bd_resize(v, dig_size);
bd_resize(u, dig_size);
bd_resize(w, dig_size);
/* Finally, do the business */
borrow = mpSubtract(w->digits, u->digits, v->digits, dig_size);
/* Make sure we've set the right size for w */
w->ndigits = mpSizeof(w->digits, dig_size);
return borrow;
}
int bdSubtractEx(T w, T u, T v)
/* Compute w = u - v (safe) */
{
DIGIT_T carry;
T ww;
assert(w && u && v);
/* Use temp */
ww = bdNew();
bdSetEqual(ww, w);
carry = bdSubtract(ww, u, v);
bdSetEqual(w, ww);
bdFree(&ww);
return carry;
}
int bdIncrement(T a)
/* Sets a = a + 1, returns carry */
{
assert(a);
return bdShortAdd(a, a, 1);
}
int bdDecrement(T a)
/* Sets a = a - 1, returns borrow */
{
assert(a);
return bdShortSub(a, a, 1);
}
int bdShortMult(T w, T u, bdigit_t d)
/* Compute w = u * d */
{
DIGIT_T overflow;
size_t dig_size = u->ndigits;
assert(w && u);
bd_resize(w, dig_size+1);
overflow = mpShortMult(w->digits, u->digits, d, dig_size);
/* Cope with overflow */
if (overflow)
{
w->digits[dig_size] = overflow;
w->ndigits = dig_size + 1;
}
else
w->ndigits = mpSizeof(w->digits, dig_size);
return 0;
}
int bdMultiply(T w, T u, T v)
/* Compute w = u * v
-- no overlap permitted
*/
{
size_t dig_size;
assert(w && u && v);
/* Check for cheaper option */
if (v->ndigits == 1)
return bdShortMult(w, u, v->digits[0]);
/* Make sure u and v are the same size */
dig_size = max(u->ndigits, v->ndigits);
bd_resize(v, dig_size);
bd_resize(u, dig_size);
/* Now make sure w is big enough for product */
bd_resize(w, 2 * dig_size);
/* Finally, do the business */
mpMultiply(w->digits, u->digits, v->digits, dig_size);
/* Make sure we've set the right size for w */
w->ndigits = mpSizeof(w->digits, 2 * dig_size);
return 0;
}
int bdMultiplyEx(T w, T u, T v)
/* Compute w = u * v (safe) */
{
T ww;
assert(w && u && v);
/* Use temp */
ww = bdNew();
bdSetEqual(ww, w);
bdMultiply(ww, u, v);
bdSetEqual(w, ww);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -