zmath.c
来自「Calc Software Package for Number Calc」· C语言 代码 · 共 2,000 行 · 第 1/3 页
C
2,000 行
*/voidzreduce(ZVALUE z1, ZVALUE z2, ZVALUE *z1res, ZVALUE *z2res){ ZVALUE tmp; if (zisabsleone(z1) || zisabsleone(z2)) tmp = _one_; else zgcd(z1, z2, &tmp); if (zisunit(tmp)) { zcopy(z1, z1res); zcopy(z2, z2res); } else { zequo(z1, tmp, z1res); zequo(z2, tmp, z2res); } zfree(tmp);}/* * Compute the quotient and remainder for division of an integer by an * integer, rounding criteria determined by rnd. Returns the sign of * the remainder. */longzdiv(ZVALUE z1, ZVALUE z2, ZVALUE *quo, ZVALUE *rem, long rnd){ register HALF *a, *b; HALF s, u; HALF *A, *B, *a1, *b0; FULL f, g, h, x; BOOL adjust, onebit; LEN m, n, len, i, p, j1, j2, k; long t, val; if (ziszero(z2)) { math_error("Division by zero in zdiv"); /*NOTREACHED*/ } m = z1.len; n = z2.len; B = z2.v; s = 0; if (m < n) { A = alloc(n + 1); memcpy(A, z1.v, m * sizeof(HALF)); for (i = m; i <= n; i++) A[i] = 0; a1 = A + n; len = 1; goto done; } A = alloc(m + 2); memcpy(A, z1.v, m * sizeof(HALF)); A[m] = 0; A[m + 1] = 0; len = m - n + 1; /* quotient length will be len or len +/- 1 */ a1 = A + n; /* start of digits for quotient */ b0 = B; p = n; while (!*b0) { /* b0: working start for divisor */ ++b0; --p; } if (p == 1) { u = *b0; if (u == 1) { for (; m >= n; m--) A[m] = A[m - 1]; A[m] = 0; goto done; } f = 0; a = A + m; i = len; while (i--) { f = f << BASEB | *--a; a[1] = (HALF)(f / u); f = f % u; } *a = (HALF)f; m = n; goto done; } f = B[n - 1]; k = 1; while (f >>= 1) k++; /* k: number of bits in top divisor digit */ j1 = BASEB - k; j2 = BASEB + j1; h = j1 ? ((FULL) B[n - 1] << j1 | B[n - 2] >> k) : B[n-1]; onebit = (BOOL)((B[n - 2] >> (k - 1)) & 1); m++; while (m > n) { m--; f = (FULL) A[m] << j2 | (FULL) A[m - 1] << j1; if (j1) f |= A[m - 2] >> k; if (s) f = ~f; x = f / h; if (x) { if (onebit && x > TOPHALF + f % h) x--; a = A + m - p; b = b0; u = 0; i = p; if (s) { while (i--) { f = (FULL) *a + u + x * *b++; *a++ = (HALF) f; u = (HALF) (f >> BASEB); } s = *a + u; A[m] = (HALF) (~x + !s); } else { while (i--) { f = (FULL) *a - u - x * *b++; *a++ = (HALF) f; u = -(HALF)(f >> BASEB); } s = *a - u; A[m] = (HALF)(x + s); } } else A[m] = s; }done: while (m > 0 && A[m - 1] == 0) m--; if (m == 0 && s == 0) { *rem = _zero_; val = 0; if (a1[len - 1] == 0) len--; if (len == 0) { *quo = _zero_; } else { quo->len = len; quo->v = alloc(len); memcpy(quo->v, a1, len * sizeof(HALF)); quo->sign = z1.sign ^ z2.sign; } freeh(A); return val; } if (rnd & 8) adjust = (((*a1 ^ rnd) & 1) ? TRUE : FALSE); else adjust = (((rnd & 1) ^ z1.sign ^ z2.sign) ? TRUE : FALSE); if (rnd & 2) adjust ^= z1.sign ^ z2.sign; if (rnd & 4) adjust ^= z2.sign; if (rnd & 16) { /* round-off case */ a = A + n; b = B + n; i = n + 1; f = g = 0; t = -1; if (s) { while (--i > 0) { g = (FULL) *--a + (*--b >> 1 | f); f = *b & 1 ? TOPHALF : 0; if (g != BASE1) break; } if (g == BASE && f == 0) { while ((--i > 0) && ((*--a | *--b) == 0)); t = (i > 0); } else if (g >= BASE) { t = 1; } } else { while (--i > 0) { g = (FULL) *--a - (*--b >> 1 | f); f = *b & 1 ? TOPHALF : 0; if (g != 0) break; } if (g > 0 && g < BASE) t = 1; else if (g == 0 && f == 0) t = 0; } if (t) adjust = (t > 0); } if (adjust) { i = len; a = a1; while (i > 0 && *a == BASE1) { i--; *a++ = 0; } (*a)++; if (i == 0) len++; } if (s && adjust) { i = 0; while (A[i] == 0) i++; A[i] = -A[i]; while (++i < n) A[i] = ~A[i]; m = n; while (A[m - 1] == 0) m--; } if (!s && adjust) { a = A; b = B; i = n; u = 0; while (i--) { f = (FULL) *b++ - *a - (FULL) u; *a++ = (HALF) f; u = -(HALF)(f >> BASEB); } m = n; while (A[m - 1] == 0) m--; } if (s && !adjust) { a = A; b = B; i = n; f = 0; while (i--) { f = (FULL) *b++ + *a + (f >> BASEB); *a++ = (HALF) f; } m = n; while (A[m-1] == 0) m--; } rem->len = m; rem->v = alloc(m); memcpy(rem->v, A, m * sizeof(HALF)); rem->sign = z1.sign ^ adjust; val = rem->sign ? -1 : 1; if (a1[len - 1] == 0) len--; if (len == 0) { *quo = _zero_; } else { quo->len = len; quo->v = alloc(len); memcpy(quo->v, a1, len * sizeof(HALF)); quo->sign = z1.sign ^ z2.sign; } freeh(A); return val;}/* * Compute and store at a specified location the integer quotient * of two integers, the type of rounding being determined by rnd. * Returns the sign of z1/z2 - calculated quotient. */longzquo(ZVALUE z1, ZVALUE z2, ZVALUE *res, long rnd){ ZVALUE tmp; long val; val = zdiv(z1, z2, res, &tmp, rnd); if (z2.sign) val = -val; zfree(tmp); return val;}/* * Compute and store at a specified location the remainder for * division of an integer by an integer, the type of rounding * used being determined by rnd. Returns the sign of the remainder. */longzmod(ZVALUE z1, ZVALUE z2, ZVALUE *res, long rnd){ ZVALUE tmp; long val; val = zdiv(z1, z2, &tmp, res, rnd); zfree(tmp); return val;}/* * Computes the quotient z1/z2 on the assumption that this is exact. * There is no thorough check on the exactness of the division * so this should not be called unless z1/z2 is an integer. */voidzequo(ZVALUE z1, ZVALUE z2, ZVALUE *res){ LEN i, j, k, len, m, n, o, p; HALF *a, *a0, *A, *b, *B, u, v, w, x; FULL f, g; if (ziszero(z1)) { *res = _zero_; return; } if (ziszero(z2)) { math_error("Division by zero"); /*NOTREACHED*/ } if (zisone(z2)) { zcopy(z1, res); return; } if (zhighbit(z1) < zhighbit(z2)) { math_error("Bad call to zequo"); /*NOTREACHED*/ } B = z2.v; o = 0; while (!*B) { ++B; ++o; } m = z1.len - o; n = z2.len - o; len = m - n + 1; /* Maximum length of quotient */ v = *B; A = alloc(len+1); memcpy(A, z1.v + o, len * sizeof(HALF)); A[len] = 0; if (n == 1) { if (v > 1) { a = A + len; i = len; f = 0; while (i--) { f = f << BASEB | *--a; *a = (HALF)(f / v); f %= v; } } } else { k = 0; while (!(v & 1)) { k++; v >>= 1; } j = BASEB - k; if (n > 1 && k > 0) v |= B[1] << j; u = v - 1; w = x = 1; while (u) { /* To find w = inverse of v modulo BASE */ do { v <<= 1; x <<= 1; } while (!(u & x)); u += v; w |= x; } a0 = A; p = len; while (p > 1) { if (!*a0) { while (!*++a0 && p > 1) p--; --a0; } if (p == 1) break; x = k ? w * (*a0 >> k | a0[1] << j) : w * *a0; g = x; if (x) { a = a0; b = B; u = 0; i = n; if (i > p) i = p; while (i--) { f = (FULL) *a - g * *b++ - (FULL) u; *a++ = (HALF)f; u = -(HALF)(f >> BASEB); } if (u && p > n) { i = p - n; while (u && i--) { f = (FULL) *a - u; *a++ = (HALF) f; u = -(HALF)(f >> BASEB); } } } *a0++ = x; p--; } if (k == 0) { *a0 = w * *a0; } else { u = (HALF)(w * *a0) >> k; x = (HALF)(((FULL) z1.v[z1.len - 1] << BASEB | z1.v[z1.len - 2]) /((FULL) B[n-1] << BASEB | B[n-2])); if ((x ^ u) & 1) x--; *a0 = x; } } if (!A[len - 1]) len--; res->v = A; res->len = len; res->sign = z1.sign != z2.sign;}/* * Return the quotient and remainder of an integer divided by a small * number. A nonzero remainder is only meaningful when both numbers * are positive. */longzdivi(ZVALUE z, long n, ZVALUE *res){ HALF *h1, *sd; FULL val; HALF divval[2]; ZVALUE div; ZVALUE dest; LEN len; if (n == 0) { math_error("Division by zero"); /*NOTREACHED*/ } if (ziszero(z)) { *res = _zero_; return 0; } if (n < 0) { n = -n; z.sign = !z.sign; } if (n == 1) { zcopy(z, res); return 0; } /* * If the division is by a large number, then call the normal * divide routine. */ if (n & ~BASE1) { div.sign = 0; div.v = divval; divval[0] = (HALF) n;#if LONG_BITS > BASEB divval[1] = (HALF)(((FULL) n) >> BASEB); div.len = 2;#else div.len = 1;#endif zdiv(z, div, res, &dest, 0); n = ztolong(dest); zfree(dest); return n; } /* * Division is by a small number, so we can be quick about it. */ len = z.len; dest.sign = z.sign; dest.len = len; dest.v = alloc(len); h1 = z.v + len; sd = dest.v + len; val = 0; while (len--) { val = ((val << BASEB) + ((FULL) *--h1)); *--sd = (HALF)(val / n); val %= n; } zquicktrim(dest); *res = dest; return (long)val;}/* * Calculate the mod of an integer by a small number. * This is only defined for positive moduli. */longzmodi(ZVALUE z, long n){ register HALF *h1; FULL val; HALF divval[2]; ZVALUE div; ZVALUE temp; long len; if (n == 0) { math_error("Division by zero"); /*NOTREACHED*/ } if (n < 0) { math_error("Non-positive modulus"); /*NOTREACHED*/ } if (ziszero(z) || (n == 1)) return 0; if (zisone(z)) return 1; /* * If the modulus is by a large number, then call the normal * modulo routine. */ if (n & ~BASE1) { div.sign = 0; div.v = divval; divval[0] = (HALF) n;#if LONG_BITS > BASEB divval[1] = (HALF)(((FULL) n) >> BASEB); div.len = 2;#else div.len = 1;#endif zmod(z, div, &temp, 0); n = ztolong(temp); zfree(temp); return n; } /* * The modulus is by a small number, so we can do this quickly. */ len = z.len; h1 = z.v + len; val = 0; while (len-- > 0) val = ((val << BASEB) + ((FULL) *--h1)) % n; if (val && z.sign) val = n - val; return (long)val;}/* * Return whether or not one number exactly divides another one. * Returns TRUE if division occurs with no remainder. * z1 is the number to be divided by z2. */BOOLzdivides(ZVALUE z1, ZVALUE z2){ LEN i, j, k, m, n; HALF u, v, w, x; HALF *a, *a0, *A, *b, *B, *c, *d; FULL f; BOOL ans; if (zisunit(z2) || ziszero(z1)) return TRUE; if (ziszero(z2)) return FALSE; m = z1.len; n = z2.len; if (m < n) return FALSE; c = z1.v; d = z2.v; while (!*d) { if (*c++) return FALSE; d++; m--; n--; } j = 0; u = *c; v = *d; while (!(v & 1)) { /* Counting and checking zero bits */ if (u & 1) return FALSE; u >>= 1; v >>= 1; j++; } if (n == 1 && v == 1) return TRUE; if (!*c) { /* Removing any further zeros */ while(!*++c) m--; c--; } if (m < n) return FALSE; if (j) { B = alloc((LEN)n); /* Array for shifted z2 */ d += n; b = B + n; i = n; f = 0; while(i--) { f = f << BASEB | *--d; *--b = (HALF)(f >> j); } if (!B[n - 1]) n--; } else B = d; u = *B; v = x = 1; w = 0; while (x) { /* Finding minv(*B, BASE) */ if (v & x) { v -= x * u; w |= x; } x <<= 1; } A = alloc((LEN)(m + 2)); /* Main working array */ memcpy(A, c, m * sizeof(HALF)); A[m + 1] = 1; A[m] = 0; k = m - n + 1; /* Length of presumed quotient */ a0 = A; while (k--) { if (*a0) { x = w * *a0; a = a0; b = B; i = n; u = 0; while (i--) { f = (FULL) *a - (FULL) x * *b++ - u; *a++ = (HALF)f; u = -(HALF)(f >> BASEB); } f = (FULL) *a - u; *a++ = (HALF)f; u = -(HALF)(f >> BASEB); if (u) { while (*a == 0) *a++ = BASE1; (*a)--; } } a0++; } ans = FALSE; if (A[m + 1]) { a = A + m; while (--n && !*--a); if (!n) ans = TRUE; } freeh(A); if (j) freeh(B);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?