blob: 963e52fd18c3250b3230cfe0a30d659a19394080 [file] [log] [blame]
// 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";
}