blob: 29966c7bc57ebf0e0ef67737dbd8791923f1d036 [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.
// This file provides Go implementations of elementary multi-precision
// arithmetic operations on word vectors. Needed for platforms without
// assembly implementations of these routines.
package big
// TODO(gri) Decide if Word needs to remain exported.
type Word uintptr
const (
// Compute the size _S of a Word in bytes.
_m = ^Word(0)
_logS = _m>>8&1 + _m>>16&1 + _m>>32&1
_S = 1 << _logS
_W = _S << 3 // word size in bits
_B = 1 << _W // digit base
_M = _B - 1 // digit mask
_W2 = _W / 2 // half word size in bits
_B2 = 1 << _W2 // half digit base
_M2 = _B2 - 1 // half digit mask
)
// ----------------------------------------------------------------------------
// Elementary operations on words
//
// These operations are used by the vector operations below.
// z1<<_W + z0 = x+y+c, with c == 0 or 1
func addWW_g(x, y, c Word) (z1, z0 Word) {
yc := y + c
z0 = x + yc
if z0 < x || yc < y {
z1 = 1
}
return
}
// z1<<_W + z0 = x-y-c, with c == 0 or 1
func subWW_g(x, y, c Word) (z1, z0 Word) {
yc := y + c
z0 = x - yc
if z0 > x || yc < y {
z1 = 1
}
return
}
// z1<<_W + z0 = x*y
func mulWW_g(x, y Word) (z1, z0 Word) {
// Split x and y into 2 halfWords each, multiply
// the halfWords separately while avoiding overflow,
// and return the product as 2 Words.
if x < y {
x, y = y, x
}
if x < _B2 {
// y < _B2 because y <= x
// sub-digits of x and y are (0, x) and (0, y)
// z = z[0] = x*y
z0 = x * y
return
}
if y < _B2 {
// sub-digits of x and y are (x1, x0) and (0, y)
// x = (x1*_B2 + x0)
// y = (y1*_B2 + y0)
x1, x0 := x>>_W2, x&_M2
// x*y = t2*_B2*_B2 + t1*_B2 + t0
t0 := x0 * y
t1 := x1 * y
// compute result digits but avoid overflow
// z = z[1]*_B + z[0] = x*y
z0 = t1<<_W2 + t0
z1 = (t1 + t0>>_W2) >> _W2
return
}
// general case
// sub-digits of x and y are (x1, x0) and (y1, y0)
// x = (x1*_B2 + x0)
// y = (y1*_B2 + y0)
x1, x0 := x>>_W2, x&_M2
y1, y0 := y>>_W2, y&_M2
// x*y = t2*_B2*_B2 + t1*_B2 + t0
t0 := x0 * y0
// t1 := x1*y0 + x0*y1;
var c Word
t1 := x1 * y0
t1a := t1
t1 += x0 * y1
if t1 < t1a {
c++
}
t2 := x1*y1 + c*_B2
// compute result digits but avoid overflow
// z = z[1]*_B + z[0] = x*y
// This may overflow, but that's ok because we also sum t1 and t0 above
// and we take care of the overflow there.
z0 = t1<<_W2 + t0
// z1 = t2 + (t1 + t0>>_W2)>>_W2;
var c3 Word
z1 = t1 + t0>>_W2
if z1 < t1 {
c3++
}
z1 >>= _W2
z1 += c3 * _B2
z1 += t2
return
}
// z1<<_W + z0 = x*y + c
func mulAddWWW_g(x, y, c Word) (z1, z0 Word) {
// Split x and y into 2 halfWords each, multiply
// the halfWords separately while avoiding overflow,
// and return the product as 2 Words.
// TODO(gri) Should implement special cases for faster execution.
// general case
// sub-digits of x, y, and c are (x1, x0), (y1, y0), (c1, c0)
// x = (x1*_B2 + x0)
// y = (y1*_B2 + y0)
x1, x0 := x>>_W2, x&_M2
y1, y0 := y>>_W2, y&_M2
c1, c0 := c>>_W2, c&_M2
// x*y + c = t2*_B2*_B2 + t1*_B2 + t0
// (1<<32-1)^2 == 1<<64 - 1<<33 + 1, so there's space to add c0 in here.
t0 := x0*y0 + c0
// t1 := x1*y0 + x0*y1 + c1;
var c2 Word // extra carry
t1 := x1*y0 + c1
t1a := t1
t1 += x0 * y1
if t1 < t1a { // If the number got smaller then we overflowed.
c2++
}
t2 := x1*y1 + c2*_B2
// compute result digits but avoid overflow
// z = z[1]*_B + z[0] = x*y
// z0 = t1<<_W2 + t0;
// This may overflow, but that's ok because we also sum t1 and t0 below
// and we take care of the overflow there.
z0 = t1<<_W2 + t0
var c3 Word
z1 = t1 + t0>>_W2
if z1 < t1 {
c3++
}
z1 >>= _W2
z1 += t2 + c3*_B2
return
}
// q = (x1<<_W + x0 - r)/y
// The most significant bit of y must be 1.
func divStep(x1, x0, y Word) (q, r Word) {
d1, d0 := y>>_W2, y&_M2
q1, r1 := x1/d1, x1%d1
m := q1 * d0
r1 = r1*_B2 | x0>>_W2
if r1 < m {
q1--
r1 += y
if r1 >= y && r1 < m {
q1--
r1 += y
}
}
r1 -= m
r0 := r1 % d1
q0 := r1 / d1
m = q0 * d0
r0 = r0*_B2 | x0&_M2
if r0 < m {
q0--
r0 += y
if r0 >= y && r0 < m {
q0--
r0 += y
}
}
r0 -= m
q = q1*_B2 | q0
r = r0
return
}
// Length of x in bits.
func bitLen(x Word) (n int) {
for ; x >= 0x100; x >>= 8 {
n += 8
}
for ; x > 0; x >>= 1 {
n++
}
return
}
// log2 computes the integer binary logarithm of x.
// The result is the integer n for which 2^n <= x < 2^(n+1).
// If x == 0, the result is -1.
func log2(x Word) int {
return bitLen(x) - 1
}
// Number of leading zeros in x.
func leadingZeros(x Word) uint {
return uint(_W - bitLen(x))
}
// q = (x1<<_W + x0 - r)/y
func divWW_g(x1, x0, y Word) (q, r Word) {
if x1 == 0 {
q, r = x0/y, x0%y
return
}
var q0, q1 Word
z := leadingZeros(y)
if y > x1 {
if z != 0 {
y <<= z
x1 = (x1 << z) | (x0 >> (_W - z))
x0 <<= z
}
q0, x0 = divStep(x1, x0, y)
q1 = 0
} else {
if z == 0 {
x1 -= y
q1 = 1
} else {
z1 := _W - z
y <<= z
x2 := x1 >> z1
x1 = (x1 << z) | (x0 >> z1)
x0 <<= z
q1, x1 = divStep(x2, x1, y)
}
q0, x0 = divStep(x1, x0, y)
}
r = x0 >> z
if q1 != 0 {
panic("div out of range")
}
return q0, r
}
func addVV_g(z, x, y []Word) (c Word) {
for i := range z {
c, z[i] = addWW_g(x[i], y[i], c)
}
return
}
func subVV_g(z, x, y []Word) (c Word) {
for i := range z {
c, z[i] = subWW_g(x[i], y[i], c)
}
return
}
func addVW_g(z, x []Word, y Word) (c Word) {
c = y
for i := range z {
c, z[i] = addWW_g(x[i], c, 0)
}
return
}
func subVW_g(z, x []Word, y Word) (c Word) {
c = y
for i := range z {
c, z[i] = subWW_g(x[i], c, 0)
}
return
}
func shlVW_g(z, x []Word, s Word) (c Word) {
if n := len(z); n > 0 {
ŝ := _W - s
w1 := x[n-1]
c = w1 >> ŝ
for i := n - 1; i > 0; i-- {
w := w1
w1 = x[i-1]
z[i] = w<<s | w1>>ŝ
}
z[0] = w1 << s
}
return
}
func shrVW_g(z, x []Word, s Word) (c Word) {
if n := len(z); n > 0 {
ŝ := _W - s
w1 := x[0]
c = w1 << ŝ
for i := 0; i < n-1; i++ {
w := w1
w1 = x[i+1]
z[i] = w>>s | w1<<ŝ
}
z[n-1] = w1 >> s
}
return
}
func mulAddVWW_g(z, x []Word, y, r Word) (c Word) {
c = r
for i := range z {
c, z[i] = mulAddWWW_g(x[i], y, c)
}
return
}
func addMulVVW_g(z, x []Word, y Word) (c Word) {
for i := range z {
z1, z0 := mulAddWWW_g(x[i], y, z[i])
c, z[i] = addWW_g(z0, c, 0)
c += z1
}
return
}
func divWVW_g(z []Word, xn Word, x []Word, y Word) (r Word) {
r = xn
for i := len(z) - 1; i >= 0; i-- {
z[i], r = divWW_g(r, x[i], y)
}
return
}