From fe8be85b56d265e3ed2a96c6c492578424acfd23 Mon Sep 17 00:00:00 2001 From: chai2010 Date: Sun, 27 Aug 2023 07:34:00 +0800 Subject: [PATCH 1/4] =?UTF-8?q?unicode/utf8=20=E5=8C=85=E8=A1=A5=E5=85=85?= =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- waroot/src/unicode/utf8/utf8_test.wa | 332 ++++++++++++++++++++++++++- 1 file changed, 331 insertions(+), 1 deletion(-) diff --git a/waroot/src/unicode/utf8/utf8_test.wa b/waroot/src/unicode/utf8/utf8_test.wa index 028b9f9..993901a 100644 --- a/waroot/src/unicode/utf8/utf8_test.wa +++ b/waroot/src/unicode/utf8/utf8_test.wa @@ -129,7 +129,7 @@ func TestEncodeRune { } } -func _TestDecodeRune { +func TestDecodeRune { for _, m := range utf8map { b := []byte(m.str) r, size := DecodeRune(b) @@ -195,6 +195,336 @@ func _TestDecodeRune { } } +func TestDecodeSurrogateRune { + for _, m := range surrogateMap { + b := []byte(m.str) + r, size := DecodeRune(b) + if r != RuneError || size != 1 { + assert(false) + //t.Errorf("DecodeRune(%q) = %x, %d want %x, %d", b, r, size, RuneError, 1) + } + s := m.str + r, size = DecodeRuneInString(s) + if r != RuneError || size != 1 { + assert(false) + //t.Errorf("DecodeRuneInString(%q) = %x, %d want %x, %d", b, r, size, RuneError, 1) + } + } +} + +// Check that a range loop, len([]rune(string)) optimization and +// []rune conversions visit the same runes. +// Not really a test of this package, but the assumption is used here and +// it's good to verify. +func TestRuntimeConversion { + /* + for _, ts := range testStrings { + count := RuneCountInString(ts) + if n := runtimeRuneCount(ts); n != count { + assert(false) + //t.Errorf("%q: len([]rune()) counted %d runes; got %d from RuneCountInString", ts, n, count) + //break + } + + runes := []rune(ts) + if n := len(runes); n != count { + assert(false) + //t.Errorf("%q: []rune() has length %d; got %d from RuneCountInString", ts, n, count) + //break + } + i := 0 + for _, r := range ts { + if r != runes[i] { + assert(false) + //t.Errorf("%q[%d]: expected %c (%U); got %c (%U)", ts, i, runes[i], runes[i], r, r) + } + i++ + } + } + */ +} + +global invalidSequenceTests = []string{ + "\xed\xa0\x80\x80", // surrogate min + "\xed\xbf\xbf\x80", // surrogate max + + // xx + "\x91\x80\x80\x80", + + // s1 + "\xC2\x7F\x80\x80", + "\xC2\xC0\x80\x80", + "\xDF\x7F\x80\x80", + "\xDF\xC0\x80\x80", + + // s2 + "\xE0\x9F\xBF\x80", + "\xE0\xA0\x7F\x80", + "\xE0\xBF\xC0\x80", + "\xE0\xC0\x80\x80", + + // s3 + "\xE1\x7F\xBF\x80", + "\xE1\x80\x7F\x80", + "\xE1\xBF\xC0\x80", + "\xE1\xC0\x80\x80", + + //s4 + "\xED\x7F\xBF\x80", + "\xED\x80\x7F\x80", + "\xED\x9F\xC0\x80", + "\xED\xA0\x80\x80", + + // s5 + "\xF0\x8F\xBF\xBF", + "\xF0\x90\x7F\xBF", + "\xF0\x90\x80\x7F", + "\xF0\xBF\xBF\xC0", + "\xF0\xBF\xC0\x80", + "\xF0\xC0\x80\x80", + + // s6 + "\xF1\x7F\xBF\xBF", + "\xF1\x80\x7F\xBF", + "\xF1\x80\x80\x7F", + "\xF1\xBF\xBF\xC0", + "\xF1\xBF\xC0\x80", + "\xF1\xC0\x80\x80", + + // s7 + "\xF4\x7F\xBF\xBF", + "\xF4\x80\x7F\xBF", + "\xF4\x80\x80\x7F", + "\xF4\x8F\xBF\xC0", + "\xF4\x8F\xC0\x80", + "\xF4\x90\x80\x80", +} + + +func TestDecodeInvalidSequence { + for _, s := range invalidSequenceTests { + r1, _ := DecodeRune([]byte(s)) + if want := RuneError; r1 != want { + assert(false) + //t.Errorf("DecodeRune(%#x) = %#04x, want %#04x", s, r1, want) + //return + } + r2, _ := DecodeRuneInString(s) + if want := RuneError; r2 != want { + assert(false) + //t.Errorf("DecodeRuneInString(%q) = %#04x, want %#04x", s, r2, want) + //return + } + if r1 != r2 { + assert(false) + //t.Errorf("DecodeRune(%#x) = %#04x mismatch with DecodeRuneInString(%q) = %#04x", s, r1, s, r2) + //return + } + //r3 := runtimeDecodeRune(s) + //if r2 != r3 { + // t.Errorf("DecodeRuneInString(%q) = %#04x mismatch with runtime.decoderune(%q) = %#04x", s, r2, s, r3) + // return + //} + } +} + +func testSequence(s: string) { + /* + type info struct { + index: int + r: rune + } + index := make([]info, len(s)) + b := []byte(s) + si := 0 + j := 0 + for i, r := range s { + if si != i { + t.Errorf("Sequence(%q) mismatched index %d, want %d", s, si, i) + return + } + index[j] = info{i, r} + j++ + r1, size1 := DecodeRune(b[i:]) + if r != r1 { + t.Errorf("DecodeRune(%q) = %#04x, want %#04x", s[i:], r1, r) + return + } + r2, size2 := DecodeRuneInString(s[i:]) + if r != r2 { + t.Errorf("DecodeRuneInString(%q) = %#04x, want %#04x", s[i:], r2, r) + return + } + if size1 != size2 { + t.Errorf("DecodeRune/DecodeRuneInString(%q) size mismatch %d/%d", s[i:], size1, size2) + return + } + si += size1 + } + j-- + for si = len(s); si > 0; { + r1, size1 := DecodeLastRune(b[0:si]) + r2, size2 := DecodeLastRuneInString(s[0:si]) + if size1 != size2 { + t.Errorf("DecodeLastRune/DecodeLastRuneInString(%q, %d) size mismatch %d/%d", s, si, size1, size2) + return + } + if r1 != index[j].r { + t.Errorf("DecodeLastRune(%q, %d) = %#04x, want %#04x", s, si, r1, index[j].r) + return + } + if r2 != index[j].r { + t.Errorf("DecodeLastRuneInString(%q, %d) = %#04x, want %#04x", s, si, r2, index[j].r) + return + } + si -= size1 + if si != index[j].index { + t.Errorf("DecodeLastRune(%q) index mismatch at %d, want %d", s, si, index[j].index) + return + } + j-- + } + if si != 0 { + t.Errorf("DecodeLastRune(%q) finished at %d, not 0", s, si) + } + */ +} + +// Check that negative runes encode as U+FFFD. +func TestNegativeRune { + errorbuf := make([]byte, UTFMax) + errorbuf = errorbuf[0:EncodeRune(errorbuf, RuneError)] + buf := make([]byte, UTFMax) + buf = buf[0:EncodeRune(buf, -1)] + if !bytes_Equal(buf, errorbuf) { + assert(false) + //t.Errorf("incorrect encoding [% x] for -1; expected [% x]", buf, errorbuf) + } +} + +type RuneCountTest struct { + in: string + out: int +} + +global runecounttests = []RuneCountTest{ + {"abcd", 4}, + {"☺☻☹", 3}, + {"1,2,3,4", 7}, + {"\xe2\x00", 2}, + {"\xe2\x80", 2}, + {"a\xe2\x80", 3}, +} + +func TestRuneCount { + for _, tt := range runecounttests { + if out := RuneCountInString(tt.in); out != tt.out { + assert(false) + //t.Errorf("RuneCountInString(%q) = %d, want %d", tt.in, out, tt.out) + } + if out := RuneCount([]byte(tt.in)); out != tt.out { + assert(false) + //t.Errorf("RuneCount(%q) = %d, want %d", tt.in, out, tt.out) + } + } +} + +type RuneLenTest struct { + r: rune + size: int +} + +global runelentests = []RuneLenTest{ + {0, 1}, + {'e', 1}, + {'é', 2}, + {'☺', 3}, + {RuneError, 3}, + {MaxRune, 4}, + {0xD800, -1}, + {0xDFFF, -1}, + {MaxRune + 1, -1}, + {-1, -1}, +} + +func TestRuneLen { + for _, tt := range runelentests { + if size := RuneLen(tt.r); size != tt.size { + assert(false) + //t.Errorf("RuneLen(%#U) = %d, want %d", tt.r, size, tt.size) + } + } +} + +type ValidTest struct { + in: string + out: bool +} + +global validTests = []ValidTest{ + {"", true}, + {"a", true}, + {"abc", true}, + {"Ж", true}, + {"ЖЖ", true}, + {"брэд-ЛГТМ", true}, + {"☺☻☹", true}, + {"aa\xe2", false}, + {string([]byte{66, 250}), false}, + {string([]byte{66, 250, 67}), false}, + {"a\uFFFDb", true}, + {string("\xF4\x8F\xBF\xBF"), true}, // U+10FFFF + {string("\xF4\x90\x80\x80"), false}, // U+10FFFF+1; out of range + {string("\xF7\xBF\xBF\xBF"), false}, // 0x1FFFFF; out of range + {string("\xFB\xBF\xBF\xBF\xBF"), false}, // 0x3FFFFFF; out of range + {string("\xc0\x80"), false}, // U+0000 encoded in two bytes: incorrect + {string("\xed\xa0\x80"), false}, // U+D800 high surrogate (sic) + {string("\xed\xbf\xbf"), false}, // U+DFFF low surrogate (sic) +} + +func TestValid { + for _, tt := range validTests { + if Valid([]byte(tt.in)) != tt.out { + assert(false) + //t.Errorf("Valid(%q) = %v; want %v", tt.in, !tt.out, tt.out) + } + if ValidString(tt.in) != tt.out { + assert(false) + //t.Errorf("ValidString(%q) = %v; want %v", tt.in, !tt.out, tt.out) + } + } +} + + +type ValidRuneTest struct { + r: rune + ok: bool +} + +global validrunetests = []ValidRuneTest{ + {0, true}, + {'e', true}, + {'é', true}, + {'☺', true}, + {RuneError, true}, + {MaxRune, true}, + {0xD7FF, true}, + {0xD800, false}, + {0xDFFF, false}, + {0xE000, true}, + {MaxRune + 1, false}, + {-1, false}, +} + +func TestValidRune { + for _, tt := range validrunetests { + if ok := ValidRune(tt.r); ok != tt.ok { + assert(false) + //t.Errorf("ValidRune(%#U) = %t, want %t", tt.r, ok, tt.ok) + } + } +} + func bytes_Equal(a, b: []byte) => bool { // Neither cmd/compile nor gccgo allocates for these string conversions. return string(a) == string(b) From b257139578b77ac3b2c4193556cb82816cbec1e8 Mon Sep 17 00:00:00 2001 From: xxx Date: Mon, 28 Aug 2023 14:54:44 +0800 Subject: [PATCH 2/4] chore: add wa wasm zip --- internal/app/wawasm/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/app/wawasm/Makefile b/internal/app/wawasm/Makefile index e1a9561..1c2a26a 100644 --- a/internal/app/wawasm/Makefile +++ b/internal/app/wawasm/Makefile @@ -3,6 +3,7 @@ default: GOOS=js GOARCH=wasm go build -o wa.wasm mv wa.wasm ../../../docs + cd ../../../docs && zip wa.wasm.zip wa.wasm dev-chai: -rm wa.wasm From d4dd9898615e8348ba811f86f7f790d209f34164 Mon Sep 17 00:00:00 2001 From: trueabc <547551933@qq.com> Date: Tue, 29 Aug 2023 19:19:48 +0800 Subject: [PATCH 3/4] =?UTF-8?q?wa=E5=A4=A7=E6=95=B4=E6=95=B0=E8=BF=90?= =?UTF-8?q?=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- waroot/src/math/big/arith.wa | 208 +++++++++++++++++ waroot/src/math/big/binary.wa | 16 ++ waroot/src/math/big/int.wa | 254 ++++++++++++++++++++ waroot/src/math/big/int_test.wa | 93 ++++++++ waroot/src/math/big/nat.wa | 318 +++++++++++++++++++++++++ waroot/src/math/big/nat_test.wa | 144 ++++++++++++ waroot/src/math/big/natdiv.wa | 375 ++++++++++++++++++++++++++++++ waroot/src/math/bits/bits_test.wa | 2 +- 8 files changed, 1409 insertions(+), 1 deletion(-) create mode 100644 waroot/src/math/big/arith.wa create mode 100644 waroot/src/math/big/binary.wa create mode 100644 waroot/src/math/big/int.wa create mode 100644 waroot/src/math/big/int_test.wa create mode 100644 waroot/src/math/big/nat.wa create mode 100644 waroot/src/math/big/nat_test.wa create mode 100644 waroot/src/math/big/natdiv.wa diff --git a/waroot/src/math/big/arith.wa b/waroot/src/math/big/arith.wa new file mode 100644 index 0000000..cb4e38b --- /dev/null +++ b/waroot/src/math/big/arith.wa @@ -0,0 +1,208 @@ +// Author: TrueAbc +// Note: +// The implementation refers to the go language "math/big" lib. + +import "math/bits" + +func AAA { + println(bits.UintSize) +} + +const ( + _S = _W / 8 // 字宽(以字节为单位) + + _W = bits.UintSize // 字宽(以位为单位) + _B = 1 << _W // 基数 + _M = _B - 1 // 基数掩码 +) + +// z1<<_W + z0 = x*y +func mulWW(x, y: uint) => (z1, z0: uint) { + return bits.Mul(x, y) +} + +// z1<<_W + z0 = x*y + c +func mulAddWWW(x, y, c: uint) => (z1, z0: uint) { + hi, lo := bits.Mul(x, y) + cc: uint + lo, cc = bits.Add(lo, c, 0) + return hi + cc, lo +} + +// nlz returns the number of leading zeros in x. +// Wraps bits.LeadingZeros call for convenience. +func nlz(x: uint) => uint { + return uint(bits.LeadingZeros(x)) +} + +// The resulting carry c is either 0 or 1. +func addVV(z, x, y: []uint) => (c: uint) { + for i := 0; i < len(z) && i < len(x) && i < len(y); i++ { + zi, cc := bits.Add(x[i], y[i], c) + z[i] = zi + c = cc + } + return +} + +// The resulting carry c is either 0 or 1. +func subVV(z, x, y: []uint) => (c: uint) { + for i := 0; i < len(z) && i < len(x) && i < len(y); i++ { + zi, cc := bits.Sub(x[i], y[i], c) + z[i] = zi + c = cc + } + return +} + +// The resulting carry c is either 0 or 1. +func addVW(z, x: []uint, y: uint) => (c: uint) { + c = y + for i := 0; i < len(z) && i < len(x); i++ { + zi, cc := bits.Add(x[i], c, 0) + z[i] = zi + c = cc + } + return +} + +func subVW(z, x: []uint, y: uint) => (c: uint) { + c = y + for i := 0; i < len(z) && i < len(x); i++ { + zi, cc := bits.Sub(x[i], c, 0) + z[i] = zi + c = cc + } + return +} + +func shlVU(z, x: []uint, s: uint) => (c: uint) { + if s == 0 { + copy(z, x) + return + } + if len(z) == 0 { + return + } + s &= _W - 1 // hint to the compiler that shifts by s don't need guard code + ŝ := _W - s + ŝ &= _W - 1 // ditto + c = x[len(z)-1] >> ŝ + for i := len(z) - 1; i > 0; i-- { + z[i] = x[i]<>ŝ + } + z[0] = x[0] << s + return +} + +func shrVU(z, x: []uint, s: uint) => (c: uint) { + if s == 0 { + copy(z, x) + return + } + if len(z) == 0 { + return + } + if len(x) != len(z) { + // This is an invariant guaranteed by the caller. + panic("len(x) != len(z)") + } + s &= _W - 1 // hint to the compiler that shifts by s don't need guard code + ŝ := _W - s + ŝ &= _W - 1 // ditto + c = x[0] << ŝ + for i := 1; i < len(z); i++ { + z[i-1] = x[i-1]>>s | x[i]<<ŝ + } + z[len(z)-1] = x[len(z)-1] >> s + return +} + +func mulAddVWW(z, x: []uint, y, r: uint) => (c: uint) { + c = r + for i := 0; i < len(z) && i < len(x); i++ { + c, z[i] = mulAddWWW(x[i], y, c) + } + return +} + +func addMulVVW(z, x: []uint, y: uint) => (c: uint) { + for i := 0; i < len(z) && i < len(x); i++ { + z1, z0 := mulAddWWW(x[i], y, z[i]) + lo, cc := bits.Add(z0, c, 0) + c, z[i] = cc, lo + c += z1 + } + return +} + +// q = ( x1 << _W + x0 - r)/y. m = floor(( _B^2 - 1 ) / d - _B). Requiring x1 (q, r: uint) { + s := nlz(y) + if s != 0 { + x1 = x1<>(_W-s) + x0 <<= s + y <<= s + } + d := y + // We know that + // m = ⎣(B^2-1)/d⎦-B + // ⎣(B^2-1)/d⎦ = m+B + // (B^2-1)/d = m+B+delta1 0 <= delta1 <= (d-1)/d + // B^2/d = m+B+delta2 0 <= delta2 <= 1 + // The quotient we're trying to compute is + // quotient = ⎣(x1*B+x0)/d⎦ + // = ⎣(x1*B*(B^2/d)+x0*(B^2/d))/B^2⎦ + // = ⎣(x1*B*(m+B+delta2)+x0*(m+B+delta2))/B^2⎦ + // = ⎣(x1*m+x1*B+x0)/B + x0*m/B^2 + delta2*(x1*B+x0)/B^2⎦ + // The latter two terms of this three-term sum are between 0 and 1. + // So we can compute just the first term, and we will be low by at most 2. + t1, t0 := bits.Mul(m, x1) + _, c := bits.Add(t0, x0, 0) + t1, _ = bits.Add(t1, x1, c) + // The quotient is either t1, t1+1, or t1+2. + // We'll try t1 and adjust if needed. + qq := t1 + // compute remainder r=x-d*q. + dq1, dq0 := bits.Mul(d, qq) + r0, b := bits.Sub(x0, dq0, 0) + r1, _ := bits.Sub(x1, dq1, b) + // The remainder we just computed is bounded above by B+d: + // r = x1*B + x0 - d*q. + // = x1*B + x0 - d*⎣(x1*m+x1*B+x0)/B⎦ + // = x1*B + x0 - d*((x1*m+x1*B+x0)/B-alpha) 0 <= alpha < 1 + // = x1*B + x0 - x1*d/B*m - x1*d - x0*d/B + d*alpha + // = x1*B + x0 - x1*d/B*⎣(B^2-1)/d-B⎦ - x1*d - x0*d/B + d*alpha + // = x1*B + x0 - x1*d/B*⎣(B^2-1)/d-B⎦ - x1*d - x0*d/B + d*alpha + // = x1*B + x0 - x1*d/B*((B^2-1)/d-B-beta) - x1*d - x0*d/B + d*alpha 0 <= beta < 1 + // = x1*B + x0 - x1*B + x1/B + x1*d + x1*d/B*beta - x1*d - x0*d/B + d*alpha + // = x0 + x1/B + x1*d/B*beta - x0*d/B + d*alpha + // = x0*(1-d/B) + x1*(1+d*beta)/B + d*alpha + // < B*(1-d/B) + d*B/B + d because x00), x1= d { + qq++ + r0 -= d + } + return qq, r0 >> s +} + +// reciprocalWord return the reciprocal of the divisor. rec = floor(( _B^2 - 1 ) / u - _B). u = d1 << nlz(d1). +func reciprocalWord(d1: uint) => uint { + u := uint(d1 << nlz(d1)) + x1 := ^u + x0 := uint(_M) + rec, _ := bits.Div(x1, x0, u) // (_B^2-1)/U-_B = (_B*(_M-C)+_M)/U + return uint(rec) +} diff --git a/waroot/src/math/big/binary.wa b/waroot/src/math/big/binary.wa new file mode 100644 index 0000000..2d5e8ca --- /dev/null +++ b/waroot/src/math/big/binary.wa @@ -0,0 +1,16 @@ +// Author: TrueAbc +// Note: +// The implementation refers to the go language "math/big" lib. + +// 暂时把go的大端转换代码copy过来 + +func Buf2Uint64(b: []byte) => uint64 { + _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 + return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | + uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 +} + +func Buf2Uint32(b: []byte) => uint32 { + _ = b[3] // bounds check hint to compiler; see golang.org/issue/14808 + return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24 +} diff --git a/waroot/src/math/big/int.wa b/waroot/src/math/big/int.wa new file mode 100644 index 0000000..677fb05 --- /dev/null +++ b/waroot/src/math/big/int.wa @@ -0,0 +1,254 @@ +// Author: TrueAbc +// Note: +// The implementation refers to the go language "math/big" lib. + +type Int struct { + neg: bool // sign + abs: nat // absolute value of the integer +} + +var intOne = &Int{false, natOne} + +// 测试用 +func NewInt(x: string) => *Int { + t := new(Int) + neg := false + if x[0] == '-' { + neg = true + x = x[1:] + } + t.abs = natFromString(x) + t.neg = neg + + return t +} + +func NewIntWithuint(x: uint) => *Int { + t := new(Int) + t.abs = nat{[]uint{x}} + t.neg = false + return t +} + +// Sign returns: +// +// -1 if x < 0 +// 0 if x == 0 +// +1 if x > 0 +// +func Int.Sign() => int { + if len(this.abs.Data) == 0 { + return 0 + } + if this.neg { + return -1 + } + return 1 +} + +// Set sets z to x and returns z. +func Int.Set(x: *Int) => *Int { + if this != x { + this.abs = this.abs.set(x.abs) + this.neg = x.neg + } + return this +} + +// Bits provides raw (unchecked but fast) access to x by returning its +// absolute value as a little-endian Word slice. The result and x share +// the same underlying array. +// Bits is intended to support implementation of missing low-level Int +// functionality outside this package; it should be avoided otherwise. +func Int.Bits() => []uint { + return this.abs.Data +} + +// SetBits provides raw (unchecked but fast) access to z by setting its +// value to abs, interpreted as a little-endian Word slice, and returning +// z. The result and abs share the same underlying array. +// SetBits is intended to support implementation of missing low-level Int +// functionality outside this package; it should be avoided otherwise. +func Int.SetBits(abs: []uint) => *Int { + temp := &nat{abs} + this.abs = temp.norm() + this.neg = false + return this +} + +// Abs sets z to |x| (the absolute value of x) and returns z. +func Int.Abs(x: *Int) => *Int { + this.Set(x) + this.neg = false + return this +} + +// Neg sets z to -x and returns z. +func Int.Neg(x: *Int) => *Int { + this.Set(x) + this.neg = len(this.abs.Data) > 0 && !this.neg // 0 has no sign + return this +} + +// Add sets z to the sum x+y and returns z. +func Int.Add(x, y: *Int) => *Int { + neg := x.neg + if x.neg == y.neg { + // x + y == x + y + // (-x) + (-y) == -(x + y) + this.abs = this.abs.add(x.abs, y.abs) + } else { + // x + (-y) == x - y == -(y - x) + // (-x) + y == y - x == -(x - y) + if x.abs.cmp(y.abs) >= 0 { + this.abs = this.abs.sub(x.abs, y.abs) + } else { + neg = !neg + this.abs = this.abs.sub(y.abs, x.abs) + } + } + this.neg = len(this.abs.Data) > 0 && neg // 0 has no sign + return this +} + +// Sub sets z to the difference x-y and returns z. +func Int.Sub(x, y: *Int) => *Int { + neg := x.neg + if x.neg != y.neg { + // x - (-y) == x + y + // (-x) - y == -(x + y) + this.abs = this.abs.add(x.abs, y.abs) + } else { + // x - y == x - y == -(y - x) + // (-x) - (-y) == y - x == -(x - y) + if x.abs.cmp(y.abs) >= 0 { + this.abs = this.abs.sub(x.abs, y.abs) + } else { + neg = !neg + this.abs = this.abs.sub(y.abs, x.abs) + } + } + this.neg = len(this.abs.Data) > 0 && neg // 0 has no sign + return this +} + +// Mul sets z to the product x*y and returns z. +func Int.Mul(x, y: *Int) => *Int { + // x * y == x * y + // x * (-y) == -(x * y) + // (-x) * y == -(x * y) + // (-x) * (-y) == x * y + this.abs = this.abs.mul(x.abs, y.abs) + this.neg = len(this.abs.Data) > 0 && this.neg != this.neg // 0 has no sign + return this +} + +// QuoRem sets z to the quotient x/y and r to the remainder x%y +// and returns the pair (z, r) for y != 0. +// If y == 0, a division-by-zero run-time panic occurs. +// +// QuoRem implements T-division and modulus (like Go): +// +// q = x/y with the result truncated to zero +// r = x - y*q +// +// (See Daan Leijen, ``Division and Modulus for Computer Scientists''.) +// See DivMod for Euclidean division and modulus (unlike Go). +// +func Int.QuoRem(x, y, r: *Int) => (*Int, *Int) { + this.abs, r.abs = this.abs.div(r.abs, x.abs, y.abs) + this.neg, r.neg = len(this.abs.Data) > 0 && x.neg != y.neg, len(r.abs.Data) > 0 && x.neg // 0 has no sign + return this, r +} + +// DivMod sets z to the quotient x div y and m to the modulus x mod y +// and returns the pair (z, m) for y != 0. +// If y == 0, a division-by-zero run-time panic occurs. +// +// DivMod implements Euclidean division and modulus (unlike Go): +// +// q = x div y such that +// m = x - y*q with 0 <= m < |y| +// +// (See Raymond T. Boute, ``The Euclidean definition of the functions +// div and mod''. ACM Transactions on Programming Languages and +// Systems (TOPLAS), 14(2):127-144, New York, NY, USA, 4/1992. +// ACM press.) +// See QuoRem for T-division and modulus (like Go). +// +func Int.DivMod(x, y, m: *Int) => (*Int, *Int) { + y0 := y // save y + if this == y || alias(this.abs, y.abs) { + y0 = new(Int).Set(y) + } + this.QuoRem(x, y, m) + if m.neg { + if y0.neg { + this.Add(this, intOne) + m.Sub(m, y0) + } else { + this.Sub(this, intOne) + m.Add(m, y0) + } + } + return this, m +} + +// Cmp compares x and y and returns: +// +// -1 if x < y +// 0 if x == y +// +1 if x > y +// +func Int.Cmp(y: *Int) => (r: int) { + // x cmp y == x cmp y + // x cmp (-y) == x + // (-x) cmp y == y + // (-x) cmp (-y) == -(x cmp y) + switch { + case this == y: + // nothing to do + case this.neg == y.neg: + r = this.abs.cmp(y.abs) + if this.neg { + r = -r + } + case this.neg: + r = -1 + default: + r = 1 + } + return +} + +// CmpAbs compares the absolute values of x and y and returns: +// +// -1 if |x| < |y| +// 0 if |x| == |y| +// +1 if |x| > |y| +// +func Int.CmpAbs(y: *Int) => int { + return this.abs.cmp(y.abs) +} + +// SetBytes interprets buf as the bytes of a big-endian unsigned +// integer, sets z to that value, and returns z. +func Int.SetBytes(buf: []byte) => *Int { + this.abs = this.abs.setBytes(buf) + this.neg = false + return this +} + +// Bytes returns the absolute value of x as a big-endian byte slice. +// +// To use a fixed length slice, or a preallocated one, use FillBytes. +func Int.Bytes() => []byte { + buf := make([]byte, len(this.abs.Data)*_S) + return buf[this.abs.bytes(buf):] +} + +// BitLen returns the length of the absolute value of x in bits. +// The bit length of 0 is 0. +func Int.BitLen() => int { + return this.abs.bitLen() +} diff --git a/waroot/src/math/big/int_test.wa b/waroot/src/math/big/int_test.wa new file mode 100644 index 0000000..1ccc730 --- /dev/null +++ b/waroot/src/math/big/int_test.wa @@ -0,0 +1,93 @@ +// Author: TrueAbc +// Note: +// The implementation refers to the go language "math/big" lib. + +type funZZ func(z, x, y: *Int) => *Int +type argZZ struct { + z, x, y: *Int +} + +var sumZZ = []argZZ{ + {NewInt("0"), NewInt("0"), NewInt("0")}, + {NewInt("1"), NewInt("1"), NewInt("0")}, + {NewInt("1111111110"), NewInt("123456789"), NewInt("987654321")}, + {NewInt("-1"), NewInt("-1"), NewInt("0")}, + {NewInt("864197532"), NewInt("-123456789"), NewInt("987654321")}, + {NewInt("-1111111110"), NewInt("-123456789"), NewInt("-987654321")}, +} + +func Test_Int_Set() { + println("start test set") + for _, a := range sumZZ { + z: Int + z.Set(a.z) + if (&z).Cmp(a.z) != 0 { + println("error for Int Set") + } + } +} + +func testFunZZ(msg: string, f: funZZ, a: argZZ) { + z: Int + f(&z, a.x, a.y) + t := (&z).Cmp(a.z) + if t != 0 { + println(msg, " compute error ", t, ".") + } +} + +func Test_Int_Sum() { + println("start test int add and sub") + AddZZ := func(z, x, y: *Int) => *Int { return z.Add(x, y) } + SubZZ := func(z, x, y: *Int) => *Int { return z.Sub(x, y) } + + for _, a := range sumZZ { + arg := a + testFunZZ("AddZZ", AddZZ, arg) + + arg = argZZ{a.z, a.y, a.x} + testFunZZ("AddZZ symmetric", AddZZ, arg) + + arg = argZZ{a.x, a.z, a.y} + testFunZZ("SubZZ", SubZZ, arg) + + arg = argZZ{a.y, a.z, a.x} + testFunZZ("SubZZ symmetric", SubZZ, arg) + } +} + +var quoTests = []struct { + x, y: string + q, r: string +}{ + { + "476217953993950760840509444250624797097991362735329973741718102894495832294430498335824897858659711275234906400899559094370964723884706254265559534144986498357", + "9353930466774385905609975137998169297361893554149986716853295022578535724979483772383667534691121982974895531435241089241440253066816724367338287092081996", + "50911", + "1", + }, + { + "11510768301994997771168", + "1328165573307167369775", + "8", + "885443715537658812968", + }, +} + +func Test_Quo() { + println("start test int quo") + + for _, test := range quoTests { + x := NewInt(test.x) + y := NewInt(test.y) + expectedQ := NewInt(test.q) + expectedR := NewInt(test.r) + + r := NewInt("0") + q, r := NewInt("0").QuoRem(x, y, r) + + if q.Cmp(expectedQ) != 0 || r.Cmp(expectedR) != 0 { + println("error for Int quo") + } + } +} diff --git a/waroot/src/math/big/nat.wa b/waroot/src/math/big/nat.wa new file mode 100644 index 0000000..36816ce --- /dev/null +++ b/waroot/src/math/big/nat.wa @@ -0,0 +1,318 @@ +// Author: TrueAbc +// Note: +// The implementation refers to the go language "math/big" lib. + +import ( + "math/bits" +) + +type nat struct { + Data: []uint +} + +func natFromString(s: string) => nat { + temp := &nat{nil} + return temp.setString(s) +} + +// 这个方法是临时使用的, 用于部分内容的测试 +func nat.setString(s: string) => nat { + // 长度应该是超过的 + z := this.make((len(s) + _S - 1) / _S) + val: uint = 0 + for i := 0; i < len(s); i++ { + if s[i] < '0' || s[i] > '9' { + println("bad big integer:", s[i]) + } + val = uint(s[i] - '0') + z = z.mulAddWW(z, 10, val) + val = 0 + } + + return z.norm() +} + +func nat.clear() { + for i := range this.Data { + this.Data[i] = 0 + } +} + +var ( + natOne = nat{Data: []uint{1}} +) + +// 临时使用 +func nat.raw_print() { + print("raw_nat: {") + for i := range this.Data { + print(this.Data[i]) + if i != len(this.Data)-1 { + print(",") + } + } + println("}") +} + +func nat.norm() => nat { + i := len(this.Data) + for i > 0 && this.Data[i-1] == 0 { + i-- + } + return nat{Data: this.Data[0:i]} +} + +func nat.make(n: int) => nat { + if n <= cap(this.Data) { + return nat{Data: this.Data[:n]} // reuse this.Data + } + if n == 1 { + // Most nats start small and stay that way; don't over-allocate. + return nat{Data: make([]uint, 1)} + } + // Choosing a good value for e has significant performance impact + // because it increases the chance that a value can be reused. + const e = 4 // extra capacity + return nat{Data: make([]uint, n, n+e)} +} + +func nat.setWord(x: uint) => nat { + if x == 0 { + return nat{Data: this.Data[:0]} + } + z := this.make(1) + z.Data[0] = x + return z +} + +func nat.set(x: nat) => nat { + z := this.make(len(x.Data)) + copy(z.Data, x.Data) + + return z +} + +func nat.add(x, y: nat) => nat { + m := len(x.Data) + n := len(y.Data) + + switch { + case m < n: + return this.add(y, x) + case m == 0: + // n == 0 because m >= n; result is 0 + return nat{Data: this.Data[:0]} + case n == 0: + // result is x + return this.set(x) + } + // m > 0 + z := this.make(m + 1) + c := addVV(z.Data[0:n], x.Data, y.Data) + if m > n { + c = addVW(z.Data[n:m], x.Data[n:], c) + } + z.Data[m] = c + + return z.norm() +} + +func nat.sub(x, y: nat) => nat { + m := len(x.Data) + n := len(y.Data) + switch { + case m < n: + panic("underflow") + case m == 0: + // n == 0 because m >= n; result is 0 + return nat{Data: this.Data[:0]} + case n == 0: + // result is x + return this.set(x) + } + // m > 0 + z := this.make(m) + c := subVV(z.Data[0:n], x.Data, y.Data) + if m > n { + c = subVW(z.Data[n:], x.Data[n:], c) + } + if c != 0 { + panic("underflow") + } + return z.norm() +} + +func nat.cmp(y: nat) => int { + r := 0 + m := len(this.Data) + n := len(y.Data) + if m != n || m == 0 { + switch { + case m < n: + r = -1 + case m > n: + r = 1 + } + return r + } + i := m - 1 + for i > 0 && this.Data[i] == y.Data[i] { + i-- + } + switch { + case this.Data[i] < y.Data[i]: + r = -1 + case this.Data[i] > y.Data[i]: + r = 1 + } + return r +} + +func nat.mulAddWW(x: nat, y, r: uint) => nat { + m := len(x.Data) + if m == 0 || y == 0 { + return this.setWord(r) // result is r + } + // m > 0 + + z := this.make(m + 1) + z.Data[m] = mulAddVWW(z.Data[:m], x.Data, y, r) + return z.norm() +} + +// basicMul multiplies x and y and leaves the result in z. +// The (non-normalized) result is placed in z[0 : len(x) + len(y)]. +func basicMul(z, x, y: nat) { + // z.Data[:len(x.Data)+len(y.Data)].clear() + // initialize z + t := nat{Data: z.Data[:len(x.Data)+len(y.Data)]} + t.clear() + + for i, d := range y.Data { + if d != 0 { + z.Data[len(x.Data)+i] = addMulVVW(z.Data[i:i+len(x.Data)], x.Data, d) + } + } +} + +// alias reports whether x and y share the same base array. +// Note: alias assumes that the capacity of underlying arrays +// is never changed for nat values; i.e. that there are +// no 3-operand slice expressions in this code (or worse, +// reflect-based operations to the same effect). +func alias(x, y: nat) => bool { + x_cap := cap(x.Data) + y_cap := cap(y.Data) + return x_cap > 0 && y_cap > 0 && &x.Data[:x_cap][x_cap-1] == &y.Data[:y_cap][y_cap-1] +} + +// addAt implements z += x<<(_W*i); z must be long enough. +// (we don't use nat.add because we need z to stay the same +// slice, and we don't need to normalize z after each addition) +func addAt(z, x: nat, i: int) { + if n := len(x.Data); n > 0 { + if c := addVV(z.Data[i:i+n], z.Data[i:], x.Data); c != 0 { + j := i + n + if j < len(z.Data) { + addVW(z.Data[j:], z.Data[j:], c) + } + } + } +} + +func nat.mul(x, y: nat) => nat { + m := len(x.Data) + n := len(y.Data) + switch { + case m < n: + return this.mul(y, x) + case m == 0 || n == 0: + return nat{Data: this.Data[:0]} + case n == 1: + return this.mulAddWW(x, y.Data[0], 0) + } + // m >= n > 1 + + // determine if z can be reused + if alias(*this, x) || alias(*this, y) { + // 空指针操作 + this = nil // z is an alias for x or y - cannot reuse + } + + z := this.make(m + n) + basicMul(z, x, y) + return z.norm() +} + +func getNat(n: int) => *nat { + r := (&nat{}).make(n) + return &r +} + +func putNat(x: *nat) { + // panic("todo") +} + +func nat.bitLen() => int { + if i := len(this.Data) - 1; i >= 0 { + return i*_W + bits.Len(uint(this.Data[i])) + } + return 0 +} + +// bytes writes the value of z into buf using big-endian encoding. +// The value of z is encoded in the slice buf[i:]. If the value of z +// cannot be represented in buf, bytes panics. The number i of unused +// bytes at the beginning of buf is returned as result. +func nat.bytes(buf: []byte) => int { + i := len(buf) + for _, d := range this.Data { + for j := 0; j < _S; j++ { + i-- + if i >= 0 { + buf[i] = byte(d) + } else if byte(d) != 0 { + panic("math/big: buffer too small to fit value") + } + d >>= 8 + } + } + + if i < 0 { + i = 0 + } + for i < len(buf) && buf[i] == 0 { + i++ + } + return i +} + +// bigEndianWord returns the contents of buf interpreted as a big-endian encoded Word value. +func bigEndianWord(buf: []byte) => uint { + if _W == 64 { + return uint(Buf2Uint64(buf)) + } + return uint(Buf2Uint32(buf)) +} + +// setBytes interprets buf as the bytes of a big-endian unsigned +// integer, sets z to that value, and returns z. +func nat.setBytes(buf: []byte) => nat { + z := this.make((len(buf) + _S - 1) / _S) + i := len(buf) + for k := 0; i >= _S; k++ { + z.Data[k] = bigEndianWord(buf[i-_S : i]) + i -= _S + } + + if i > 0 { + d: uint = 0 + for s := uint(0); i > 0; s += 8 { + d |= uint(buf[i-1]) << s + i-- + } + z.Data[len(z.Data)-1] = d + } + + return z.norm() +} diff --git a/waroot/src/math/big/nat_test.wa b/waroot/src/math/big/nat_test.wa new file mode 100644 index 0000000..9cb15c8 --- /dev/null +++ b/waroot/src/math/big/nat_test.wa @@ -0,0 +1,144 @@ +// Author: TrueAbc +// Note: +// The implementation refers to the go language "math/big" lib. + +// test好像并不执行该文件夹, 改为导出方法放在外层执行 +func Test_Norm { + t := nat{Data: []uint{1, 0, 0, 0}} + t = t.norm() + if len(t.Data) == 1 && t.Data[0] == 1 { + println("NORM TEST PASS!") + } else { + println("NORM TEST FAILED!") + } +} + +func Test_setWord { + t := nat{Data: []uint{1, 2, 3, 4}} + z := t.setWord(0) + println("set zero:", len(z.Data)) + + z = t.setWord(10) + println("set to 10:", z.Data[0], " ", t.Data[0]) +} + +func Test_set { + t := nat{Data: []uint{1, 2, 3, 4}} + z := t.set(t) + println(len(z.Data) == len(t.Data)) + for i := 0; i < len(z.Data); i++ { + println("item:", z.Data[i], "===", t.Data[i]) + } +} + +// 参考go单元测试的内容 +type funNN func(z: nat, x: nat, y: nat) => nat +type argNN struct { + z: nat + x: nat + y: nat +} + +var sumNN = []argNN{ + {}, + {z: nat{[]uint{1}}, x: nat{nil}, y: nat{[]uint{1}}}, + {z: nat{[]uint{1111111110}}, x: nat{[]uint{123456789}}, y: nat{[]uint{987654321}}}, + {z: nat{[]uint{0, 0, 0, 1}}, x: nat{nil}, y: nat{[]uint{0, 0, 0, 1}}}, + {z: nat{[]uint{0, 0, 0, 1111111110}}, x: nat{[]uint{0, 0, 0, 123456789}}, y: nat{Data: []uint{0, 0, 0, 987654321}}}, + {z: nat{[]uint{0, 0, 0, 1}}, x: nat{[]uint{0, 0, _M}}, y: nat{[]uint{0, 0, 1}}}, +} + +var prodNN = []argNN{ + {}, + {z: nat{nil}, x: nat{nil}, y: nat{nil}}, + {z: nat{nil}, x: nat{[]uint{991}}, y: nat{nil}}, + {z: nat{[]uint{991}}, x: nat{[]uint{991}}, y: nat{[]uint{1}}}, + {z: nat{[]uint{991 * 991}}, x: nat{[]uint{991}}, y: nat{[]uint{991}}}, + {z: nat{[]uint{0, 0, 991 * 991}}, x: nat{[]uint{0, 991}}, y: nat{[]uint{0, 991}}}, + {z: nat{[]uint{1 * 991, 2 * 991, 3 * 991, 4 * 991}}, x: nat{[]uint{1, 2, 3, 4}}, y: nat{[]uint{991}}}, + {z: nat{[]uint{4, 11, 20, 30, 20, 11, 4}}, x: nat{[]uint{1, 2, 3, 4}}, y: nat{[]uint{4, 3, 2, 1}}}, + { + z: natFromString("11790184577738583171520872861412518665678211592275841109096961"), + x: natFromString("515377520732011331036461129765621272702107522001"), + y: natFromString("22876792454961"), + }, +} + +func testAdd(a: argNN) { + temp := &nat{Data: nil} + z := temp.add(a.x, a.y) + if z.cmp(a.z) != 0 { + println("Error for add result") + } +} + +func testSub(a: argNN) { + temp := &nat{Data: nil} + z := temp.sub(a.x, a.y) + if z.cmp(a.z) != 0 { + println("Error for sub result") + } +} + +func testMul(a: argNN) { + temp := &nat{Data: nil} + z := temp.mul(a.x, a.y) + if z.cmp(a.z) != 0 { + println("Error for mul result") + } +} + +func Test_add { + println("start test add") + for _, a := range sumNN { + testAdd(a) + } +} + +func Test_sub { + println("start test sub") + for _, a := range sumNN { + arg := argNN{a.x, a.z, a.y} + testSub(arg) + } +} + +func Test_mul { + println("start test mul") + for _, a := range prodNN { + arg := a + testMul(arg) + + arg = argNN{a.z, a.y, a.x} + testMul(arg) + } +} + +func Test_div { + u := natFromString("11790184577738583171520872861412518665678211592275841109096961") + v := natFromString("515377520732011331036461129765621272702107522001") + w := natFromString("22876792454961") // 商 + r := natFromString("0") // 余数 + + q := &nat{nil} + q2, r2 := q.div(nat{nil}, u, v) + + if q2.cmp(w) != 0 { + println("wrong div result") + } else { + println("ok div result") + } + if r2.cmp(r) != 0 { + println("wrong div remainder result") + } else { + println("ok div remainder result") + } + +} + +func Test_setString { + n := natFromString("22876792454961") + for _, i := range n.Data { + println(i) + } +} diff --git a/waroot/src/math/big/natdiv.wa b/waroot/src/math/big/natdiv.wa new file mode 100644 index 0000000..5190dba --- /dev/null +++ b/waroot/src/math/big/natdiv.wa @@ -0,0 +1,375 @@ +// Author: TrueAbc +// Note: +// The implementation refers to the go language "math/big" lib. + +import "math/bits" + +// div returns q, r such that q = ⌊u/v⌋ and r = u%v = u - q·v. +// It uses z and z2 as the storage for q and r. +func nat.div(z2, u, v: nat) => (q: nat, r: nat) { + if len(v.Data) == 0 { + panic("division by zero") + } + + if u.cmp(v) < 0 { + q.Data = this.Data[:0] + r = z2.set(u) + return + } + if len(v.Data) == 1 { + // Short division: long optimized for a single-word divisor. + // In that case, the 2-by-1 guess is all we need at each step. + r2: uint + q, r2 = this.divW(u, v.Data[0]) + r = z2.setWord(r2) + return + } + q, r = this.divLarge(z2, u, v) + return +} + +func nat.divW(x: nat, y: uint) => (q: nat, r: uint) { + m := len(x.Data) + switch { + case y == 0: + panic("division by zero") + case y == 1: + q = this.set(x) // result is x + return + case m == 0: + q.Data = this.Data[:0] // result is 0 + return + } + // m > 0 + z := this.make(m) + r = divWVW(z.Data, 0, x.Data, y) + q = z.norm() + return +} + +// divWVW overwrites z with ⌊x/y⌋, returning the remainder r. +// The caller must ensure that len(z) = len(x). +func divWVW(z: []uint, xn: uint, x: []uint, y: uint) => (r: uint) { + r = xn + if len(x) == 1 { + qq, rr := bits.Div(uint(r), uint(x[0]), uint(y)) + z[0] = uint(qq) + return uint(rr) + } + + rec := reciprocalWord(y) + for i := len(z) - 1; i >= 0; i-- { + z[i], r = divWW(r, x[i], y, rec) + } + return r +} + +// div returns q, r such that q = ⌊uIn/vIn⌋ and r = uIn%vIn = uIn - q·vIn. +// It uses z and u as the storage for q and r. +// The caller must ensure that len(vIn) ≥ 2 (use divW otherwise) +// and that len(uIn) ≥ len(vIn) (the answer is 0, uIn otherwise). +func nat.divLarge(u, uIn, vIn: nat) => (q: nat, r: nat) { + z := *this + n := len(vIn.Data) + m := len(uIn.Data) - n + // Scale the inputs so vIn's top bit is 1 (see “Scaling Inputs” above). + // vIn is treated as a read-only input (it may be in use by another + // goroutine), so we must make a copy. + shift := nlz(vIn.Data[n-1]) + vp := getNat(n) + v := *vp + shlVU(v.Data, vIn.Data, shift) + u = u.make(len(uIn.Data) + 1) + u.Data[len(uIn.Data)] = shlVU(u.Data[0:len(uIn.Data)], uIn.Data, shift) + + // The caller should not pass aliased z and u, since those are + // the two different outputs, but correct just in case. + if alias(z, u) { + z.Data = nil + } + q = z.make(m + 1) + + // Use basic or recursive long division depending on size. + if n < divRecursiveThreshold { + q.divBasic(u, v) + } else { + q.divRecursive(u, v) + } + putNat(vp) + + q = q.norm() + + // Undo scaling of remainder. + shrVU(u.Data, u.Data, shift) + r = u.norm() + + return q, r +} + +// divBasic implements long division as described above. +// It overwrites q with ⌊u/v⌋ and overwrites u with the remainder r. +// q must be large enough to hold ⌊u/v⌋. +func nat.divBasic(u, v: nat) { + n := len(v.Data) + m := len(u.Data) - n + + qhatvp := getNat(n + 1) + qhatv := *qhatvp + + // Set up for divWW below, precomputing reciprocal argument. + vn1 := v.Data[n-1] + rec := reciprocalWord(vn1) + + // Compute each digit of quotient. + for j := m; j >= 0; j-- { + // Compute the 2-by-1 guess q̂. + // The first iteration must invent a leading 0 for u. + qhat := uint(_M) + ujn: uint + if j+n < len(u.Data) { + ujn = u.Data[j+n] + } + + // ujn ≤ vn1, or else q̂ would be more than one digit. + // For ujn == vn1, we set q̂ to the max digit M above. + // Otherwise, we compute the 2-by-1 guess. + if ujn != vn1 { + rhat: uint + qhat, rhat = divWW(ujn, u.Data[j+n-1], vn1, rec) + + // Refine q̂ to a 3-by-2 guess. See “Refining Guesses” above. + vn2 := v.Data[n-2] + x1, x2 := mulWW(qhat, vn2) + ujn2 := u.Data[j+n-2] + for greaterThan(x1, x2, rhat, ujn2) { + qhat-- + prevRhat := rhat + rhat += vn1 + // If r̂ overflows, then + // r̂ u[j+n-2]v[n-1] is now definitely > x1 x2. + if rhat < prevRhat { + break + } + // TODO(rsc): No need for a full mulWW. + // x2 += vn2; if x2 overflows, x1++ + x1, x2 = mulWW(qhat, vn2) + } + } + + // Compute q̂·v. + qhatv.Data[n] = mulAddVWW(qhatv.Data[0:n], v.Data, qhat, 0) + qhl := len(qhatv.Data) + if j+qhl > len(u.Data) && qhatv.Data[n] == 0 { + qhl-- + } + + // Subtract q̂·v from the current section of u. + // If it underflows, q̂·v > u, which we fix up + // by decrementing q̂ and adding v back. + c := subVV(u.Data[j:j+qhl], u.Data[j:], qhatv.Data) + if c != 0 { + c := addVV(u.Data[j:j+n], u.Data[j:], v.Data) + // If n == qhl, the carry from subVV and the carry from addVV + // cancel out and don't affect u[j+n]. + if n < qhl { + u.Data[j+n] += c + } + qhat-- + } + + // Save quotient digit. + // Caller may know the top digit is zero and not leave room for it. + if j == m && m == len(this.Data) && qhat == 0 { + continue + } + this.Data[j] = qhat + } + + putNat(qhatvp) +} + +// greaterThan reports whether the two digit numbers x1 x2 > y1 y2. +// TODO(rsc): In contradiction to most of this file, x1 is the high +// digit and x2 is the low digit. This should be fixed. +func greaterThan(x1, x2, y1, y2: uint) => bool { + return x1 > y1 || x1 == y1 && x2 > y2 +} + +// divRecursiveThreshold is the number of divisor digits +// at which point divRecursive is faster than divBasic. +const divRecursiveThreshold = 100 + +// divRecursive implements recursive division as described above. +// It overwrites z with ⌊u/v⌋ and overwrites u with the remainder r. +// z must be large enough to hold ⌊u/v⌋. +// This function is just for allocating and freeing temporaries +// around divRecursiveStep, the real implementation. +func nat.divRecursive(u, v: nat) { + // Recursion depth is (much) less than 2 log₂(len(v)). + // Allocate a slice of temporaries to be reused across recursion, + // plus one extra temporary not live across the recursion. + recDepth := 2 * bits.Len(uint(len(v.Data))) + tmp := getNat(3 * len(v.Data)) + + temps := make([]*nat, recDepth) + + this.clear() + this.divRecursiveStep(u, v, 0, tmp, temps) + // Free temporaries. + for _, n := range temps { + if n != nil { + putNat(n) + } + } + putNat(tmp) +} + +// divRecursiveStep is the actual implementation of recursive division. +// It adds ⌊u/v⌋ to z and overwrites u with the remainder r. +// z must be large enough to hold ⌊u/v⌋. +// It uses temps[depth] (allocating if needed) as a temporary live across +// the recursive call. It also uses tmp, but not live across the recursion. +func nat.divRecursiveStep(u, v: nat, depth: int, tmp: *nat, temps: []*nat) { + // u is a subsection of the original and may have leading zeros. + // TODO(rsc): The v = v.norm() is useless and should be removed. + // We know (and require) that v's top digit is ≥ B/2. + u = u.norm() + v = v.norm() + if len(u.Data) == 0 { + this.clear() + return + } + + // Fall back to basic division if the problem is now small enough. + n := len(v.Data) + if n < divRecursiveThreshold { + this.divBasic(u, v) + return + } + + // Nothing to do if u is shorter than v (implies u < v). + m := len(u.Data) - n + if m < 0 { + return + } + // We consider B digits in a row as a single wide digit. + // (See “Recursive Division” above.) + // + // TODO(rsc): rename B to Wide, to avoid confusion with _B, + // which is something entirely different. + // TODO(rsc): Look into whether using ⌈n/2⌉ is better than ⌊n/2⌋. + B := n / 2 + + // Allocate a nat for qhat below. + if temps[depth] == nil { + temps[depth] = getNat(n) // TODO(rsc): Can be just B+1. + } else { + *temps[depth] = temps[depth].make(B + 1) + } + + // Compute each wide digit of the quotient. + // + // TODO(rsc): Change the loop to be + // for j := (m+B-1)/B*B; j > 0; j -= B { + // which will make the final step a regular step, letting us + // delete what amounts to an extra copy of the loop body below. + j := m + + for j > B { + // Divide u[j-B:j+n] (3 wide digits) by v (2 wide digits). + // First make the 2-by-1-wide-digit guess using a recursive call. + // Then extend the guess to the full 3-by-2 (see “Refining Guesses”). + // + // For the 2-by-1-wide-digit guess, instead of doing 2B-by-B-digit, + // we use a (2B+1)-by-(B+1) digit, which handles the possibility that + // the result has an extra leading 1 digit as well as guaranteeing + // that the computed q̂ will be off by at most 1 instead of 2. + + // s is the number of digits to drop from the 3B- and 2B-digit chunks. + // We drop B-1 to be left with 2B+1 and B+1. + s := (B - 1) + + // uu is the up-to-3B-digit section of u we are working on. + uu := nat{u.Data[j-B:]} + + // Compute the 2-by-1 guess q̂, leaving r̂ in uu[s:B+n]. + qhat := *temps[depth] + qhat.clear() + qhat.divRecursiveStep(nat{uu.Data[s : B+n]}, nat{v.Data[s:]}, depth+1, tmp, temps) + qhat = qhat.norm() + + // Extend to a 3-by-2 quotient and remainder. + // Because divRecursiveStep overwrote the top part of uu with + // the remainder r̂, the full uu already contains the equivalent + // of r̂·B + uₙ₋₂ from the “Refining Guesses” discussion. + // Subtracting q̂·vₙ₋₂ from it will compute the full-length remainder. + // If that subtraction underflows, q̂·v > u, which we fix up + // by decrementing q̂ and adding v back, same as in long division. + + // TODO(rsc): Instead of subtract and fix-up, this code is computing + // q̂·vₙ₋₂ and decrementing q̂ until that product is ≤ u. + // But we can do the subtraction directly, as in the comment above + // and in long division, because we know that q̂ is wrong by at most one. + qhatv := tmp.make(3 * n) + qhatv.clear() + qhatv = qhatv.mul(qhat, nat{v.Data[:s]}) + for i := 0; i < 2; i++ { + e := qhatv.cmp(uu.norm()) + if e <= 0 { + break + } + subVW(qhat.Data, qhat.Data, 1) + c := subVV(qhatv.Data[:s], qhatv.Data[:s], v.Data[:s]) + if len(qhatv.Data) > s { + subVW(qhatv.Data[s:], qhatv.Data[s:], c) + } + addAt(nat{uu.Data[s:]}, nat{v.Data[s:]}, 0) + } + if qhatv.cmp(uu.norm()) > 0 { + panic("impossible") + } + c := subVV(uu.Data[:len(qhatv.Data)], uu.Data[:len(qhatv.Data)], qhatv.Data) + if c > 0 { + subVW(uu.Data[len(qhatv.Data):], uu.Data[len(qhatv.Data):], c) + } + addAt(*this, qhat, j-B) + j -= B + } + + // TODO(rsc): Rewrite loop as described above and delete all this code. + + // Now u < (v< 0 { + subVW(qhat.Data, qhat.Data, 1) + c := subVV(qhatv.Data[:s], qhatv.Data[:s], v.Data[:s]) + if len(qhatv.Data) > s { + subVW(qhatv.Data[s:], qhatv.Data[s:], c) + } + addAt(nat{u.Data[s:]}, nat{v.Data[s:]}, 0) + } + } + if qhatv.cmp(u.norm()) > 0 { + panic("impossible") + } + c := subVV(u.Data[0:len(qhatv.Data)], u.Data[0:len(qhatv.Data)], qhatv.Data) + if c > 0 { + c = subVW(u.Data[len(qhatv.Data):], u.Data[len(qhatv.Data):], c) + } + if c > 0 { + panic("impossible") + } + // Done! + addAt(*this, qhat.norm(), 0) +} diff --git a/waroot/src/math/bits/bits_test.wa b/waroot/src/math/bits/bits_test.wa index f577807..d2eac20 100644 --- a/waroot/src/math/bits/bits_test.wa +++ b/waroot/src/math/bits/bits_test.wa @@ -384,7 +384,7 @@ func testReverse(x64, want64: u64) { func TestReverseBytes { for _, test := range []struct { - x, r uint64 + x, r: uint64 }{ {0, 0}, {0x01, 0x01 << 56}, From e556debf78446c1786d828f45b1111be63036710 Mon Sep 17 00:00:00 2001 From: chai2010 Date: Tue, 29 Aug 2023 21:55:53 +0800 Subject: [PATCH 4/4] =?UTF-8?q?math/big=20=E6=A0=87=E5=87=86=E5=BA=93?= =?UTF-8?q?=E7=94=9F=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- waroot/waroot.go | 1 + 1 file changed, 1 insertion(+) diff --git a/waroot/waroot.go b/waroot/waroot.go index a5df45d..ac687e8 100644 --- a/waroot/waroot.go +++ b/waroot/waroot.go @@ -118,6 +118,7 @@ var stdPkgs = []string{ "image/color/palette", "io", "math", + "math/big", "math/bits", "os", "reflect",