1// Copyright ©2015 The Gonum 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
5package f64_test
6
7import (
8	"fmt"
9	"math"
10	"testing"
11
12	"golang.org/x/exp/rand"
13
14	. "gonum.org/v1/gonum/internal/asm/f64"
15)
16
17func TestDotUnitary(t *testing.T) {
18	for i, test := range []struct {
19		xData []float64
20		yData []float64
21
22		want float64
23	}{
24		{
25			xData: []float64{2},
26			yData: []float64{-3},
27			want:  -6,
28		},
29		{
30			xData: []float64{2, 3},
31			yData: []float64{-3, 4},
32			want:  6,
33		},
34		{
35			xData: []float64{2, 3, -4},
36			yData: []float64{-3, 4, 5},
37			want:  -14,
38		},
39		{
40			xData: []float64{2, 3, -4, -5},
41			yData: []float64{-3, 4, 5, -6},
42			want:  16,
43		},
44		{
45			xData: []float64{0, 2, 3, -4, -5},
46			yData: []float64{0, -3, 4, 5, -6},
47			want:  16,
48		},
49		{
50			xData: []float64{0, 0, 2, 3, -4, -5},
51			yData: []float64{0, 1, -3, 4, 5, -6},
52			want:  16,
53		},
54		{
55			xData: []float64{0, 0, 1, 1, 2, -3, -4},
56			yData: []float64{0, 1, 0, 3, -4, 5, -6},
57			want:  4,
58		},
59		{
60			xData: []float64{0, 0, 1, 1, 2, -3, -4, 5},
61			yData: []float64{0, 1, 0, 3, -4, 5, -6, 7},
62			want:  39,
63		},
64	} {
65		const msgGuard = "test %v: out-of-bounds write to %v argument\nfront guard: %v\nback guard: %v"
66
67		x, xFront, xBack := newGuardedVector(test.xData, 1)
68		y, yFront, yBack := newGuardedVector(test.yData, 1)
69		got := DotUnitary(x, y)
70
71		if !allNaN(xFront) || !allNaN(xBack) {
72			t.Errorf(msgGuard, i, "x", xFront, xBack)
73		}
74		if !allNaN(yFront) || !allNaN(yBack) {
75			t.Errorf(msgGuard, i, "y", yFront, yBack)
76		}
77		if !equalStrided(test.xData, x, 1) {
78			t.Errorf("test %v: modified read-only x argument", i)
79		}
80		if !equalStrided(test.yData, y, 1) {
81			t.Errorf("test %v: modified read-only y argument", i)
82		}
83		if math.IsNaN(got) {
84			t.Errorf("test %v: invalid memory read", i)
85			continue
86		}
87
88		if got != test.want {
89			t.Errorf("test %v: unexpected result. want %v, got %v", i, test.want, got)
90		}
91	}
92}
93
94func TestDotInc(t *testing.T) {
95	for i, test := range []struct {
96		xData []float64
97		yData []float64
98
99		want    float64
100		wantRev float64 // Result when one of the vectors is reversed.
101	}{
102		{
103			xData:   []float64{2},
104			yData:   []float64{-3},
105			want:    -6,
106			wantRev: -6,
107		},
108		{
109			xData:   []float64{2, 3},
110			yData:   []float64{-3, 4},
111			want:    6,
112			wantRev: -1,
113		},
114		{
115			xData:   []float64{2, 3, -4},
116			yData:   []float64{-3, 4, 5},
117			want:    -14,
118			wantRev: 34,
119		},
120		{
121			xData:   []float64{2, 3, -4, -5},
122			yData:   []float64{-3, 4, 5, -6},
123			want:    16,
124			wantRev: 2,
125		},
126		{
127			xData:   []float64{0, 2, 3, -4, -5},
128			yData:   []float64{0, -3, 4, 5, -6},
129			want:    16,
130			wantRev: 34,
131		},
132		{
133			xData:   []float64{0, 0, 2, 3, -4, -5},
134			yData:   []float64{0, 1, -3, 4, 5, -6},
135			want:    16,
136			wantRev: -5,
137		},
138		{
139			xData:   []float64{0, 0, 1, 1, 2, -3, -4},
140			yData:   []float64{0, 1, 0, 3, -4, 5, -6},
141			want:    4,
142			wantRev: -4,
143		},
144		{
145			xData:   []float64{0, 0, 1, 1, 2, -3, -4, 5},
146			yData:   []float64{0, 1, 0, 3, -4, 5, -6, 7},
147			want:    39,
148			wantRev: 3,
149		},
150	} {
151		const msgGuard = "%v: out-of-bounds write to %v argument\nfront guard: %v\nback guard: %v"
152
153		for _, incX := range []int{-7, -3, -2, -1, 1, 2, 3, 7} {
154			for _, incY := range []int{-7, -3, -2, -1, 1, 2, 3, 7} {
155				n := len(test.xData)
156				x, xFront, xBack := newGuardedVector(test.xData, incX)
157				y, yFront, yBack := newGuardedVector(test.yData, incY)
158
159				var ix, iy int
160				if incX < 0 {
161					ix = (-n + 1) * incX
162				}
163				if incY < 0 {
164					iy = (-n + 1) * incY
165				}
166				got := DotInc(x, y, uintptr(n), uintptr(incX), uintptr(incY), uintptr(ix), uintptr(iy))
167
168				prefix := fmt.Sprintf("test %v, incX = %v, incY = %v", i, incX, incY)
169				if !allNaN(xFront) || !allNaN(xBack) {
170					t.Errorf(msgGuard, prefix, "x", xFront, xBack)
171				}
172				if !allNaN(yFront) || !allNaN(yBack) {
173					t.Errorf(msgGuard, prefix, "y", yFront, yBack)
174				}
175				if nonStridedWrite(x, incX) || !equalStrided(test.xData, x, incX) {
176					t.Errorf("%v: modified read-only x argument", prefix)
177				}
178				if nonStridedWrite(y, incY) || !equalStrided(test.yData, y, incY) {
179					t.Errorf("%v: modified read-only y argument", prefix)
180				}
181				if math.IsNaN(got) {
182					t.Errorf("%v: invalid memory read", prefix)
183					continue
184				}
185
186				want := test.want
187				if incX*incY < 0 {
188					want = test.wantRev
189				}
190				if got != want {
191					t.Errorf("%v: unexpected result. want %v, got %v", prefix, want, got)
192				}
193			}
194		}
195	}
196}
197
198func BenchmarkDotUnitaryN1(b *testing.B)      { dotUnitaryBenchmark(b, 1) }
199func BenchmarkDotUnitaryN2(b *testing.B)      { dotUnitaryBenchmark(b, 2) }
200func BenchmarkDotUnitaryN3(b *testing.B)      { dotUnitaryBenchmark(b, 3) }
201func BenchmarkDotUnitaryN4(b *testing.B)      { dotUnitaryBenchmark(b, 4) }
202func BenchmarkDotUnitaryN10(b *testing.B)     { dotUnitaryBenchmark(b, 10) }
203func BenchmarkDotUnitaryN100(b *testing.B)    { dotUnitaryBenchmark(b, 100) }
204func BenchmarkDotUnitaryN1000(b *testing.B)   { dotUnitaryBenchmark(b, 1000) }
205func BenchmarkDotUnitaryN10000(b *testing.B)  { dotUnitaryBenchmark(b, 10000) }
206func BenchmarkDotUnitaryN100000(b *testing.B) { dotUnitaryBenchmark(b, 100000) }
207
208var r float64
209
210func dotUnitaryBenchmark(b *testing.B, n int) {
211	x := make([]float64, n)
212	for i := range x {
213		x[i] = rand.Float64()
214	}
215	y := make([]float64, n)
216	for i := range y {
217		y[i] = rand.Float64()
218	}
219	b.ResetTimer()
220	for i := 0; i < b.N; i++ {
221		r = DotUnitary(x, y)
222	}
223}
224
225func BenchmarkDotIncN1Inc1(b *testing.B) { dotIncBenchmark(b, 1, 1) }
226
227func BenchmarkDotIncN2Inc1(b *testing.B)  { dotIncBenchmark(b, 2, 1) }
228func BenchmarkDotIncN2Inc2(b *testing.B)  { dotIncBenchmark(b, 2, 2) }
229func BenchmarkDotIncN2Inc4(b *testing.B)  { dotIncBenchmark(b, 2, 4) }
230func BenchmarkDotIncN2Inc10(b *testing.B) { dotIncBenchmark(b, 2, 10) }
231
232func BenchmarkDotIncN3Inc1(b *testing.B)  { dotIncBenchmark(b, 3, 1) }
233func BenchmarkDotIncN3Inc2(b *testing.B)  { dotIncBenchmark(b, 3, 2) }
234func BenchmarkDotIncN3Inc4(b *testing.B)  { dotIncBenchmark(b, 3, 4) }
235func BenchmarkDotIncN3Inc10(b *testing.B) { dotIncBenchmark(b, 3, 10) }
236
237func BenchmarkDotIncN4Inc1(b *testing.B)  { dotIncBenchmark(b, 4, 1) }
238func BenchmarkDotIncN4Inc2(b *testing.B)  { dotIncBenchmark(b, 4, 2) }
239func BenchmarkDotIncN4Inc4(b *testing.B)  { dotIncBenchmark(b, 4, 4) }
240func BenchmarkDotIncN4Inc10(b *testing.B) { dotIncBenchmark(b, 4, 10) }
241
242func BenchmarkDotIncN10Inc1(b *testing.B)  { dotIncBenchmark(b, 10, 1) }
243func BenchmarkDotIncN10Inc2(b *testing.B)  { dotIncBenchmark(b, 10, 2) }
244func BenchmarkDotIncN10Inc4(b *testing.B)  { dotIncBenchmark(b, 10, 4) }
245func BenchmarkDotIncN10Inc10(b *testing.B) { dotIncBenchmark(b, 10, 10) }
246
247func BenchmarkDotIncN1000Inc1(b *testing.B)  { dotIncBenchmark(b, 1000, 1) }
248func BenchmarkDotIncN1000Inc2(b *testing.B)  { dotIncBenchmark(b, 1000, 2) }
249func BenchmarkDotIncN1000Inc4(b *testing.B)  { dotIncBenchmark(b, 1000, 4) }
250func BenchmarkDotIncN1000Inc10(b *testing.B) { dotIncBenchmark(b, 1000, 10) }
251
252func BenchmarkDotIncN100000Inc1(b *testing.B)  { dotIncBenchmark(b, 100000, 1) }
253func BenchmarkDotIncN100000Inc2(b *testing.B)  { dotIncBenchmark(b, 100000, 2) }
254func BenchmarkDotIncN100000Inc4(b *testing.B)  { dotIncBenchmark(b, 100000, 4) }
255func BenchmarkDotIncN100000Inc10(b *testing.B) { dotIncBenchmark(b, 100000, 10) }
256
257func BenchmarkDotIncN100000IncM1(b *testing.B)  { dotIncBenchmark(b, 100000, -1) }
258func BenchmarkDotIncN100000IncM2(b *testing.B)  { dotIncBenchmark(b, 100000, -2) }
259func BenchmarkDotIncN100000IncM4(b *testing.B)  { dotIncBenchmark(b, 100000, -4) }
260func BenchmarkDotIncN100000IncM10(b *testing.B) { dotIncBenchmark(b, 100000, -10) }
261
262func dotIncBenchmark(b *testing.B, n, inc int) {
263	absInc := inc
264	if inc < 0 {
265		absInc = -inc
266	}
267	x := make([]float64, (n-1)*absInc+1)
268	for i := range x {
269		x[i] = rand.Float64()
270	}
271	y := make([]float64, (n-1)*absInc+1)
272	for i := range y {
273		y[i] = rand.Float64()
274	}
275	var ini int
276	if inc < 0 {
277		ini = (-n + 1) * inc
278	}
279	b.ResetTimer()
280	for i := 0; i < b.N; i++ {
281		r = DotInc(x, y, uintptr(n), uintptr(inc), uintptr(inc), uintptr(ini), uintptr(ini))
282	}
283}
284