1// Copyright 2023 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
5package runtime_test
6
7import (
8	"math"
9	"strings"
10	"testing"
11	"unsafe"
12)
13
14var (
15	zero    = math.Copysign(0, +1)
16	negZero = math.Copysign(0, -1)
17	inf     = math.Inf(+1)
18	negInf  = math.Inf(-1)
19	nan     = math.NaN()
20)
21
22var tests = []struct{ min, max float64 }{
23	{1, 2},
24	{-2, 1},
25	{negZero, zero},
26	{zero, inf},
27	{negInf, zero},
28	{negInf, inf},
29	{1, inf},
30	{negInf, 1},
31}
32
33var all = []float64{1, 2, -1, -2, zero, negZero, inf, negInf, nan}
34
35func eq(x, y float64) bool {
36	return x == y && math.Signbit(x) == math.Signbit(y)
37}
38
39func TestMinFloat(t *testing.T) {
40	for _, tt := range tests {
41		if z := min(tt.min, tt.max); !eq(z, tt.min) {
42			t.Errorf("min(%v, %v) = %v, want %v", tt.min, tt.max, z, tt.min)
43		}
44		if z := min(tt.max, tt.min); !eq(z, tt.min) {
45			t.Errorf("min(%v, %v) = %v, want %v", tt.max, tt.min, z, tt.min)
46		}
47	}
48	for _, x := range all {
49		if z := min(nan, x); !math.IsNaN(z) {
50			t.Errorf("min(%v, %v) = %v, want %v", nan, x, z, nan)
51		}
52		if z := min(x, nan); !math.IsNaN(z) {
53			t.Errorf("min(%v, %v) = %v, want %v", nan, x, z, nan)
54		}
55	}
56}
57
58func TestMaxFloat(t *testing.T) {
59	for _, tt := range tests {
60		if z := max(tt.min, tt.max); !eq(z, tt.max) {
61			t.Errorf("max(%v, %v) = %v, want %v", tt.min, tt.max, z, tt.max)
62		}
63		if z := max(tt.max, tt.min); !eq(z, tt.max) {
64			t.Errorf("max(%v, %v) = %v, want %v", tt.max, tt.min, z, tt.max)
65		}
66	}
67	for _, x := range all {
68		if z := max(nan, x); !math.IsNaN(z) {
69			t.Errorf("max(%v, %v) = %v, want %v", nan, x, z, nan)
70		}
71		if z := max(x, nan); !math.IsNaN(z) {
72			t.Errorf("max(%v, %v) = %v, want %v", nan, x, z, nan)
73		}
74	}
75}
76
77// testMinMax tests that min/max behave correctly on every pair of
78// values in vals.
79//
80// vals should be a sequence of values in strictly ascending order.
81func testMinMax[T int | uint8 | string](t *testing.T, vals ...T) {
82	for i, x := range vals {
83		for _, y := range vals[i+1:] {
84			if !(x < y) {
85				t.Fatalf("values out of order: !(%v < %v)", x, y)
86			}
87
88			if z := min(x, y); z != x {
89				t.Errorf("min(%v, %v) = %v, want %v", x, y, z, x)
90			}
91			if z := min(y, x); z != x {
92				t.Errorf("min(%v, %v) = %v, want %v", y, x, z, x)
93			}
94
95			if z := max(x, y); z != y {
96				t.Errorf("max(%v, %v) = %v, want %v", x, y, z, y)
97			}
98			if z := max(y, x); z != y {
99				t.Errorf("max(%v, %v) = %v, want %v", y, x, z, y)
100			}
101		}
102	}
103}
104
105func TestMinMaxInt(t *testing.T)    { testMinMax[int](t, -7, 0, 9) }
106func TestMinMaxUint8(t *testing.T)  { testMinMax[uint8](t, 0, 1, 2, 4, 7) }
107func TestMinMaxString(t *testing.T) { testMinMax[string](t, "a", "b", "c") }
108
109// TestMinMaxStringTies ensures that min(a, b) returns a when a == b.
110func TestMinMaxStringTies(t *testing.T) {
111	s := "xxx"
112	x := strings.Split(s, "")
113
114	test := func(i, j, k int) {
115		if z := min(x[i], x[j], x[k]); unsafe.StringData(z) != unsafe.StringData(x[i]) {
116			t.Errorf("min(x[%v], x[%v], x[%v]) = %p, want %p", i, j, k, unsafe.StringData(z), unsafe.StringData(x[i]))
117		}
118		if z := max(x[i], x[j], x[k]); unsafe.StringData(z) != unsafe.StringData(x[i]) {
119			t.Errorf("max(x[%v], x[%v], x[%v]) = %p, want %p", i, j, k, unsafe.StringData(z), unsafe.StringData(x[i]))
120		}
121	}
122
123	test(0, 1, 2)
124	test(0, 2, 1)
125	test(1, 0, 2)
126	test(1, 2, 0)
127	test(2, 0, 1)
128	test(2, 1, 0)
129}
130
131func BenchmarkMinFloat(b *testing.B) {
132	var m float64 = 0
133	for i := 0; i < b.N; i++ {
134		for _, f := range all {
135			m = min(m, f)
136		}
137	}
138}
139
140func BenchmarkMaxFloat(b *testing.B) {
141	var m float64 = 0
142	for i := 0; i < b.N; i++ {
143		for _, f := range all {
144			m = max(m, f)
145		}
146	}
147}
148