...

Source file src/math/big/arith_test.go

Documentation: math/big

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package big
     6  
     7  import (
     8  	"fmt"
     9  	"internal/testenv"
    10  	"math/bits"
    11  	"math/rand"
    12  	"strings"
    13  	"testing"
    14  )
    15  
    16  var isRaceBuilder = strings.HasSuffix(testenv.Builder(), "-race")
    17  
    18  type funVV func(z, x, y []Word) (c Word)
    19  type argVV struct {
    20  	z, x, y nat
    21  	c       Word
    22  }
    23  
    24  var sumVV = []argVV{
    25  	{},
    26  	{nat{0}, nat{0}, nat{0}, 0},
    27  	{nat{1}, nat{1}, nat{0}, 0},
    28  	{nat{0}, nat{_M}, nat{1}, 1},
    29  	{nat{80235}, nat{12345}, nat{67890}, 0},
    30  	{nat{_M - 1}, nat{_M}, nat{_M}, 1},
    31  	{nat{0, 0, 0, 0}, nat{_M, _M, _M, _M}, nat{1, 0, 0, 0}, 1},
    32  	{nat{0, 0, 0, _M}, nat{_M, _M, _M, _M - 1}, nat{1, 0, 0, 0}, 0},
    33  	{nat{0, 0, 0, 0}, nat{_M, 0, _M, 0}, nat{1, _M, 0, _M}, 1},
    34  }
    35  
    36  func testFunVV(t *testing.T, msg string, f funVV, a argVV) {
    37  	z := make(nat, len(a.z))
    38  	c := f(z, a.x, a.y)
    39  	for i, zi := range z {
    40  		if zi != a.z[i] {
    41  			t.Errorf("%s%+v\n\tgot z[%d] = %#x; want %#x", msg, a, i, zi, a.z[i])
    42  			break
    43  		}
    44  	}
    45  	if c != a.c {
    46  		t.Errorf("%s%+v\n\tgot c = %#x; want %#x", msg, a, c, a.c)
    47  	}
    48  }
    49  
    50  func TestFunVV(t *testing.T) {
    51  	for _, a := range sumVV {
    52  		arg := a
    53  		testFunVV(t, "addVV_g", addVV_g, arg)
    54  		testFunVV(t, "addVV", addVV, arg)
    55  
    56  		arg = argVV{a.z, a.y, a.x, a.c}
    57  		testFunVV(t, "addVV_g symmetric", addVV_g, arg)
    58  		testFunVV(t, "addVV symmetric", addVV, arg)
    59  
    60  		arg = argVV{a.x, a.z, a.y, a.c}
    61  		testFunVV(t, "subVV_g", subVV_g, arg)
    62  		testFunVV(t, "subVV", subVV, arg)
    63  
    64  		arg = argVV{a.y, a.z, a.x, a.c}
    65  		testFunVV(t, "subVV_g symmetric", subVV_g, arg)
    66  		testFunVV(t, "subVV symmetric", subVV, arg)
    67  	}
    68  }
    69  
    70  // Always the same seed for reproducible results.
    71  var rnd = rand.New(rand.NewSource(0))
    72  
    73  func rndW() Word {
    74  	return Word(rnd.Int63()<<1 | rnd.Int63n(2))
    75  }
    76  
    77  func rndV(n int) []Word {
    78  	v := make([]Word, n)
    79  	for i := range v {
    80  		v[i] = rndW()
    81  	}
    82  	return v
    83  }
    84  
    85  var benchSizes = []int{1, 2, 3, 4, 5, 1e1, 1e2, 1e3, 1e4, 1e5}
    86  
    87  func BenchmarkAddVV(b *testing.B) {
    88  	for _, n := range benchSizes {
    89  		if isRaceBuilder && n > 1e3 {
    90  			continue
    91  		}
    92  		x := rndV(n)
    93  		y := rndV(n)
    94  		z := make([]Word, n)
    95  		b.Run(fmt.Sprint(n), func(b *testing.B) {
    96  			b.SetBytes(int64(n * _W))
    97  			for i := 0; i < b.N; i++ {
    98  				addVV(z, x, y)
    99  			}
   100  		})
   101  	}
   102  }
   103  
   104  func BenchmarkSubVV(b *testing.B) {
   105  	for _, n := range benchSizes {
   106  		if isRaceBuilder && n > 1e3 {
   107  			continue
   108  		}
   109  		x := rndV(n)
   110  		y := rndV(n)
   111  		z := make([]Word, n)
   112  		b.Run(fmt.Sprint(n), func(b *testing.B) {
   113  			b.SetBytes(int64(n * _W))
   114  			for i := 0; i < b.N; i++ {
   115  				subVV(z, x, y)
   116  			}
   117  		})
   118  	}
   119  }
   120  
   121  type funVW func(z, x []Word, y Word) (c Word)
   122  type argVW struct {
   123  	z, x nat
   124  	y    Word
   125  	c    Word
   126  }
   127  
   128  var sumVW = []argVW{
   129  	{},
   130  	{nil, nil, 2, 2},
   131  	{nat{0}, nat{0}, 0, 0},
   132  	{nat{1}, nat{0}, 1, 0},
   133  	{nat{1}, nat{1}, 0, 0},
   134  	{nat{0}, nat{_M}, 1, 1},
   135  	{nat{0, 0, 0, 0}, nat{_M, _M, _M, _M}, 1, 1},
   136  	{nat{585}, nat{314}, 271, 0},
   137  }
   138  
   139  var lshVW = []argVW{
   140  	{},
   141  	{nat{0}, nat{0}, 0, 0},
   142  	{nat{0}, nat{0}, 1, 0},
   143  	{nat{0}, nat{0}, 20, 0},
   144  
   145  	{nat{_M}, nat{_M}, 0, 0},
   146  	{nat{_M << 1 & _M}, nat{_M}, 1, 1},
   147  	{nat{_M << 20 & _M}, nat{_M}, 20, _M >> (_W - 20)},
   148  
   149  	{nat{_M, _M, _M}, nat{_M, _M, _M}, 0, 0},
   150  	{nat{_M << 1 & _M, _M, _M}, nat{_M, _M, _M}, 1, 1},
   151  	{nat{_M << 20 & _M, _M, _M}, nat{_M, _M, _M}, 20, _M >> (_W - 20)},
   152  }
   153  
   154  var rshVW = []argVW{
   155  	{},
   156  	{nat{0}, nat{0}, 0, 0},
   157  	{nat{0}, nat{0}, 1, 0},
   158  	{nat{0}, nat{0}, 20, 0},
   159  
   160  	{nat{_M}, nat{_M}, 0, 0},
   161  	{nat{_M >> 1}, nat{_M}, 1, _M << (_W - 1) & _M},
   162  	{nat{_M >> 20}, nat{_M}, 20, _M << (_W - 20) & _M},
   163  
   164  	{nat{_M, _M, _M}, nat{_M, _M, _M}, 0, 0},
   165  	{nat{_M, _M, _M >> 1}, nat{_M, _M, _M}, 1, _M << (_W - 1) & _M},
   166  	{nat{_M, _M, _M >> 20}, nat{_M, _M, _M}, 20, _M << (_W - 20) & _M},
   167  }
   168  
   169  func testFunVW(t *testing.T, msg string, f funVW, a argVW) {
   170  	z := make(nat, len(a.z))
   171  	c := f(z, a.x, a.y)
   172  	for i, zi := range z {
   173  		if zi != a.z[i] {
   174  			t.Errorf("%s%+v\n\tgot z[%d] = %#x; want %#x", msg, a, i, zi, a.z[i])
   175  			break
   176  		}
   177  	}
   178  	if c != a.c {
   179  		t.Errorf("%s%+v\n\tgot c = %#x; want %#x", msg, a, c, a.c)
   180  	}
   181  }
   182  
   183  func testFunVWext(t *testing.T, msg string, f funVW, f_g funVW, a argVW) {
   184  	// using the result of addVW_g/subVW_g as golden
   185  	z_g := make(nat, len(a.z))
   186  	c_g := f_g(z_g, a.x, a.y)
   187  	c := f(a.z, a.x, a.y)
   188  
   189  	for i, zi := range a.z {
   190  		if zi != z_g[i] {
   191  			t.Errorf("%s\n\tgot z[%d] = %#x; want %#x", msg, i, zi, z_g[i])
   192  			break
   193  		}
   194  	}
   195  	if c != c_g {
   196  		t.Errorf("%s\n\tgot c = %#x; want %#x", msg, c, c_g)
   197  	}
   198  }
   199  
   200  func makeFunVW(f func(z, x []Word, s uint) (c Word)) funVW {
   201  	return func(z, x []Word, s Word) (c Word) {
   202  		return f(z, x, uint(s))
   203  	}
   204  }
   205  
   206  func TestFunVW(t *testing.T) {
   207  	for _, a := range sumVW {
   208  		arg := a
   209  		testFunVW(t, "addVW_g", addVW_g, arg)
   210  		testFunVW(t, "addVW", addVW, arg)
   211  
   212  		arg = argVW{a.x, a.z, a.y, a.c}
   213  		testFunVW(t, "subVW_g", subVW_g, arg)
   214  		testFunVW(t, "subVW", subVW, arg)
   215  	}
   216  
   217  	shlVW_g := makeFunVW(shlVU_g)
   218  	shlVW := makeFunVW(shlVU)
   219  	for _, a := range lshVW {
   220  		arg := a
   221  		testFunVW(t, "shlVU_g", shlVW_g, arg)
   222  		testFunVW(t, "shlVU", shlVW, arg)
   223  	}
   224  
   225  	shrVW_g := makeFunVW(shrVU_g)
   226  	shrVW := makeFunVW(shrVU)
   227  	for _, a := range rshVW {
   228  		arg := a
   229  		testFunVW(t, "shrVU_g", shrVW_g, arg)
   230  		testFunVW(t, "shrVU", shrVW, arg)
   231  	}
   232  }
   233  
   234  // Construct a vector comprising the same word, usually '0' or 'maximum uint'
   235  func makeWordVec(e Word, n int) []Word {
   236  	v := make([]Word, n)
   237  	for i := range v {
   238  		v[i] = e
   239  	}
   240  	return v
   241  }
   242  
   243  // Extended testing to addVW and subVW using various kinds of input data.
   244  // We utilize the results of addVW_g and subVW_g as golden reference to check
   245  // correctness.
   246  func TestFunVWExt(t *testing.T) {
   247  	// 32 is the current threshold that triggers an optimized version of
   248  	// calculation for large-sized vector, ensure we have sizes around it tested.
   249  	var vwSizes = []int{0, 1, 3, 4, 5, 8, 9, 23, 31, 32, 33, 34, 35, 36, 50, 120}
   250  	for _, n := range vwSizes {
   251  		// vector of random numbers, using the result of addVW_g/subVW_g as golden
   252  		x := rndV(n)
   253  		y := rndW()
   254  		z := make(nat, n)
   255  		arg := argVW{z, x, y, 0}
   256  		testFunVWext(t, "addVW, random inputs", addVW, addVW_g, arg)
   257  		testFunVWext(t, "subVW, random inputs", subVW, subVW_g, arg)
   258  
   259  		// vector of random numbers, but make 'x' and 'z' share storage
   260  		arg = argVW{x, x, y, 0}
   261  		testFunVWext(t, "addVW, random inputs, sharing storage", addVW, addVW_g, arg)
   262  		testFunVWext(t, "subVW, random inputs, sharing storage", subVW, subVW_g, arg)
   263  
   264  		// vector of maximum uint, to force carry flag set in each 'add'
   265  		y = ^Word(0)
   266  		x = makeWordVec(y, n)
   267  		arg = argVW{z, x, y, 0}
   268  		testFunVWext(t, "addVW, vector of max uint", addVW, addVW_g, arg)
   269  
   270  		// vector of '0', to force carry flag set in each 'sub'
   271  		x = makeWordVec(0, n)
   272  		arg = argVW{z, x, 1, 0}
   273  		testFunVWext(t, "subVW, vector of zero", subVW, subVW_g, arg)
   274  	}
   275  }
   276  
   277  type argVU struct {
   278  	d  []Word // d is a Word slice, the input parameters x and z come from this array.
   279  	l  uint   // l is the length of the input parameters x and z.
   280  	xp uint   // xp is the starting position of the input parameter x, x := d[xp:xp+l].
   281  	zp uint   // zp is the starting position of the input parameter z, z := d[zp:zp+l].
   282  	s  uint   // s is the shift number.
   283  	r  []Word // r is the expected output result z.
   284  	c  Word   // c is the expected return value.
   285  	m  string // message.
   286  }
   287  
   288  var argshlVUIn = []Word{1, 2, 4, 8, 16, 32, 64, 0, 0, 0}
   289  var argshlVUr0 = []Word{1, 2, 4, 8, 16, 32, 64}
   290  var argshlVUr1 = []Word{2, 4, 8, 16, 32, 64, 128}
   291  var argshlVUrWm1 = []Word{1 << (_W - 1), 0, 1, 2, 4, 8, 16}
   292  
   293  var argshlVU = []argVU{
   294  	// test cases for shlVU
   295  	{[]Word{1, _M, _M, _M, _M, _M, 3 << (_W - 2), 0}, 7, 0, 0, 1, []Word{2, _M - 1, _M, _M, _M, _M, 1<<(_W-1) + 1}, 1, "complete overlap of shlVU"},
   296  	{[]Word{1, _M, _M, _M, _M, _M, 3 << (_W - 2), 0, 0, 0, 0}, 7, 0, 3, 1, []Word{2, _M - 1, _M, _M, _M, _M, 1<<(_W-1) + 1}, 1, "partial overlap by half of shlVU"},
   297  	{[]Word{1, _M, _M, _M, _M, _M, 3 << (_W - 2), 0, 0, 0, 0, 0, 0, 0}, 7, 0, 6, 1, []Word{2, _M - 1, _M, _M, _M, _M, 1<<(_W-1) + 1}, 1, "partial overlap by 1 Word of shlVU"},
   298  	{[]Word{1, _M, _M, _M, _M, _M, 3 << (_W - 2), 0, 0, 0, 0, 0, 0, 0, 0}, 7, 0, 7, 1, []Word{2, _M - 1, _M, _M, _M, _M, 1<<(_W-1) + 1}, 1, "no overlap of shlVU"},
   299  	// additional test cases with shift values of 0, 1 and (_W-1)
   300  	{argshlVUIn, 7, 0, 0, 0, argshlVUr0, 0, "complete overlap of shlVU and shift of 0"},
   301  	{argshlVUIn, 7, 0, 0, 1, argshlVUr1, 0, "complete overlap of shlVU and shift of 1"},
   302  	{argshlVUIn, 7, 0, 0, _W - 1, argshlVUrWm1, 32, "complete overlap of shlVU and shift of _W - 1"},
   303  	{argshlVUIn, 7, 0, 1, 0, argshlVUr0, 0, "partial overlap by 6 Words of shlVU and shift of 0"},
   304  	{argshlVUIn, 7, 0, 1, 1, argshlVUr1, 0, "partial overlap by 6 Words of shlVU and shift of 1"},
   305  	{argshlVUIn, 7, 0, 1, _W - 1, argshlVUrWm1, 32, "partial overlap by 6 Words of shlVU and shift of _W - 1"},
   306  	{argshlVUIn, 7, 0, 2, 0, argshlVUr0, 0, "partial overlap by 5 Words of shlVU and shift of 0"},
   307  	{argshlVUIn, 7, 0, 2, 1, argshlVUr1, 0, "partial overlap by 5 Words of shlVU and shift of 1"},
   308  	{argshlVUIn, 7, 0, 2, _W - 1, argshlVUrWm1, 32, "partial overlap by 5 Words of shlVU abd shift of _W - 1"},
   309  	{argshlVUIn, 7, 0, 3, 0, argshlVUr0, 0, "partial overlap by 4 Words of shlVU and shift of 0"},
   310  	{argshlVUIn, 7, 0, 3, 1, argshlVUr1, 0, "partial overlap by 4 Words of shlVU and shift of 1"},
   311  	{argshlVUIn, 7, 0, 3, _W - 1, argshlVUrWm1, 32, "partial overlap by 4 Words of shlVU and shift of _W - 1"},
   312  }
   313  
   314  var argshrVUIn = []Word{0, 0, 0, 1, 2, 4, 8, 16, 32, 64}
   315  var argshrVUr0 = []Word{1, 2, 4, 8, 16, 32, 64}
   316  var argshrVUr1 = []Word{0, 1, 2, 4, 8, 16, 32}
   317  var argshrVUrWm1 = []Word{4, 8, 16, 32, 64, 128, 0}
   318  
   319  var argshrVU = []argVU{
   320  	// test cases for shrVU
   321  	{[]Word{0, 3, _M, _M, _M, _M, _M, 1 << (_W - 1)}, 7, 1, 1, 1, []Word{1<<(_W-1) + 1, _M, _M, _M, _M, _M >> 1, 1 << (_W - 2)}, 1 << (_W - 1), "complete overlap of shrVU"},
   322  	{[]Word{0, 0, 0, 0, 3, _M, _M, _M, _M, _M, 1 << (_W - 1)}, 7, 4, 1, 1, []Word{1<<(_W-1) + 1, _M, _M, _M, _M, _M >> 1, 1 << (_W - 2)}, 1 << (_W - 1), "partial overlap by half of shrVU"},
   323  	{[]Word{0, 0, 0, 0, 0, 0, 0, 3, _M, _M, _M, _M, _M, 1 << (_W - 1)}, 7, 7, 1, 1, []Word{1<<(_W-1) + 1, _M, _M, _M, _M, _M >> 1, 1 << (_W - 2)}, 1 << (_W - 1), "partial overlap by 1 Word of shrVU"},
   324  	{[]Word{0, 0, 0, 0, 0, 0, 0, 0, 3, _M, _M, _M, _M, _M, 1 << (_W - 1)}, 7, 8, 1, 1, []Word{1<<(_W-1) + 1, _M, _M, _M, _M, _M >> 1, 1 << (_W - 2)}, 1 << (_W - 1), "no overlap of shrVU"},
   325  	// additional test cases with shift values of 0, 1 and (_W-1)
   326  	{argshrVUIn, 7, 3, 3, 0, argshrVUr0, 0, "complete overlap of shrVU and shift of 0"},
   327  	{argshrVUIn, 7, 3, 3, 1, argshrVUr1, 1 << (_W - 1), "complete overlap of shrVU and shift of 1"},
   328  	{argshrVUIn, 7, 3, 3, _W - 1, argshrVUrWm1, 2, "complete overlap of shrVU and shift of _W - 1"},
   329  	{argshrVUIn, 7, 3, 2, 0, argshrVUr0, 0, "partial overlap by 6 Words of shrVU and shift of 0"},
   330  	{argshrVUIn, 7, 3, 2, 1, argshrVUr1, 1 << (_W - 1), "partial overlap by 6 Words of shrVU and shift of 1"},
   331  	{argshrVUIn, 7, 3, 2, _W - 1, argshrVUrWm1, 2, "partial overlap by 6 Words of shrVU and shift of _W - 1"},
   332  	{argshrVUIn, 7, 3, 1, 0, argshrVUr0, 0, "partial overlap by 5 Words of shrVU and shift of 0"},
   333  	{argshrVUIn, 7, 3, 1, 1, argshrVUr1, 1 << (_W - 1), "partial overlap by 5 Words of shrVU and shift of 1"},
   334  	{argshrVUIn, 7, 3, 1, _W - 1, argshrVUrWm1, 2, "partial overlap by 5 Words of shrVU and shift of _W - 1"},
   335  	{argshrVUIn, 7, 3, 0, 0, argshrVUr0, 0, "partial overlap by 4 Words of shrVU and shift of 0"},
   336  	{argshrVUIn, 7, 3, 0, 1, argshrVUr1, 1 << (_W - 1), "partial overlap by 4 Words of shrVU and shift of 1"},
   337  	{argshrVUIn, 7, 3, 0, _W - 1, argshrVUrWm1, 2, "partial overlap by 4 Words of shrVU and shift of _W - 1"},
   338  }
   339  
   340  func testShiftFunc(t *testing.T, f func(z, x []Word, s uint) Word, a argVU) {
   341  	// work on copy of a.d to preserve the original data.
   342  	b := make([]Word, len(a.d))
   343  	copy(b, a.d)
   344  	z := b[a.zp : a.zp+a.l]
   345  	x := b[a.xp : a.xp+a.l]
   346  	c := f(z, x, a.s)
   347  	for i, zi := range z {
   348  		if zi != a.r[i] {
   349  			t.Errorf("d := %v, %s(d[%d:%d], d[%d:%d], %d)\n\tgot z[%d] = %#x; want %#x", a.d, a.m, a.zp, a.zp+a.l, a.xp, a.xp+a.l, a.s, i, zi, a.r[i])
   350  			break
   351  		}
   352  	}
   353  	if c != a.c {
   354  		t.Errorf("d := %v, %s(d[%d:%d], d[%d:%d], %d)\n\tgot c = %#x; want %#x", a.d, a.m, a.zp, a.zp+a.l, a.xp, a.xp+a.l, a.s, c, a.c)
   355  	}
   356  }
   357  
   358  func TestShiftOverlap(t *testing.T) {
   359  	for _, a := range argshlVU {
   360  		arg := a
   361  		testShiftFunc(t, shlVU, arg)
   362  	}
   363  
   364  	for _, a := range argshrVU {
   365  		arg := a
   366  		testShiftFunc(t, shrVU, arg)
   367  	}
   368  }
   369  
   370  func TestIssue31084(t *testing.T) {
   371  	// compute 10^n via 5^n << n.
   372  	const n = 165
   373  	p := nat(nil).expNN(nat{5}, nat{n}, nil, false)
   374  	p = p.shl(p, n)
   375  	got := string(p.utoa(10))
   376  	want := "1" + strings.Repeat("0", n)
   377  	if got != want {
   378  		t.Errorf("shl(%v, %v)\n\tgot  %s\n\twant %s", p, n, got, want)
   379  	}
   380  }
   381  
   382  const issue42838Value = "159309191113245227702888039776771180559110455519261878607388585338616290151305816094308987472018268594098344692611135542392730712890625"
   383  
   384  func TestIssue42838(t *testing.T) {
   385  	const s = 192
   386  	z, _, _, _ := nat(nil).scan(strings.NewReader(issue42838Value), 0, false)
   387  	z = z.shl(z, s)
   388  	got := string(z.utoa(10))
   389  	want := "1" + strings.Repeat("0", s)
   390  	if got != want {
   391  		t.Errorf("shl(%v, %v)\n\tgot  %s\n\twant %s", z, s, got, want)
   392  	}
   393  }
   394  
   395  func BenchmarkAddVW(b *testing.B) {
   396  	for _, n := range benchSizes {
   397  		if isRaceBuilder && n > 1e3 {
   398  			continue
   399  		}
   400  		x := rndV(n)
   401  		y := rndW()
   402  		z := make([]Word, n)
   403  		b.Run(fmt.Sprint(n), func(b *testing.B) {
   404  			b.SetBytes(int64(n * _S))
   405  			for i := 0; i < b.N; i++ {
   406  				addVW(z, x, y)
   407  			}
   408  		})
   409  	}
   410  }
   411  
   412  // Benchmarking addVW using vector of maximum uint to force carry flag set
   413  func BenchmarkAddVWext(b *testing.B) {
   414  	for _, n := range benchSizes {
   415  		if isRaceBuilder && n > 1e3 {
   416  			continue
   417  		}
   418  		y := ^Word(0)
   419  		x := makeWordVec(y, n)
   420  		z := make([]Word, n)
   421  		b.Run(fmt.Sprint(n), func(b *testing.B) {
   422  			b.SetBytes(int64(n * _S))
   423  			for i := 0; i < b.N; i++ {
   424  				addVW(z, x, y)
   425  			}
   426  		})
   427  	}
   428  }
   429  
   430  func BenchmarkSubVW(b *testing.B) {
   431  	for _, n := range benchSizes {
   432  		if isRaceBuilder && n > 1e3 {
   433  			continue
   434  		}
   435  		x := rndV(n)
   436  		y := rndW()
   437  		z := make([]Word, n)
   438  		b.Run(fmt.Sprint(n), func(b *testing.B) {
   439  			b.SetBytes(int64(n * _S))
   440  			for i := 0; i < b.N; i++ {
   441  				subVW(z, x, y)
   442  			}
   443  		})
   444  	}
   445  }
   446  
   447  // Benchmarking subVW using vector of zero to force carry flag set
   448  func BenchmarkSubVWext(b *testing.B) {
   449  	for _, n := range benchSizes {
   450  		if isRaceBuilder && n > 1e3 {
   451  			continue
   452  		}
   453  		x := makeWordVec(0, n)
   454  		y := Word(1)
   455  		z := make([]Word, n)
   456  		b.Run(fmt.Sprint(n), func(b *testing.B) {
   457  			b.SetBytes(int64(n * _S))
   458  			for i := 0; i < b.N; i++ {
   459  				subVW(z, x, y)
   460  			}
   461  		})
   462  	}
   463  }
   464  
   465  type funVWW func(z, x []Word, y, r Word) (c Word)
   466  type argVWW struct {
   467  	z, x nat
   468  	y, r Word
   469  	c    Word
   470  }
   471  
   472  var prodVWW = []argVWW{
   473  	{},
   474  	{nat{0}, nat{0}, 0, 0, 0},
   475  	{nat{991}, nat{0}, 0, 991, 0},
   476  	{nat{0}, nat{_M}, 0, 0, 0},
   477  	{nat{991}, nat{_M}, 0, 991, 0},
   478  	{nat{0}, nat{0}, _M, 0, 0},
   479  	{nat{991}, nat{0}, _M, 991, 0},
   480  	{nat{1}, nat{1}, 1, 0, 0},
   481  	{nat{992}, nat{1}, 1, 991, 0},
   482  	{nat{22793}, nat{991}, 23, 0, 0},
   483  	{nat{22800}, nat{991}, 23, 7, 0},
   484  	{nat{0, 0, 0, 22793}, nat{0, 0, 0, 991}, 23, 0, 0},
   485  	{nat{7, 0, 0, 22793}, nat{0, 0, 0, 991}, 23, 7, 0},
   486  	{nat{0, 0, 0, 0}, nat{7893475, 7395495, 798547395, 68943}, 0, 0, 0},
   487  	{nat{991, 0, 0, 0}, nat{7893475, 7395495, 798547395, 68943}, 0, 991, 0},
   488  	{nat{0, 0, 0, 0}, nat{0, 0, 0, 0}, 894375984, 0, 0},
   489  	{nat{991, 0, 0, 0}, nat{0, 0, 0, 0}, 894375984, 991, 0},
   490  	{nat{_M << 1 & _M}, nat{_M}, 1 << 1, 0, _M >> (_W - 1)},
   491  	{nat{_M<<1&_M + 1}, nat{_M}, 1 << 1, 1, _M >> (_W - 1)},
   492  	{nat{_M << 7 & _M}, nat{_M}, 1 << 7, 0, _M >> (_W - 7)},
   493  	{nat{_M<<7&_M + 1<<6}, nat{_M}, 1 << 7, 1 << 6, _M >> (_W - 7)},
   494  	{nat{_M << 7 & _M, _M, _M, _M}, nat{_M, _M, _M, _M}, 1 << 7, 0, _M >> (_W - 7)},
   495  	{nat{_M<<7&_M + 1<<6, _M, _M, _M}, nat{_M, _M, _M, _M}, 1 << 7, 1 << 6, _M >> (_W - 7)},
   496  }
   497  
   498  func testFunVWW(t *testing.T, msg string, f funVWW, a argVWW) {
   499  	z := make(nat, len(a.z))
   500  	c := f(z, a.x, a.y, a.r)
   501  	for i, zi := range z {
   502  		if zi != a.z[i] {
   503  			t.Errorf("%s%+v\n\tgot z[%d] = %#x; want %#x", msg, a, i, zi, a.z[i])
   504  			break
   505  		}
   506  	}
   507  	if c != a.c {
   508  		t.Errorf("%s%+v\n\tgot c = %#x; want %#x", msg, a, c, a.c)
   509  	}
   510  }
   511  
   512  // TODO(gri) mulAddVWW and divWVW are symmetric operations but
   513  // their signature is not symmetric. Try to unify.
   514  
   515  type funWVW func(z []Word, xn Word, x []Word, y Word) (r Word)
   516  type argWVW struct {
   517  	z  nat
   518  	xn Word
   519  	x  nat
   520  	y  Word
   521  	r  Word
   522  }
   523  
   524  func testFunWVW(t *testing.T, msg string, f funWVW, a argWVW) {
   525  	z := make(nat, len(a.z))
   526  	r := f(z, a.xn, a.x, a.y)
   527  	for i, zi := range z {
   528  		if zi != a.z[i] {
   529  			t.Errorf("%s%+v\n\tgot z[%d] = %#x; want %#x", msg, a, i, zi, a.z[i])
   530  			break
   531  		}
   532  	}
   533  	if r != a.r {
   534  		t.Errorf("%s%+v\n\tgot r = %#x; want %#x", msg, a, r, a.r)
   535  	}
   536  }
   537  
   538  func TestFunVWW(t *testing.T) {
   539  	for _, a := range prodVWW {
   540  		arg := a
   541  		testFunVWW(t, "mulAddVWW_g", mulAddVWW_g, arg)
   542  		testFunVWW(t, "mulAddVWW", mulAddVWW, arg)
   543  
   544  		if a.y != 0 && a.r < a.y {
   545  			arg := argWVW{a.x, a.c, a.z, a.y, a.r}
   546  			testFunWVW(t, "divWVW", divWVW, arg)
   547  		}
   548  	}
   549  }
   550  
   551  var mulWWTests = []struct {
   552  	x, y Word
   553  	q, r Word
   554  }{
   555  	{_M, _M, _M - 1, 1},
   556  	// 32 bit only: {0xc47dfa8c, 50911, 0x98a4, 0x998587f4},
   557  }
   558  
   559  func TestMulWW(t *testing.T) {
   560  	for i, test := range mulWWTests {
   561  		q, r := mulWW(test.x, test.y)
   562  		if q != test.q || r != test.r {
   563  			t.Errorf("#%d got (%x, %x) want (%x, %x)", i, q, r, test.q, test.r)
   564  		}
   565  	}
   566  }
   567  
   568  var mulAddWWWTests = []struct {
   569  	x, y, c Word
   570  	q, r    Word
   571  }{
   572  	// TODO(agl): These will only work on 64-bit platforms.
   573  	// {15064310297182388543, 0xe7df04d2d35d5d80, 13537600649892366549, 13644450054494335067, 10832252001440893781},
   574  	// {15064310297182388543, 0xdab2f18048baa68d, 13644450054494335067, 12869334219691522700, 14233854684711418382},
   575  	{_M, _M, 0, _M - 1, 1},
   576  	{_M, _M, _M, _M, 0},
   577  }
   578  
   579  func TestMulAddWWW(t *testing.T) {
   580  	for i, test := range mulAddWWWTests {
   581  		q, r := mulAddWWW_g(test.x, test.y, test.c)
   582  		if q != test.q || r != test.r {
   583  			t.Errorf("#%d got (%x, %x) want (%x, %x)", i, q, r, test.q, test.r)
   584  		}
   585  	}
   586  }
   587  
   588  var divWWTests = []struct {
   589  	x1, x0, y Word
   590  	q, r      Word
   591  }{
   592  	{_M >> 1, 0, _M, _M >> 1, _M >> 1},
   593  	{_M - (1 << (_W - 2)), _M, 3 << (_W - 2), _M, _M - (1 << (_W - 2))},
   594  }
   595  
   596  const testsNumber = 1 << 16
   597  
   598  func TestDivWW(t *testing.T) {
   599  	i := 0
   600  	for i, test := range divWWTests {
   601  		rec := reciprocalWord(test.y)
   602  		q, r := divWW(test.x1, test.x0, test.y, rec)
   603  		if q != test.q || r != test.r {
   604  			t.Errorf("#%d got (%x, %x) want (%x, %x)", i, q, r, test.q, test.r)
   605  		}
   606  	}
   607  	//random tests
   608  	for ; i < testsNumber; i++ {
   609  		x1 := rndW()
   610  		x0 := rndW()
   611  		y := rndW()
   612  		if x1 >= y {
   613  			continue
   614  		}
   615  		rec := reciprocalWord(y)
   616  		qGot, rGot := divWW(x1, x0, y, rec)
   617  		qWant, rWant := bits.Div(uint(x1), uint(x0), uint(y))
   618  		if uint(qGot) != qWant || uint(rGot) != rWant {
   619  			t.Errorf("#%d got (%x, %x) want (%x, %x)", i, qGot, rGot, qWant, rWant)
   620  		}
   621  	}
   622  }
   623  
   624  func BenchmarkMulAddVWW(b *testing.B) {
   625  	for _, n := range benchSizes {
   626  		if isRaceBuilder && n > 1e3 {
   627  			continue
   628  		}
   629  		z := make([]Word, n+1)
   630  		x := rndV(n)
   631  		y := rndW()
   632  		r := rndW()
   633  		b.Run(fmt.Sprint(n), func(b *testing.B) {
   634  			b.SetBytes(int64(n * _W))
   635  			for i := 0; i < b.N; i++ {
   636  				mulAddVWW(z, x, y, r)
   637  			}
   638  		})
   639  	}
   640  }
   641  
   642  func BenchmarkAddMulVVW(b *testing.B) {
   643  	for _, n := range benchSizes {
   644  		if isRaceBuilder && n > 1e3 {
   645  			continue
   646  		}
   647  		x := rndV(n)
   648  		y := rndW()
   649  		z := make([]Word, n)
   650  		b.Run(fmt.Sprint(n), func(b *testing.B) {
   651  			b.SetBytes(int64(n * _W))
   652  			for i := 0; i < b.N; i++ {
   653  				addMulVVW(z, x, y)
   654  			}
   655  		})
   656  	}
   657  }
   658  func BenchmarkDivWVW(b *testing.B) {
   659  	for _, n := range benchSizes {
   660  		if isRaceBuilder && n > 1e3 {
   661  			continue
   662  		}
   663  		x := rndV(n)
   664  		y := rndW()
   665  		z := make([]Word, n)
   666  		b.Run(fmt.Sprint(n), func(b *testing.B) {
   667  			b.SetBytes(int64(n * _W))
   668  			for i := 0; i < b.N; i++ {
   669  				divWVW(z, 0, x, y)
   670  			}
   671  		})
   672  	}
   673  }
   674  
   675  func BenchmarkNonZeroShifts(b *testing.B) {
   676  	for _, n := range benchSizes {
   677  		if isRaceBuilder && n > 1e3 {
   678  			continue
   679  		}
   680  		x := rndV(n)
   681  		s := uint(rand.Int63n(_W-2)) + 1 // avoid 0 and over-large shifts
   682  		z := make([]Word, n)
   683  		b.Run(fmt.Sprint(n), func(b *testing.B) {
   684  			b.SetBytes(int64(n * _W))
   685  			b.Run("shrVU", func(b *testing.B) {
   686  				for i := 0; i < b.N; i++ {
   687  					_ = shrVU(z, x, s)
   688  				}
   689  			})
   690  			b.Run("shlVU", func(b *testing.B) {
   691  				for i := 0; i < b.N; i++ {
   692  					_ = shlVU(z, x, s)
   693  				}
   694  			})
   695  		})
   696  	}
   697  }
   698  

View as plain text