blob: 725b1acea8a1401f2028b6b888df79aad5e0bfaf [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 implements Newton-Raphson division and uses
// it as an additional test case for bignum.
//
// Division of x/y is achieved by computing r = 1/y to
// obtain the quotient q = x*r = x*(1/y) = x/y. The
// reciprocal r is the solution for f(x) = 1/x - y and
// the solution is approximated through iteration. The
// iteration does not require division.
package bignum
import "testing"
// An fpNat is a Natural scaled by a power of two
// (an unsigned floating point representation). The
// value of an fpNat x is x.m * 2^x.e .
//
type fpNat struct {
m Natural
e int
}
// sub computes x - y.
func (x fpNat) sub(y fpNat) fpNat {
switch d := x.e - y.e; {
case d < 0:
return fpNat{x.m.Sub(y.m.Shl(uint(-d))), x.e}
case d > 0:
return fpNat{x.m.Shl(uint(d)).Sub(y.m), y.e}
}
return fpNat{x.m.Sub(y.m), x.e}
}
// mul2 computes x*2.
func (x fpNat) mul2() fpNat { return fpNat{x.m, x.e + 1} }
// mul computes x*y.
func (x fpNat) mul(y fpNat) fpNat { return fpNat{x.m.Mul(y.m), x.e + y.e} }
// mant computes the (possibly truncated) Natural representation
// of an fpNat x.
//
func (x fpNat) mant() Natural {
switch {
case x.e > 0:
return x.m.Shl(uint(x.e))
case x.e < 0:
return x.m.Shr(uint(-x.e))
}
return x.m
}
// nrDivEst computes an estimate of the quotient q = x0/y0 and returns q.
// q may be too small (usually by 1).
//
func nrDivEst(x0, y0 Natural) Natural {
if y0.IsZero() {
panic("division by zero")
return nil
}
// y0 > 0
if y0.Cmp(Nat(1)) == 0 {
return x0
}
// y0 > 1
switch d := x0.Cmp(y0); {
case d < 0:
return Nat(0)
case d == 0:
return Nat(1)
}
// x0 > y0 > 1
// Determine maximum result length.
maxLen := int(x0.Log2() - y0.Log2() + 1)
// In the following, each number x is represented
// as a mantissa x.m and an exponent x.e such that
// x = xm * 2^x.e.
x := fpNat{x0, 0}
y := fpNat{y0, 0}
// Determine a scale factor f = 2^e such that
// 0.5 <= y/f == y*(2^-e) < 1.0
// and scale y accordingly.
e := int(y.m.Log2()) + 1
y.e -= e
// t1
var c = 2.9142
const n = 14
t1 := fpNat{Nat(uint64(c * (1 << n))), -n}
// Compute initial value r0 for the reciprocal of y/f.
// r0 = t1 - 2*y
r := t1.sub(y.mul2())
two := fpNat{Nat(2), 0}
// Newton-Raphson iteration
p := Nat(0)
for i := 0; ; i++ {
// check if we are done
// TODO: Need to come up with a better test here
// as it will reduce computation time significantly.
// q = x*r/f
q := x.mul(r)
q.e -= e
res := q.mant()
if res.Cmp(p) == 0 {
return res
}
p = res
// r' = r*(2 - y*r)
r = r.mul(two.sub(y.mul(r)))
// reduce mantissa size
// TODO: Find smaller bound as it will reduce
// computation time massively.
d := int(r.m.Log2()+1) - maxLen
if d > 0 {
r = fpNat{r.m.Shr(uint(d)), r.e + d}
}
}
panic("unreachable")
return nil
}
func nrdiv(x, y Natural) (q, r Natural) {
q = nrDivEst(x, y)
r = x.Sub(y.Mul(q))
// if r is too large, correct q and r
// (usually one iteration)
for r.Cmp(y) >= 0 {
q = q.Add(Nat(1))
r = r.Sub(y)
}
return
}
func div(t *testing.T, x, y Natural) {
q, r := nrdiv(x, y)
qx, rx := x.DivMod(y)
if q.Cmp(qx) != 0 {
t.Errorf("x = %s, y = %s, got q = %s, want q = %s", x, y, q, qx)
}
if r.Cmp(rx) != 0 {
t.Errorf("x = %s, y = %s, got r = %s, want r = %s", x, y, r, rx)
}
}
func idiv(t *testing.T, x0, y0 uint64) { div(t, Nat(x0), Nat(y0)) }
func TestNRDiv(t *testing.T) {
idiv(t, 17, 18)
idiv(t, 17, 17)
idiv(t, 17, 1)
idiv(t, 17, 16)
idiv(t, 17, 10)
idiv(t, 17, 9)
idiv(t, 17, 8)
idiv(t, 17, 5)
idiv(t, 17, 3)
idiv(t, 1025, 512)
idiv(t, 7489595, 2)
idiv(t, 5404679459, 78495)
idiv(t, 7484890589595, 7484890589594)
div(t, Fact(100), Fact(91))
div(t, Fact(1000), Fact(991))
//div(t, Fact(10000), Fact(9991)); // takes too long - disabled for now
}