| // Copyright 2009 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package Integer |
| |
| const ValueLen = 1000; |
| type Word uint32 |
| type Value *[ValueLen]Word |
| type IntegerImpl struct { |
| val Value |
| } |
| type Integer *IntegerImpl |
| |
| export IntegerImpl, Integer |
| |
| const N = 4; |
| const H = 1 |
| const L = 28; |
| const M = 1 << L - 1; |
| |
| |
| // ---------------------------------------------------------------------------- |
| // Support |
| |
| // TODO What are we going to about asserts? |
| func ASSERT(p bool) { |
| if !p { |
| panic "ASSERT failed"; |
| } |
| } |
| |
| |
| func CHECK(p bool) { |
| if !p { |
| panic "CHECK failed"; |
| } |
| } |
| |
| |
| func UNIMPLEMENTED(s string) { |
| panic "UNIMPLEMENTED: ", s; |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| // |
| |
| // TODO "len" is a reserved word at the moment - I think that's wrong |
| func len_(x Value) int { |
| l := int(x[0]); |
| if l < 0 { return -l; } |
| return l; |
| } |
| |
| |
| func set_len(x Value, len_ int) { |
| x[0] = Word(len_); |
| } |
| |
| |
| func alloc(len_ int) Value { |
| ASSERT(len_ >= 0); |
| z := new([ValueLen] Word); |
| set_len(z, len_); |
| return z; |
| } |
| |
| |
| func sign(x Value) bool { |
| return int(x[0]) < 0; |
| } |
| |
| |
| func zero(x Value) bool { |
| return x[0] == 0; |
| } |
| |
| |
| func neg(x Value) { |
| x[0] = Word(-int(x[0])); |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| // Unsigned ops |
| |
| func make(x int) Value; |
| |
| |
| func Update(x Word) (z, c Word) { |
| // z = x & M; |
| // c = x >> L; |
| return x & M, x >> L; |
| } |
| |
| |
| func uadd(x, y Value) Value { |
| xl := len_(x); |
| yl := len_(y); |
| if xl < yl { |
| return uadd(y, x); |
| } |
| ASSERT(xl >= yl); |
| z := alloc(xl + 1); |
| |
| i := 0; |
| c := Word(0); |
| for i < yl { z[i + H], c = Update(x[i + H] + y[i + H] + c); i++; } |
| for i < xl { z[i + H], c = Update(x[i + H] + c); i++; } |
| if c != 0 { z[i + H] = c; i++; } |
| set_len(z, i); |
| |
| return z; |
| } |
| |
| |
| func usub(x, y Value) Value { |
| xl := len_(x); |
| yl := len_(y); |
| if xl < yl { |
| return uadd(y, x); |
| } |
| ASSERT(xl >= yl); |
| z := alloc(xl + 1); |
| |
| i := 0; |
| c := Word(0); |
| for i < yl { z[i + H], c = Update(x[i + H] - y[i + H] + c); i++; } |
| for i < xl { z[i + H], c = Update(x[i + H] + c); i++; } |
| ASSERT(c == 0); // usub(x, y) must be called with x >= y |
| for i > 0 && z[i - 1 + H] == 0 { i--; } |
| set_len(z, i); |
| |
| return z; |
| } |
| |
| |
| // Computes x = x*a + c (in place) for "small" a's. |
| func umul_add(x Value, a, c Word) Value { |
| CHECK(0 <= a && a < (1 << N)); |
| CHECK(0 <= c && c < (1 << N)); |
| if (zero(x) || a == 0) && c == 0 { |
| return make(0); |
| } |
| xl := len_(x); |
| |
| z := alloc(xl + 1); |
| i := 0; |
| for i < xl { z[i + H], c = Update(x[i + H] * a + c); i++; } |
| if c != 0 { z[i + H] = c; i++; } |
| set_len(z, i); |
| |
| return z; |
| } |
| |
| |
| // Computes x = x div d (in place) for "small" d's. Returns x mod d. |
| func umod(x Value, d Word) Word { |
| CHECK(0 < d && d < (1 << N)); |
| xl := len_(x); |
| c := Word(0); |
| |
| i := xl; |
| for i > 0 { |
| i--; |
| c = c << L + x[i + H]; |
| |
| q := c / d; |
| x[i + H] = q; |
| |
| //x[i + H] = c / d; // BUG |
| |
| c = c % d; |
| } |
| if xl > 0 && x[xl - 1 + H] == 0 { |
| set_len(x, xl - 1); |
| } |
| |
| return c; |
| } |
| |
| |
| // Returns z = (x * y) div B, c = (x * y) mod B. |
| func umul1(x, y Word) (z Word, c Word) { |
| const L2 = (L + 1) >> 1; |
| const B2 = 1 << L2; |
| const M2 = B2 - 1; |
| |
| x0 := x & M2; |
| x1 := x >> L2; |
| |
| y0 := y & M2; |
| y1 := y >> L2; |
| |
| z10 := x0*y0; |
| z21 := x1*y0 + x0*y1 + (z10 >> L2); |
| |
| cc := x1*y1 + (z21 >> L2); |
| zz := ((z21 & M2) << L2) | (z10 & M2); |
| return zz, cc |
| } |
| |
| |
| func umul(x Value, y Value) Value { |
| if zero(x) || zero(y) { |
| return make(0); |
| } |
| xl := len_(x); |
| yl := len_(y); |
| if xl < yl { |
| return umul(y, x); // for speed |
| } |
| ASSERT(xl >= yl && yl > 0); |
| |
| // initialize z |
| zl := xl + yl; |
| z := alloc(zl); |
| for i := 0; i < zl; i++ { z[i + H] = 0; } |
| |
| k := 0; |
| for j := 0; j < yl; j++ { |
| d := y[j + H]; |
| if d != 0 { |
| k = j; |
| c := Word(0); |
| for i := 0; i < xl; i++ { |
| // compute z[k + H] += x[i + H] * d + c; |
| t := z[k + H] + c; |
| var z1 Word; |
| z1, c = umul1(x[i + H], d); |
| t += z1; |
| z[k + H] = t & M; |
| c += t >> L; |
| k++; |
| } |
| if c != 0 { |
| z[k + H] = Word(c); |
| k++; |
| } |
| } |
| } |
| set_len(z, k); |
| |
| return z; |
| } |
| |
| |
| func ucmp(x Value, y Value) int { |
| xl := len_(x); |
| yl := len_(y); |
| |
| if xl != yl || xl == 0 { |
| return xl - yl; |
| } |
| |
| i := xl - 1; |
| for i > 0 && x[i + H] == y[i + H] { i--; } |
| return int(x[i + H]) - int(y[i + H]); |
| } |
| |
| |
| func ulog(x Value) int { |
| xl := len_(x); |
| if xl == 0 { return 0; } |
| |
| n := (xl - 1) * L; |
| for t := x[xl - 1 + H]; t != 0; t >>= 1 { n++ }; |
| |
| return n; |
| } |
| |
| |
| func make(x int) Value { |
| if x == 0 { |
| z := alloc(0); |
| set_len(z, 0); |
| return z; |
| } |
| |
| if x == -x { // smallest int |
| z := alloc(2); |
| z[0 + H] = 0; |
| z[1 + H] = 1; |
| set_len(z, -2); |
| return z; |
| } |
| |
| z := alloc(1); |
| if x < 0 { |
| z[0 + H] = Word(-x); |
| set_len(z, -1); |
| } else { |
| z[0 + H] = Word(x); |
| set_len(z, 1); |
| } |
| |
| return z; |
| } |
| |
| |
| func make_from_string(s string) Value { |
| // skip sign, if any |
| i := 0; |
| if len(s) > 0 && (s[i] == '-' || s[i] == '+') { |
| i = 1; |
| } |
| |
| // read digits |
| x := make(0); |
| for i < len(s) && '0' <= s[i] && s[i] <= '9' { |
| x = umul_add(x, 10, Word(s[i] - '0')); |
| i++; |
| } |
| |
| // read sign |
| if len(s) > 0 && s[0] == '-' { |
| neg(x); |
| } |
| |
| return x; |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| // Creation |
| |
| |
| func (x Integer) Init(val Value) Integer { |
| x.val = val; |
| return x; |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| // Signed ops |
| |
| func add(x Value, y Value) Value { |
| var z Value; |
| if sign(x) == sign(y) { |
| // x + y == x + y |
| // (-x) + (-y) == -(x + y) |
| z = uadd(x, y); |
| } else { |
| // x + (-y) == x - y == -(y - x) |
| // (-x) + y == y - x == -(x - y) |
| if ucmp(x, y) >= 0 { |
| z = usub(x, y); |
| } else { |
| z = usub(y, x); |
| neg(z); |
| } |
| } |
| if sign(x) { |
| neg(z); |
| } |
| return z; |
| } |
| |
| |
| func sub(x Value, y Value) Value { |
| var z Value; |
| if sign(x) != sign(y) { |
| // x - (-y) == x + y |
| // (-x) - y == -(x + y) |
| z = uadd(x, y); |
| } else { |
| // x - y == x - y == -(y - x) |
| // (-x) - (-y) == y - x == -(x - y) |
| if ucmp(x, y) >= 0 { |
| z = usub(x, y); |
| } else { |
| z = usub(y, x); |
| neg(z); |
| } |
| } |
| if sign(x) { |
| neg(z); |
| } |
| return z; |
| } |
| |
| |
| func mul(x Value, y Value) Value { |
| // x * y == x * y |
| // x * (-y) == -(x * y) |
| // (-x) * y == -(x * y) |
| // (-x) * (-y) == x * y |
| z := umul(x, y); |
| if sign(x) != sign(y) { |
| neg(z); |
| } |
| return z; |
| } |
| |
| |
| func mul_range(a, b int) Value { |
| if a > b { return make(1) }; |
| if a == b { return make(a) }; |
| if a + 1 == b { return mul(make(a), make(b)) }; |
| m := (a + b) >> 1; |
| ASSERT(a <= m && m < b); |
| return mul(mul_range(a, m), mul_range(m + 1, b)); |
| } |
| |
| |
| func fact(n int) Value { |
| return mul_range(2, n); |
| } |
| |
| |
| // Returns a copy of x with space for one extra digit. |
| func copy(x Value) Value { |
| xl := len_(x); |
| |
| z := alloc(xl + 1); // add space for one extra digit |
| for i := 0; i < xl; i++ { z[i + H] = x[i + H]; } |
| set_len(z, int(x[0])); // don't loose sign! |
| |
| return z; |
| } |
| |
| |
| func tostring(x Value) string { |
| // allocate string |
| // approx. length: 1 char for 3 bits |
| n := ulog(x)/3 + 3; // +1 (round up) +1 (sign) +1 (0 termination) |
| //s := new([]byte, n); |
| s := new([100000]byte); |
| |
| // convert |
| z := copy(x); |
| i := 0; |
| for i == 0 || !zero(z) { |
| s[i] = byte(umod(z, 10) + '0'); |
| i++; |
| }; |
| if sign(x) { |
| s[i] = '-'; |
| i++; |
| } |
| length := i; |
| ASSERT(0 < i && i < n); |
| |
| // reverse in place |
| i--; |
| for j := 0; j < i; j++ { |
| t := s[j]; |
| s[j] = s[i]; |
| s[i] = t; |
| i--; |
| } |
| |
| return string(s)[0:length]; |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| // Creation |
| |
| export FromInt |
| func FromInt(v int) Integer { |
| return new(IntegerImpl).Init(make(v)); |
| } |
| |
| |
| export FromString |
| func FromString(s string) Integer { |
| return new(IntegerImpl).Init(make_from_string(s)); |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| // Arithmetic ops |
| |
| func (x Integer) neg () Integer { |
| if (zero(x.val)) { |
| return new(IntegerImpl).Init(make(0)); |
| } |
| |
| z := copy(x.val); |
| neg(z); |
| return new(IntegerImpl).Init(z); |
| } |
| |
| |
| func (x Integer) add (y Integer) Integer { |
| return new(IntegerImpl).Init(add(x.val, y.val)); |
| } |
| |
| |
| func (x Integer) sub (y Integer) Integer { |
| return new(IntegerImpl).Init(sub(x.val, y.val)); |
| } |
| |
| |
| func (x Integer) mul (y Integer) Integer { |
| return new(IntegerImpl).Init(mul(x.val, y.val)); |
| } |
| |
| |
| func (x Integer) quo (y Integer) Integer { |
| UNIMPLEMENTED("quo"); |
| return nil; |
| } |
| |
| |
| func (x Integer) rem (y Integer) Integer { |
| UNIMPLEMENTED("rem"); |
| return nil; |
| } |
| |
| |
| func (x Integer) div (y Integer) Integer { |
| UNIMPLEMENTED("div"); |
| return nil; |
| } |
| |
| |
| func (x Integer) mod (y Integer) Integer { |
| UNIMPLEMENTED("mod"); |
| return nil; |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| // Arithmetic shifts |
| |
| func (x Integer) shl (s uint) Integer { |
| UNIMPLEMENTED("shl"); |
| return nil; |
| } |
| |
| |
| func (x Integer) shr (s uint) Integer { |
| UNIMPLEMENTED("shr"); |
| return nil; |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| // Logical ops |
| |
| func (x Integer) inv () Integer { |
| UNIMPLEMENTED("inv"); |
| return nil; |
| } |
| |
| |
| func (x Integer) and (y Integer) Integer { |
| UNIMPLEMENTED("and"); |
| return nil; |
| } |
| |
| |
| func (x Integer) or (y Integer) Integer { |
| UNIMPLEMENTED("or"); |
| return nil; |
| } |
| |
| |
| func (x Integer) xor (y Integer) Integer { |
| UNIMPLEMENTED("xor"); |
| return nil; |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| // Comparisons |
| |
| func (x Integer) cmp (y Integer) int { |
| // do better then this |
| d := x.sub(y); |
| switch { |
| case sign(d.val): return -1; |
| case zero(d.val): return 0; |
| default : return +1; |
| } |
| CHECK(false); // unreachable |
| } |
| |
| |
| func (x Integer) eql (y Integer) bool { |
| return x.cmp(y) == 0; |
| } |
| |
| |
| func (x Integer) neq (y Integer) bool { |
| return x.cmp(y) != 0; |
| } |
| |
| |
| func (x Integer) lss (y Integer) bool { |
| return x.cmp(y) < 0; |
| } |
| |
| |
| func (x Integer) leq (y Integer) bool { |
| return x.cmp(y) <= 0; |
| } |
| |
| |
| func (x Integer) gtr (y Integer) bool { |
| return x.cmp(y) > 0; |
| } |
| |
| |
| func (x Integer) geq (y Integer) bool { |
| return x.cmp(y) >= 0; |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| // Specials |
| |
| export Fact |
| func Fact(n int) Integer { |
| return new(IntegerImpl).Init(fact(n)); |
| } |
| |
| |
| func (x Integer) ToString() string { |
| return tostring(x.val); |
| } |
| |
| |
| func (x Integer) ToInt() int { |
| v := x.val; |
| if len_(v) <= 1 { |
| if zero(v) { |
| return 0; |
| } |
| i := int(v[0 + H]); |
| if sign(v) { |
| i = -i; // incorrect for smallest int |
| } |
| return i; |
| } |
| panic "integer too large"; |
| } |