pax_global_header00006660000000000000000000000064135173641500014517gustar00rootroot0000000000000052 comment=6a916e37a237384e18eefa3270c09247db1ecf50 golang-github-remyoudompheng-bigfft-0.0~git20190728.0.6a916e3/000077500000000000000000000000001351736415000232765ustar00rootroot00000000000000golang-github-remyoudompheng-bigfft-0.0~git20190728.0.6a916e3/LICENSE000066400000000000000000000027071351736415000243110ustar00rootroot00000000000000Copyright (c) 2012 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. golang-github-remyoudompheng-bigfft-0.0~git20190728.0.6a916e3/README000066400000000000000000000032351351736415000241610ustar00rootroot00000000000000Benchmarking math/big vs. bigfft Number size old ns/op new ns/op delta 1kb 1599 1640 +2.56% 10kb 61533 62170 +1.04% 50kb 833693 831051 -0.32% 100kb 2567995 2693864 +4.90% 1Mb 105237800 28446400 -72.97% 5Mb 1272947000 168554600 -86.76% 10Mb 3834354000 405120200 -89.43% 20Mb 11514488000 845081600 -92.66% 50Mb 49199945000 2893950000 -94.12% 100Mb 147599836000 5921594000 -95.99% Benchmarking GMP vs bigfft Number size GMP ns/op Go ns/op delta 1kb 536 1500 +179.85% 10kb 26669 50777 +90.40% 50kb 252270 658534 +161.04% 100kb 686813 2127534 +209.77% 1Mb 12100000 22391830 +85.06% 5Mb 111731843 133550600 +19.53% 10Mb 212314000 318595800 +50.06% 20Mb 490196000 671512800 +36.99% 50Mb 1280000000 2451476000 +91.52% 100Mb 2673000000 5228991000 +95.62% Benchmarks were run on a Core 2 Quad Q8200 (2.33GHz). FFT is enabled when input numbers are over 200kbits. Scanning large decimal number from strings. (math/big [n^2 complexity] vs bigfft [n^1.6 complexity], Core i5-4590) Digits old ns/op new ns/op delta 1e3 9995 10876 +8.81% 1e4 175356 243806 +39.03% 1e5 9427422 6780545 -28.08% 1e6 1776707489 144867502 -91.85% 2e6 6865499995 346540778 -94.95% 5e6 42641034189 1069878799 -97.49% 10e6 151975273589 2693328580 -98.23% golang-github-remyoudompheng-bigfft-0.0~git20190728.0.6a916e3/arith_386.s000066400000000000000000000015761351736415000252020ustar00rootroot00000000000000// Trampolines to math/big assembly implementations. #include "textflag.h" // func addVV(z, x, y []Word) (c Word) TEXT ·addVV(SB),NOSPLIT,$0 JMP math∕big·addVV(SB) // func subVV(z, x, y []Word) (c Word) TEXT ·subVV(SB),NOSPLIT,$0 JMP math∕big·subVV(SB) // func addVW(z, x []Word, y Word) (c Word) TEXT ·addVW(SB),NOSPLIT,$0 JMP math∕big·addVW(SB) // func subVW(z, x []Word, y Word) (c Word) TEXT ·subVW(SB),NOSPLIT,$0 JMP math∕big·subVW(SB) // func shlVU(z, x []Word, s uint) (c Word) TEXT ·shlVU(SB),NOSPLIT,$0 JMP math∕big·shlVU(SB) // func shrVU(z, x []Word, s uint) (c Word) TEXT ·shrVU(SB),NOSPLIT,$0 JMP math∕big·shrVU(SB) // func mulAddVWW(z, x []Word, y, r Word) (c Word) TEXT ·mulAddVWW(SB),NOSPLIT,$0 JMP math∕big·mulAddVWW(SB) // func addMulVVW(z, x []Word, y Word) (c Word) TEXT ·addMulVVW(SB),NOSPLIT,$0 JMP math∕big·addMulVVW(SB) golang-github-remyoudompheng-bigfft-0.0~git20190728.0.6a916e3/arith_amd64.s000066400000000000000000000020161351736415000255630ustar00rootroot00000000000000// Trampolines to math/big assembly implementations. #include "textflag.h" // func addVV(z, x, y []Word) (c Word) TEXT ·addVV(SB),NOSPLIT,$0 JMP math∕big·addVV(SB) // func subVV(z, x, y []Word) (c Word) // (same as addVV except for SBBQ instead of ADCQ and label names) TEXT ·subVV(SB),NOSPLIT,$0 JMP math∕big·subVV(SB) // func addVW(z, x []Word, y Word) (c Word) TEXT ·addVW(SB),NOSPLIT,$0 JMP math∕big·addVW(SB) // func subVW(z, x []Word, y Word) (c Word) // (same as addVW except for SUBQ/SBBQ instead of ADDQ/ADCQ and label names) TEXT ·subVW(SB),NOSPLIT,$0 JMP math∕big·subVW(SB) // func shlVU(z, x []Word, s uint) (c Word) TEXT ·shlVU(SB),NOSPLIT,$0 JMP math∕big·shlVU(SB) // func shrVU(z, x []Word, s uint) (c Word) TEXT ·shrVU(SB),NOSPLIT,$0 JMP math∕big·shrVU(SB) // func mulAddVWW(z, x []Word, y, r Word) (c Word) TEXT ·mulAddVWW(SB),NOSPLIT,$0 JMP math∕big·mulAddVWW(SB) // func addMulVVW(z, x []Word, y Word) (c Word) TEXT ·addMulVVW(SB),NOSPLIT,$0 JMP math∕big·addMulVVW(SB) golang-github-remyoudompheng-bigfft-0.0~git20190728.0.6a916e3/arith_arm.s000066400000000000000000000015561351736415000254370ustar00rootroot00000000000000// Trampolines to math/big assembly implementations. #include "textflag.h" // func addVV(z, x, y []Word) (c Word) TEXT ·addVV(SB),NOSPLIT,$0 B math∕big·addVV(SB) // func subVV(z, x, y []Word) (c Word) TEXT ·subVV(SB),NOSPLIT,$0 B math∕big·subVV(SB) // func addVW(z, x []Word, y Word) (c Word) TEXT ·addVW(SB),NOSPLIT,$0 B math∕big·addVW(SB) // func subVW(z, x []Word, y Word) (c Word) TEXT ·subVW(SB),NOSPLIT,$0 B math∕big·subVW(SB) // func shlVU(z, x []Word, s uint) (c Word) TEXT ·shlVU(SB),NOSPLIT,$0 B math∕big·shlVU(SB) // func shrVU(z, x []Word, s uint) (c Word) TEXT ·shrVU(SB),NOSPLIT,$0 B math∕big·shrVU(SB) // func mulAddVWW(z, x []Word, y, r Word) (c Word) TEXT ·mulAddVWW(SB),NOSPLIT,$0 B math∕big·mulAddVWW(SB) // func addMulVVW(z, x []Word, y Word) (c Word) TEXT ·addMulVVW(SB),NOSPLIT,$0 B math∕big·addMulVVW(SB) golang-github-remyoudompheng-bigfft-0.0~git20190728.0.6a916e3/arith_arm64.s000066400000000000000000000015561351736415000256110ustar00rootroot00000000000000// Trampolines to math/big assembly implementations. #include "textflag.h" // func addVV(z, x, y []Word) (c Word) TEXT ·addVV(SB),NOSPLIT,$0 B math∕big·addVV(SB) // func subVV(z, x, y []Word) (c Word) TEXT ·subVV(SB),NOSPLIT,$0 B math∕big·subVV(SB) // func addVW(z, x []Word, y Word) (c Word) TEXT ·addVW(SB),NOSPLIT,$0 B math∕big·addVW(SB) // func subVW(z, x []Word, y Word) (c Word) TEXT ·subVW(SB),NOSPLIT,$0 B math∕big·subVW(SB) // func shlVU(z, x []Word, s uint) (c Word) TEXT ·shlVU(SB),NOSPLIT,$0 B math∕big·shlVU(SB) // func shrVU(z, x []Word, s uint) (c Word) TEXT ·shrVU(SB),NOSPLIT,$0 B math∕big·shrVU(SB) // func mulAddVWW(z, x []Word, y, r Word) (c Word) TEXT ·mulAddVWW(SB),NOSPLIT,$0 B math∕big·mulAddVWW(SB) // func addMulVVW(z, x []Word, y Word) (c Word) TEXT ·addMulVVW(SB),NOSPLIT,$0 B math∕big·addMulVVW(SB) golang-github-remyoudompheng-bigfft-0.0~git20190728.0.6a916e3/arith_decl.go000066400000000000000000000010101351736415000257130ustar00rootroot00000000000000// Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package bigfft import . "math/big" // implemented in arith_$GOARCH.s func addVV(z, x, y []Word) (c Word) func subVV(z, x, y []Word) (c Word) func addVW(z, x []Word, y Word) (c Word) func subVW(z, x []Word, y Word) (c Word) func shlVU(z, x []Word, s uint) (c Word) func mulAddVWW(z, x []Word, y, r Word) (c Word) func addMulVVW(z, x []Word, y Word) (c Word) golang-github-remyoudompheng-bigfft-0.0~git20190728.0.6a916e3/arith_mips64x.s000066400000000000000000000020511351736415000261610ustar00rootroot00000000000000// Trampolines to math/big assembly implementations. // +build mips64 mips64le #include "textflag.h" // func addVV(z, x, y []Word) (c Word) TEXT ·addVV(SB),NOSPLIT,$0 JMP math∕big·addVV(SB) // func subVV(z, x, y []Word) (c Word) // (same as addVV except for SBBQ instead of ADCQ and label names) TEXT ·subVV(SB),NOSPLIT,$0 JMP math∕big·subVV(SB) // func addVW(z, x []Word, y Word) (c Word) TEXT ·addVW(SB),NOSPLIT,$0 JMP math∕big·addVW(SB) // func subVW(z, x []Word, y Word) (c Word) // (same as addVW except for SUBQ/SBBQ instead of ADDQ/ADCQ and label names) TEXT ·subVW(SB),NOSPLIT,$0 JMP math∕big·subVW(SB) // func shlVU(z, x []Word, s uint) (c Word) TEXT ·shlVU(SB),NOSPLIT,$0 JMP math∕big·shlVU(SB) // func shrVU(z, x []Word, s uint) (c Word) TEXT ·shrVU(SB),NOSPLIT,$0 JMP math∕big·shrVU(SB) // func mulAddVWW(z, x []Word, y, r Word) (c Word) TEXT ·mulAddVWW(SB),NOSPLIT,$0 JMP math∕big·mulAddVWW(SB) // func addMulVVW(z, x []Word, y Word) (c Word) TEXT ·addMulVVW(SB),NOSPLIT,$0 JMP math∕big·addMulVVW(SB) golang-github-remyoudompheng-bigfft-0.0~git20190728.0.6a916e3/arith_mipsx.s000066400000000000000000000020451351736415000260120ustar00rootroot00000000000000// Trampolines to math/big assembly implementations. // +build mips mipsle #include "textflag.h" // func addVV(z, x, y []Word) (c Word) TEXT ·addVV(SB),NOSPLIT,$0 JMP math∕big·addVV(SB) // func subVV(z, x, y []Word) (c Word) // (same as addVV except for SBBQ instead of ADCQ and label names) TEXT ·subVV(SB),NOSPLIT,$0 JMP math∕big·subVV(SB) // func addVW(z, x []Word, y Word) (c Word) TEXT ·addVW(SB),NOSPLIT,$0 JMP math∕big·addVW(SB) // func subVW(z, x []Word, y Word) (c Word) // (same as addVW except for SUBQ/SBBQ instead of ADDQ/ADCQ and label names) TEXT ·subVW(SB),NOSPLIT,$0 JMP math∕big·subVW(SB) // func shlVU(z, x []Word, s uint) (c Word) TEXT ·shlVU(SB),NOSPLIT,$0 JMP math∕big·shlVU(SB) // func shrVU(z, x []Word, s uint) (c Word) TEXT ·shrVU(SB),NOSPLIT,$0 JMP math∕big·shrVU(SB) // func mulAddVWW(z, x []Word, y, r Word) (c Word) TEXT ·mulAddVWW(SB),NOSPLIT,$0 JMP math∕big·mulAddVWW(SB) // func addMulVVW(z, x []Word, y Word) (c Word) TEXT ·addMulVVW(SB),NOSPLIT,$0 JMP math∕big·addMulVVW(SB) golang-github-remyoudompheng-bigfft-0.0~git20190728.0.6a916e3/arith_ppc64x.s000066400000000000000000000016171351736415000260020ustar00rootroot00000000000000// Trampolines to math/big assembly implementations. // +build ppc64 ppc64le #include "textflag.h" // func addVV(z, x, y []Word) (c Word) TEXT ·addVV(SB),NOSPLIT,$0 BR math∕big·addVV(SB) // func subVV(z, x, y []Word) (c Word) TEXT ·subVV(SB),NOSPLIT,$0 BR math∕big·subVV(SB) // func addVW(z, x []Word, y Word) (c Word) TEXT ·addVW(SB),NOSPLIT,$0 BR math∕big·addVW(SB) // func subVW(z, x []Word, y Word) (c Word) TEXT ·subVW(SB),NOSPLIT,$0 BR math∕big·subVW(SB) // func shlVU(z, x []Word, s uint) (c Word) TEXT ·shlVU(SB),NOSPLIT,$0 BR math∕big·shlVU(SB) // func shrVU(z, x []Word, s uint) (c Word) TEXT ·shrVU(SB),NOSPLIT,$0 BR math∕big·shrVU(SB) // func mulAddVWW(z, x []Word, y, r Word) (c Word) TEXT ·mulAddVWW(SB),NOSPLIT,$0 BR math∕big·mulAddVWW(SB) // func addMulVVW(z, x []Word, y Word) (c Word) TEXT ·addMulVVW(SB),NOSPLIT,$0 BR math∕big·addMulVVW(SB) golang-github-remyoudompheng-bigfft-0.0~git20190728.0.6a916e3/benchmarks/000077500000000000000000000000001351736415000254135ustar00rootroot00000000000000golang-github-remyoudompheng-bigfft-0.0~git20190728.0.6a916e3/benchmarks/bench000066400000000000000000000015561351736415000264240ustar00rootroot00000000000000# Benchmark of combined FFT and math/big (using threshold) # Run on a Core i5-4590 (Go 1.8) BenchmarkMul_1kb 3000000 525 ns/op BenchmarkMul_10kb 100000 19274 ns/op BenchmarkMul_50kb 5000 213375 ns/op BenchmarkMul_100kb 2000 651794 ns/op BenchmarkMul_1Mb 200 8546244 ns/op BenchmarkMul_5Mb 30 49127283 ns/op BenchmarkMul_10Mb 10 109888838 ns/op BenchmarkMul_20Mb 5 227088971 ns/op BenchmarkMul_50Mb 2 731298339 ns/op BenchmarkMul_100Mb 1 1480340166 ns/op BenchmarkMul_1x5Mb 50 28872973 ns/op BenchmarkMul_1x10Mb 20 58841416 ns/op BenchmarkMul_1x20Mb 10 124189252 ns/op BenchmarkMul_1x50Mb 3 349402586 ns/op BenchmarkMul_5x20Mb 10 153528843 ns/op BenchmarkMul_5x50Mb 3 348753322 ns/op golang-github-remyoudompheng-bigfft-0.0~git20190728.0.6a916e3/benchmarks/bench.big000066400000000000000000000015001351736415000271510ustar00rootroot00000000000000# Benchmarks run on a Core i5-4590 (Go 1.8) BenchmarkMul_1kb 5000000 298 ns/op BenchmarkMul_10kb 100000 14562 ns/op BenchmarkMul_50kb 10000 204698 ns/op BenchmarkMul_100kb 2000 636230 ns/op BenchmarkMul_1Mb 50 25950594 ns/op BenchmarkMul_5Mb 5 314799939 ns/op BenchmarkMul_10Mb 2 943065686 ns/op BenchmarkMul_20Mb 1 2837283743 ns/op BenchmarkMul_50Mb 1 14329431306 ns/op BenchmarkMul_100Mb 1 42590328264 ns/op BenchmarkMul_1x5Mb 10 126106007 ns/op BenchmarkMul_1x10Mb 5 248876061 ns/op BenchmarkMul_1x20Mb 3 492849546 ns/op BenchmarkMul_1x50Mb 1 1249673962 ns/op BenchmarkMul_5x20Mb 1 1261943492 ns/op BenchmarkMul_5x50Mb 1 3098019651 ns/op golang-github-remyoudompheng-bigfft-0.0~git20190728.0.6a916e3/benchmarks/bench.fft000066400000000000000000000015401351736415000271730ustar00rootroot00000000000000# Benchmarks using the mulFFT function only. # Run on a Core i5-4590 (Go 1.8) BenchmarkMul_1kb 200000 9737 ns/op BenchmarkMul_10kb 10000 105408 ns/op BenchmarkMul_50kb 3000 584090 ns/op BenchmarkMul_100kb 2000 973130 ns/op BenchmarkMul_1Mb 200 8622463 ns/op BenchmarkMul_5Mb 30 48602728 ns/op BenchmarkMul_10Mb 10 109184721 ns/op BenchmarkMul_20Mb 5 227053895 ns/op BenchmarkMul_50Mb 2 727421044 ns/op BenchmarkMul_100Mb 1 1550029484 ns/op BenchmarkMul_1x5Mb 50 28827150 ns/op BenchmarkMul_1x10Mb 20 58097775 ns/op BenchmarkMul_1x20Mb 10 124998246 ns/op BenchmarkMul_1x50Mb 3 350045770 ns/op BenchmarkMul_5x20Mb 10 160220847 ns/op BenchmarkMul_5x50Mb 3 350824154 ns/op golang-github-remyoudompheng-bigfft-0.0~git20190728.0.6a916e3/benchmarks/bench.gmp000066400000000000000000000016751351736415000272100ustar00rootroot00000000000000# These benchamrks were realised using gmpbench at # http://gmplib.org/gmpbench.html # and converted to the go test output format. # Numbers are for a Core i5-4590 with GMP 6.1.2 BenchmarkMul_1kb 47143107 175 ns/op BenchmarkMul_10kb 1321291 7573 ns/op BenchmarkMul_50kb 125645 79693 ns/op BenchmarkMul_100kb 47298 211500 ns/op BenchmarkMul_1Mb 2950 3344500 ns/op BenchmarkMul_5Mb 413 23920000 ns/op BenchmarkMul_10Mb 164 60606000 ns/op BenchmarkMul_20Mb 78 127700000 ns/op BenchmarkMul_50Mb 8 352100000 ns/op BenchmarkMul_100Mb 4 746270000 ns/op BenchmarkMul_1x5Mb 884 11670000 ns/op BenchmarkMul_1x10Mb 337 27174000 ns/op BenchmarkMul_1x20Mb 195 52630000 ns/op BenchmarkMul_1x50Mb 70 131000000 ns/op BenchmarkMul_5x20Mb 134 74188000 ns/op BenchmarkMul_5x50Mb 49 207770000 ns/op golang-github-remyoudompheng-bigfft-0.0~git20190728.0.6a916e3/benchmarks/scan.big000066400000000000000000000011621351736415000270220ustar00rootroot00000000000000Benchmarks on a Core i5-4590 BenchmarkScan1k-4 200000 9995 ns/op 32 B/op 1 allocs/op BenchmarkScan10k-4 10000 175356 ns/op 57 B/op 1 allocs/op BenchmarkScan100k-4 200 9427422 ns/op 117499 B/op 6 allocs/op BenchmarkScan1M-4 1 1776707489 ns/op 2197961776 B/op 10386 allocs/op BenchmarkScan2M-4 1 6865499995 ns/op 8708998320 B/op 20774 allocs/op BenchmarkScan5M-4 1 42641034189 ns/op 54105679664 B/op 51925 allocs/op BenchmarkScan10M-4 1 151975273589 ns/op 215978795792 B/op 103837 allocs/op golang-github-remyoudompheng-bigfft-0.0~git20190728.0.6a916e3/benchmarks/scan.bigfft000066400000000000000000000011371351736415000275240ustar00rootroot00000000000000Benchmarks on a Core i5-4590 BenchmarkScan1k-4 200000 10876 ns/op 2784 B/op 13 allocs/op BenchmarkScan10k-4 5000 243806 ns/op 86796 B/op 64 allocs/op BenchmarkScan100k-4 200 6780545 ns/op 1733425 B/op 332 allocs/op BenchmarkScan1M-4 10 144867502 ns/op 41509963 B/op 3130 allocs/op BenchmarkScan2M-4 3 346540778 ns/op 94912754 B/op 6213 allocs/op BenchmarkScan5M-4 1 1069878799 ns/op 278606280 B/op 15444 allocs/op BenchmarkScan10M-4 1 2693328580 ns/op 625284488 B/op 30842 allocs/op golang-github-remyoudompheng-bigfft-0.0~git20190728.0.6a916e3/calibrate_test.go000066400000000000000000000071571351736415000266240ustar00rootroot00000000000000// Usage: go test -run=TestCalibrate -calibrate package bigfft import ( "flag" "fmt" "testing" "time" ) var calibrate = flag.Bool("calibrate", false, "run calibration test") // measureMul benchmarks math/big versus FFT for a given input size // (in bits). func measureMul(th int) (tBig, tFFT time.Duration) { bigLoad := func(b *testing.B) { benchmarkMulBig(b, th, th) } fftLoad := func(b *testing.B) { benchmarkMulFFT(b, th, th) } res1 := testing.Benchmark(bigLoad) res2 := testing.Benchmark(fftLoad) tBig = time.Duration(res1.NsPerOp()) tFFT = time.Duration(res2.NsPerOp()) return } func roundDur(d time.Duration) time.Duration { if d > 100*time.Millisecond { return d / time.Millisecond * time.Millisecond } else { return d / time.Microsecond * time.Microsecond } } func TestCalibrateThreshold(t *testing.T) { if !*calibrate { t.Log("not calibrating, use -calibrate to do so.") return } lower := int(1e3) // math/big is faster at this size. upper := int(300e3) // FFT is faster at this size. var sizes [9]int var speedups [9]float64 for i := 0; i < 3; i++ { for idx := 1; idx <= 9; idx++ { sz := ((10-idx)*lower + idx*upper) / 10 big, fft := measureMul(sz) spd := float64(big) / float64(fft) sizes[idx-1] = sz speedups[idx-1] = spd fmt.Printf("speedup of FFT over math/big at size %d bits: %.2f (%s vs %s)\n", sz, spd, roundDur(big), roundDur(fft)) } narrow := false for idx, s := range speedups { if s < .98 { lower = sizes[idx] narrow = true } else { break } } for idx := range speedups { if speedups[8-idx] > 1.02 { upper = sizes[8-idx] narrow = true } else { break } } if lower >= upper { panic("impossible") } if !narrow || (upper-lower) <= 10 { break } } fmt.Printf("sizes: %d\n", sizes) fmt.Printf("speedups: %.2f\n", speedups) } func measureFFTSize(w int, k uint) time.Duration { load := func(b *testing.B) { x := rndNat(w) y := rndNat(w) for i := 0; i < b.N; i++ { m := (w+w)>>k + 1 xp := polyFromNat(x, k, m) yp := polyFromNat(y, k, m) rp := xp.Mul(&yp) _ = rp.Int() } } res := testing.Benchmark(load) return time.Duration(res.NsPerOp()) } func TestCalibrateFFT(t *testing.T) { if !*calibrate { t.Log("not calibrating, use -calibrate to do so.") return } lows := [...]int{10, 10, 10, 10, 20, 50, 100, 200, 500, // 8 1000, 2000, 5000, 10000, // 12 20000, 50000, 100e3, 200e3, // 16 } his := [...]int{100, 100, 100, 200, 500, 1000, 2000, 5000, 10000, // 8 50e3, 100e3, 200e3, 800e3, // 12 2e6, 5e6, 10e6, 20e6, // 16 } for k := uint(3); k <= 16; k++ { // Measure the speedup between k and k+1 low := lows[k] // FFT of size 1< 1.02 { hi = sizes[8-idx] narrow = true } else { break } } if low >= hi { panic("impossible") } if !narrow || (hi-low) <= 10 { break } } fmt.Printf("sizes: %d\n", sizes) fmt.Printf("speedups: %.2f\n", speedups) } } golang-github-remyoudompheng-bigfft-0.0~git20190728.0.6a916e3/fermat.go000066400000000000000000000077611351736415000251160ustar00rootroot00000000000000package bigfft import ( "math/big" ) // Arithmetic modulo 2^n+1. // A fermat of length w+1 represents a number modulo 2^(w*_W) + 1. The last // word is zero or one. A number has at most two representatives satisfying the // 0-1 last word constraint. type fermat nat func (n fermat) String() string { return nat(n).String() } func (z fermat) norm() { n := len(z) - 1 c := z[n] if c == 0 { return } if z[0] >= c { z[n] = 0 z[0] -= c return } // z[0] < z[n]. subVW(z, z, c) // Substract c if c > 1 { z[n] -= c - 1 c = 1 } // Add back c. if z[n] == 1 { z[n] = 0 return } else { addVW(z, z, 1) } } // Shift computes (x << k) mod (2^n+1). func (z fermat) Shift(x fermat, k int) { if len(z) != len(x) { panic("len(z) != len(x) in Shift") } n := len(x) - 1 // Shift by n*_W is taking the opposite. k %= 2 * n * _W if k < 0 { k += 2 * n * _W } neg := false if k >= n*_W { k -= n * _W neg = true } kw, kb := k/_W, k%_W z[n] = 1 // Add (-1) if !neg { for i := 0; i < kw; i++ { z[i] = 0 } // Shift left by kw words. // x = a·2^(n-k) + b // x< 0 { z[kw+1] -= b } else { subVW(z[kw+1:], z[kw+1:], b) } } else { for i := kw + 1; i < n; i++ { z[i] = 0 } // Shift left and negate, by kw words. copy(z[:kw+1], x[n-kw:n+1]) // z_low = x_high b := subVV(z[kw:n], z[kw:n], x[:n-kw]) // z_high -= x_low z[n] -= b } // Add back 1. if z[n] > 0 { z[n]-- } else if z[0] < ^big.Word(0) { z[0]++ } else { addVW(z, z, 1) } // Shift left by kb bits shlVU(z, z, uint(kb)) z.norm() } // ShiftHalf shifts x by k/2 bits the left. Shifting by 1/2 bit // is multiplication by sqrt(2) mod 2^n+1 which is 2^(3n/4) - 2^(n/4). // A temporary buffer must be provided in tmp. func (z fermat) ShiftHalf(x fermat, k int, tmp fermat) { n := len(z) - 1 if k%2 == 0 { z.Shift(x, k/2) return } u := (k - 1) / 2 a := u + (3*_W/4)*n b := u + (_W/4)*n z.Shift(x, a) tmp.Shift(x, b) z.Sub(z, tmp) } // Add computes addition mod 2^n+1. func (z fermat) Add(x, y fermat) fermat { if len(z) != len(x) { panic("Add: len(z) != len(x)") } addVV(z, x, y) // there cannot be a carry here. z.norm() return z } // Sub computes substraction mod 2^n+1. func (z fermat) Sub(x, y fermat) fermat { if len(z) != len(x) { panic("Add: len(z) != len(x)") } n := len(y) - 1 b := subVV(z[:n], x[:n], y[:n]) b += y[n] // If b > 0, we need to subtract b< 2*n+1 { panic("len(z) > 2n+1") } // We now have // z = z[:n] + 1<<(n*W) * z[n:2n+1] // which normalizes to: // z = z[:n] - z[n:2n] + z[2n] c1 := big.Word(0) if len(z) > 2*n { c1 = addVW(z[:n], z[:n], z[2*n]) } c2 := big.Word(0) if len(z) >= 2*n { c2 = subVV(z[:n], z[:n], z[n:2*n]) } else { m := len(z) - n c2 = subVV(z[:m], z[:m], z[n:]) c2 = subVW(z[m:n], z[m:n], c2) } // Restore carries. // Substracting z[n] -= c2 is the same // as z[0] += c2 z = z[:n+1] z[n] = c1 c := addVW(z, z, c2) if c != 0 { panic("impossible") } z.norm() return z } // copied from math/big // // basicMul multiplies x and y and leaves the result in z. // The (non-normalized) result is placed in z[0 : len(x) + len(y)]. func basicMul(z, x, y fermat) { // initialize z for i := 0; i < len(z); i++ { z[i] = 0 } for i, d := range y { if d != 0 { z[len(x)+i] = addMulVVW(z[i:i+len(x)], x, d) } } } golang-github-remyoudompheng-bigfft-0.0~git20190728.0.6a916e3/fermat_test.go000066400000000000000000000116161351736415000261470ustar00rootroot00000000000000package bigfft import ( "fmt" . "math/big" "testing" ) // parseHex reads an hex-formatted number modulo 2^bits+1. func parseHex(s string, bits int) fermat { z := new(Int) z, ok := z.SetString(s, 0) if !ok { panic(s) } f := fermat(z.Bits()) for len(f)*_W <= bits { f = append(f, 0) } return f } func compare(t *testing.T, prefix string, a, b fermat) { var x, y Int x.SetBits(a) y.SetBits(b) if x.Cmp(&y) != 0 { t.Errorf("%s: %x != %x", prefix, &x, &y) } } func TestFermatShift(t *testing.T) { const n = 4 f := make(fermat, n+1) for i := 0; i < n; i++ { f[i] = Word(rnd.Int63()) } b := NewInt(1) b = b.Lsh(b, uint(n*_W)) b = b.Add(b, NewInt(1)) z := make(fermat, len(f)) // Test with uninitialized z. for shift := -2048; shift < 2048; shift++ { z.Shift(f, shift) z2 := new(Int) z2.SetBits(f) if shift < 0 { s2 := (-shift) % (2 * n * _W) z2 = z2.Lsh(z2, uint(2*n*_W-s2)) } else { z2 = z2.Lsh(z2, uint(shift)) } z2 = z2.Mod(z2, b) compare(t, fmt.Sprintf("shift %d", shift), z, z2.Bits()) } } func TestFermatShiftHalf(t *testing.T) { const n = 3 f := make(fermat, n+1) for i := 0; i < n; i++ { f[i] = ^Word(0) } b := NewInt(1) b = b.Lsh(b, uint(n*_W)) b = b.Add(b, NewInt(1)) z := make(fermat, len(f)) // Test with uninitialized z. tmp := make(fermat, len(f)) tmp2 := make(fermat, len(f)) for shift := 0; shift < 16384; shift++ { // Shift twice by shift/2 z.ShiftHalf(f, shift, tmp) copy(tmp, z) z.ShiftHalf(tmp, shift, tmp2) z2 := new(Int) z2 = z2.Lsh(new(Int).SetBits(f), uint(shift)) z2 = z2.Mod(z2, b) compare(t, fmt.Sprintf("shift %d", shift), z, z2.Bits()) } } type test struct{ a, b, c fermat } // addTests is a series of mod 2^256+1 tests. var addTests = []test{ { parseHex("0x5555555555555555555555555555555555555555555555555555555555555555", 256), parseHex("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab", 256), parseHex("0x10000000000000000000000000000000000000000000000000000000000000000", 256), }, { parseHex("0x5555555555555555555555555555555555555555555555555555555555555555", 256), parseHex("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 256), parseHex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 256), }, { parseHex("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 256), parseHex("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 256), parseHex("0x5555555555555555555555555555555555555555555555555555555555555553", 256), }, } func TestFermatAdd(t *testing.T) { for i, item := range addTests { z := make(fermat, len(item.a)) z = z.Add(item.a, item.b) compare(t, fmt.Sprintf("addTests[%d]", i), z, item.c) } } var mulTests = []test{ { // 3^400 = 3^200 * 3^200 parseHex("0xc21a937a76f3432ffd73d97e447606b683ecf6f6e4a7ae223c2578e26c486a03", 256), parseHex("0xc21a937a76f3432ffd73d97e447606b683ecf6f6e4a7ae223c2578e26c486a03", 256), parseHex("0x0e65f4d3508036eaca8faa2b8194ace009c863e44bdc040c459a7127bf8bcc62", 256), }, { // 2^256 * 2^256 mod (2^256+1) = 1. parseHex("0x10000000000000000000000000000000000000000000000000000000000000000", 256), parseHex("0x10000000000000000000000000000000000000000000000000000000000000000", 256), parseHex("0x1", 256), }, { // (2^256-1) * (2^256-1) mod (2^256+1) = 4. parseHex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 256), parseHex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 256), parseHex("0x4", 256), }, { // 1<<(64W) * 1<<(64W) mod (1<<64W+1) = 1 fermat{64: 1}, fermat{64: 1}, fermat{0: 1}, }, { // Test case from issue 1. One of the squares of the Fourier // transforms was miscomputed. // The input number is made of 18 words, but we are working modulo 2^1280+1 parseHex("0xfffffffffffffffffffffffeffffffffffffffffffffffffffffffffffff00000000000000000000000100000000000000000000000000000000000000000000000000000000fffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff000100000000000000000000000100000000000000000000000000000000fffefffffffffffffffffffd", 1280), parseHex("0xfffffffffffffffffffffffeffffffffffffffffffffffffffffffffffff00000000000000000000000100000000000000000000000000000000000000000000000000000000fffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff000100000000000000000000000100000000000000000000000000000000fffefffffffffffffffffffd", 1280), parseHex("0xfffe00000003fffc0000000000000000fff40003000000000000000000060001fffffffd0001fffffffffffffffe000dfffbfffffffffffffffffffafffe0000000200000000000000000002fff60002fffffffffffffffa00060001ffffffff0000000000000000fffc0007fffe0000000000000007fff8fffdfffffffffffffffffffa00000004fffa0000fffffffffffffff600080000000000000000000a", 1280), }, } func TestFermatMul(t *testing.T) { for i, item := range mulTests { z := make(fermat, 3*len(item.a)) z = z.Mul(item.a, item.b) compare(t, fmt.Sprintf("mulTests[%d]", i), z, item.c) } } golang-github-remyoudompheng-bigfft-0.0~git20190728.0.6a916e3/fft.go000066400000000000000000000233501351736415000244070ustar00rootroot00000000000000// Package bigfft implements multiplication of big.Int using FFT. // // The implementation is based on the Schönhage-Strassen method // using integer FFT modulo 2^n+1. package bigfft import ( "math/big" "unsafe" ) const _W = int(unsafe.Sizeof(big.Word(0)) * 8) type nat []big.Word func (n nat) String() string { v := new(big.Int) v.SetBits(n) return v.String() } // fftThreshold is the size (in words) above which FFT is used over // Karatsuba from math/big. // // TestCalibrate seems to indicate a threshold of 60kbits on 32-bit // arches and 110kbits on 64-bit arches. var fftThreshold = 1800 // Mul computes the product x*y and returns z. // It can be used instead of the Mul method of // *big.Int from math/big package. func Mul(x, y *big.Int) *big.Int { xwords := len(x.Bits()) ywords := len(y.Bits()) if xwords > fftThreshold && ywords > fftThreshold { return mulFFT(x, y) } return new(big.Int).Mul(x, y) } func mulFFT(x, y *big.Int) *big.Int { var xb, yb nat = x.Bits(), y.Bits() zb := fftmul(xb, yb) z := new(big.Int) z.SetBits(zb) if x.Sign()*y.Sign() < 0 { z.Neg(z) } return z } // A FFT size of K=1< bits { k = uint(i) break } } // The 1< words m = words>>k + 1 return } // valueSize returns the length (in words) to use for polynomial // coefficients, to compute a correct product of polynomials P*Q // where deg(P*Q) < K (== 1<= 2*m*W+K n := 2*m*_W + int(k) // necessary bits K := 1 << (k - extra) if K < _W { K = _W } n = ((n / K) + 1) * K // round to a multiple of K return n / _W } // poly represents an integer via a polynomial in Z[x]/(x^K+1) // where K is the FFT length and b^m is the computation basis 1<<(m*_W). // If P = a[0] + a[1] x + ... a[n] x^(K-1), the associated natural number // is P(b^m). type poly struct { k uint // k is such that K = 1< 0 { length += len(p.a[na-1]) } n := make(nat, length) m := p.m np := n for i := range p.a { l := len(p.a[i]) c := addVV(np[:l], np[:l], p.a[i]) if np[l] < ^big.Word(0) { np[l] += c } else { addVW(np[l:], np[l:], c) } np = np[m:] } n = trim(n) return n } func trim(n nat) nat { for i := range n { if n[len(n)-1-i] != 0 { return n[:len(n)-i] } } return nil } // Mul multiplies p and q modulo X^K-1, where K = 1<= 1<= 1<> k // p(x) = a_0 + a_1 x + ... + a_{K-1} x^(K-1) // p(θx) = q(x) where // q(x) = a_0 + θa_1 x + ... + θ^(K-1) a_{K-1} x^(K-1) // // Twist p by θ to obtain q. tbits := make([]big.Word, (n+1)<> k // Perform an inverse Fourier transform to recover q. qbits := make([]big.Word, (n+1)<> size if backward { ω2shift = -ω2shift } // Easy cases. if len(src[0]) != n+1 || len(dst[0]) != n+1 { panic("len(src[0]) != n+1 || len(dst[0]) != n+1") } switch size { case 0: copy(dst[0], src[0]) return case 1: dst[0].Add(src[0], src[1< FFT size %d, chunk size = %d, value size = %d", s, 1< 3*m { t.Errorf("FFT word size %d >> input word size %d", v, m) } } } func testFourier(t *testing.T, N int, k uint) { // Random coefficients src := make([]fermat, 1<> k if inverse { ωshift = -ωshift } dst1 := make([]fermat, 1< 64 { t.Log(s[:64]) s = s[64:] } t.Log(s) } func benchmarkMulBig(b *testing.B, sizex, sizey int) { mulx := rndNat(sizex / _W) muly := rndNat(sizey / _W) b.ResetTimer() var x, y, z Int x.SetBits(mulx) y.SetBits(muly) for i := 0; i < b.N; i++ { z.Mul(&x, &y) } } func benchmarkMulFFT(b *testing.B, sizex, sizey int) { mulx := rndNat(sizex / _W) muly := rndNat(sizey / _W) b.ResetTimer() var x, y Int x.SetBits(mulx) y.SetBits(muly) for i := 0; i < b.N; i++ { _ = mulFFT(&x, &y) } } func BenchmarkMulBig_1kb(b *testing.B) { benchmarkMulBig(b, 1e3, 1e3) } func BenchmarkMulBig_10kb(b *testing.B) { benchmarkMulBig(b, 1e4, 1e4) } func BenchmarkMulBig_50kb(b *testing.B) { benchmarkMulBig(b, 5e4, 5e4) } func BenchmarkMulBig_100kb(b *testing.B) { benchmarkMulBig(b, 1e5, 1e5) } func BenchmarkMulBig_200kb(b *testing.B) { benchmarkMulBig(b, 2e5, 2e5) } func BenchmarkMulBig_500kb(b *testing.B) { benchmarkMulBig(b, 5e5, 5e5) } func BenchmarkMulBig_1Mb(b *testing.B) { benchmarkMulBig(b, 1e6, 1e6) } func BenchmarkMulBig_2Mb(b *testing.B) { benchmarkMulBig(b, 2e6, 2e6) } func BenchmarkMulBig_5Mb(b *testing.B) { benchmarkMulBig(b, 5e6, 5e6) } func BenchmarkMulBig_10Mb(b *testing.B) { benchmarkMulBig(b, 10e6, 10e6) } func BenchmarkMulBig_20Mb(b *testing.B) { benchmarkMulBig(b, 20e6, 20e6) } func BenchmarkMulBig_50Mb(b *testing.B) { benchmarkMulBig(b, 50e6, 50e6) } func BenchmarkMulBig_100Mb(b *testing.B) { benchmarkMulBig(b, 100e6, 100e6) } func BenchmarkMulFFT_1kb(b *testing.B) { benchmarkMulFFT(b, 1e3, 1e3) } func BenchmarkMulFFT_10kb(b *testing.B) { benchmarkMulFFT(b, 1e4, 1e4) } func BenchmarkMulFFT_50kb(b *testing.B) { benchmarkMulFFT(b, 5e4, 5e4) } func BenchmarkMulFFT_100kb(b *testing.B) { benchmarkMulFFT(b, 1e5, 1e5) } func BenchmarkMulFFT_200kb(b *testing.B) { benchmarkMulFFT(b, 2e5, 2e5) } func BenchmarkMulFFT_500kb(b *testing.B) { benchmarkMulFFT(b, 5e5, 5e5) } func BenchmarkMulFFT_1Mb(b *testing.B) { benchmarkMulFFT(b, 1e6, 1e6) } func BenchmarkMulFFT_2Mb(b *testing.B) { benchmarkMulFFT(b, 2e6, 2e6) } func BenchmarkMulFFT_5Mb(b *testing.B) { benchmarkMulFFT(b, 5e6, 5e6) } func BenchmarkMulFFT_10Mb(b *testing.B) { benchmarkMulFFT(b, 10e6, 10e6) } func BenchmarkMulFFT_20Mb(b *testing.B) { benchmarkMulFFT(b, 20e6, 20e6) } func BenchmarkMulFFT_50Mb(b *testing.B) { benchmarkMulFFT(b, 50e6, 50e6) } func BenchmarkMulFFT_100Mb(b *testing.B) { benchmarkMulFFT(b, 100e6, 100e6) } func BenchmarkMulFFT_200Mb(b *testing.B) { benchmarkMulFFT(b, 200e6, 200e6) } func BenchmarkMulFFT_500Mb(b *testing.B) { benchmarkMulFFT(b, 500e6, 500e6) } func BenchmarkMulFFT_1Gb(b *testing.B) { benchmarkMulFFT(b, 1e9, 1e9) } func benchmarkMul(b *testing.B, sizex, sizey int) { mulx := rndNat(sizex / _W) muly := rndNat(sizey / _W) b.ResetTimer() for i := 0; i < b.N; i++ { var x, y Int x.SetBits(mulx) y.SetBits(muly) _ = Mul(&x, &y) } } func BenchmarkMul_50kb(b *testing.B) { benchmarkMul(b, 5e4, 5e4) } func BenchmarkMul_100kb(b *testing.B) { benchmarkMul(b, 1e5, 1e5) } func BenchmarkMul_200kb(b *testing.B) { benchmarkMul(b, 2e5, 2e5) } func BenchmarkMul_500kb(b *testing.B) { benchmarkMul(b, 5e5, 5e5) } func BenchmarkMul_1Mb(b *testing.B) { benchmarkMul(b, 1e6, 1e6) } func BenchmarkMul_2Mb(b *testing.B) { benchmarkMul(b, 2e6, 2e6) } func BenchmarkMul_5Mb(b *testing.B) { benchmarkMul(b, 5e6, 5e6) } func BenchmarkMul_10Mb(b *testing.B) { benchmarkMul(b, 10e6, 10e6) } func BenchmarkMul_20Mb(b *testing.B) { benchmarkMul(b, 20e6, 20e6) } func BenchmarkMul_50Mb(b *testing.B) { benchmarkMul(b, 50e6, 50e6) } func BenchmarkMul_100Mb(b *testing.B) { benchmarkMul(b, 100e6, 100e6) } // Unbalanced multiplication benchmarks func BenchmarkMul_1x5Mb(b *testing.B) { benchmarkMul(b, 1e6, 5e6) } func BenchmarkMul_1x10Mb(b *testing.B) { benchmarkMul(b, 1e6, 10e6) } func BenchmarkMul_1x20Mb(b *testing.B) { benchmarkMul(b, 1e6, 20e6) } func BenchmarkMul_1x50Mb(b *testing.B) { benchmarkMul(b, 1e6, 50e6) } func BenchmarkMul_5x20Mb(b *testing.B) { benchmarkMul(b, 5e6, 20e6) } func BenchmarkMul_5x50Mb(b *testing.B) { benchmarkMul(b, 5e6, 50e6) } func BenchmarkMulBig_1x5Mb(b *testing.B) { benchmarkMulBig(b, 1e6, 5e6) } func BenchmarkMulBig_1x10Mb(b *testing.B) { benchmarkMulBig(b, 1e6, 10e6) } func BenchmarkMulBig_1x20Mb(b *testing.B) { benchmarkMulBig(b, 1e6, 20e6) } func BenchmarkMulBig_1x50Mb(b *testing.B) { benchmarkMulBig(b, 1e6, 50e6) } func BenchmarkMulBig_5x20Mb(b *testing.B) { benchmarkMulBig(b, 5e6, 20e6) } func BenchmarkMulBig_5x50Mb(b *testing.B) { benchmarkMulBig(b, 5e6, 50e6) } func BenchmarkMulFFT_1x5Mb(b *testing.B) { benchmarkMulFFT(b, 1e6, 5e6) } func BenchmarkMulFFT_1x10Mb(b *testing.B) { benchmarkMulFFT(b, 1e6, 10e6) } func BenchmarkMulFFT_1x20Mb(b *testing.B) { benchmarkMulFFT(b, 1e6, 20e6) } func BenchmarkMulFFT_1x50Mb(b *testing.B) { benchmarkMulFFT(b, 1e6, 50e6) } func BenchmarkMulFFT_5x20Mb(b *testing.B) { benchmarkMulFFT(b, 5e6, 20e6) } func BenchmarkMulFFT_5x50Mb(b *testing.B) { benchmarkMulFFT(b, 5e6, 50e6) } func TestIssue1(t *testing.T) { e := NewInt(1) e.SetBit(e, 132048, 1) e.Sub(e, NewInt(4)) // e == 1<<132048 - 4 g := NewInt(0).Set(e) e.Mul(e, e) g = Mul(g, g) if g.Cmp(e) != 0 { t.Fatal("incorrect Mul result") } } golang-github-remyoudompheng-bigfft-0.0~git20190728.0.6a916e3/go.mod000066400000000000000000000000611351736415000244010ustar00rootroot00000000000000module github.com/remyoudompheng/bigfft go 1.12 golang-github-remyoudompheng-bigfft-0.0~git20190728.0.6a916e3/scan.go000066400000000000000000000032311351736415000245500ustar00rootroot00000000000000package bigfft import ( "math/big" ) // FromDecimalString converts the base 10 string // representation of a natural (non-negative) number // into a *big.Int. // Its asymptotic complexity is less than quadratic. func FromDecimalString(s string) *big.Int { var sc scanner z := new(big.Int) sc.scan(z, s) return z } type scanner struct { // powers[i] is 10^(2^i * quadraticScanThreshold). powers []*big.Int } func (s *scanner) chunkSize(size int) (int, *big.Int) { if size <= quadraticScanThreshold { panic("size < quadraticScanThreshold") } pow := uint(0) for n := size; n > quadraticScanThreshold; n /= 2 { pow++ } // threshold * 2^(pow-1) <= size < threshold * 2^pow return quadraticScanThreshold << (pow - 1), s.power(pow - 1) } func (s *scanner) power(k uint) *big.Int { for i := len(s.powers); i <= int(k); i++ { z := new(big.Int) if i == 0 { if quadraticScanThreshold%14 != 0 { panic("quadraticScanThreshold % 14 != 0") } z.Exp(big.NewInt(1e14), big.NewInt(quadraticScanThreshold/14), nil) } else { z.Mul(s.powers[i-1], s.powers[i-1]) } s.powers = append(s.powers, z) } return s.powers[k] } func (s *scanner) scan(z *big.Int, str string) { if len(str) <= quadraticScanThreshold { z.SetString(str, 10) return } sz, pow := s.chunkSize(len(str)) // Scan the left half. s.scan(z, str[:len(str)-sz]) // FIXME: reuse temporaries. left := Mul(z, pow) // Scan the right half s.scan(z, str[len(str)-sz:]) z.Add(z, left) } // quadraticScanThreshold is the number of digits // below which big.Int.SetString is more efficient // than subquadratic algorithms. // 1232 digits fit in 4096 bits. const quadraticScanThreshold = 1232 golang-github-remyoudompheng-bigfft-0.0~git20190728.0.6a916e3/scan_test.go000066400000000000000000000035531351736415000256160ustar00rootroot00000000000000package bigfft import ( "math/big" "testing" "time" ) func TestScan(t *testing.T) { for size := 10; size <= 1e5; size += 191 { s := rndStr(size) x, ok := new(big.Int).SetString(s, 10) if !ok { t.Fatal("cannot parse", s) } t0 := time.Now() y := FromDecimalString(s) if x.Cmp(y) != 0 { t.Errorf("failed at size %d", size) } else { t.Logf("OK for size %d in %s", size, time.Since(t0)) } } } func BenchmarkScanFast1k(b *testing.B) { benchmarkScanFast(1e3, b) } func BenchmarkScanFast10k(b *testing.B) { benchmarkScanFast(10e3, b) } func BenchmarkScanFast100k(b *testing.B) { benchmarkScanFast(100e3, b) } func BenchmarkScanFast1M(b *testing.B) { benchmarkScanFast(1e6, b) } func BenchmarkScanFast2M(b *testing.B) { benchmarkScanFast(2e6, b) } func BenchmarkScanFast5M(b *testing.B) { benchmarkScanFast(5e6, b) } func BenchmarkScanFast10M(b *testing.B) { benchmarkScanFast(10e6, b) } //func BenchmarkScanFast100M(b *testing.B) { benchmarkScanFast(100e6, b) } func benchmarkScanFast(n int, b *testing.B) { s := rndStr(n) var x *big.Int for i := 0; i < b.N; i++ { x = FromDecimalString(s) } _ = x } func BenchmarkScanBig1k(b *testing.B) { benchmarkScanBig(1e3, b) } func BenchmarkScanBig10k(b *testing.B) { benchmarkScanBig(10e3, b) } func BenchmarkScanBig100k(b *testing.B) { benchmarkScanBig(100e3, b) } func BenchmarkScanBig1M(b *testing.B) { benchmarkScanBig(1e6, b) } func BenchmarkScanBig2M(b *testing.B) { benchmarkScanBig(2e6, b) } func BenchmarkScanBig5M(b *testing.B) { benchmarkScanBig(5e6, b) } func BenchmarkScanBig10M(b *testing.B) { benchmarkScanBig(10e6, b) } func benchmarkScanBig(n int, b *testing.B) { s := rndStr(n) var x big.Int for i := 0; i < b.N; i++ { x.SetString(s, 10) } } func rndStr(n int) string { x := make([]byte, n) for i := 0; i < n; i++ { x[i] = '0' + byte(rnd.Intn(10)) } return string(x) }