// 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 }