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