-
Notifications
You must be signed in to change notification settings - Fork 2
/
number.go
100 lines (91 loc) · 3.42 KB
/
number.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package verify
import (
"fmt"
"math"
)
// FluentNumber encapsulates assertions for numbers
// that supports the operators < <= >= > + - * /.
type FluentNumber[T ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | ~float32 | ~float64] struct {
FluentOrdered[T]
}
// Number is used for testing numbers
// that supports the operators < <= >= > + - * /.
func Number[T ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | ~float32 | ~float64](got T) FluentNumber[T] {
return FluentNumber[T]{FluentOrdered[T]{FluentObj[T]{FluentAny[T]{got}}}}
}
// InDelta tests that the numbers have an absolute error (distance) less or equal than delta.
func (x FluentNumber[T]) InDelta(want T, delta float64) FailureMessage {
distance, msg := x.calcDistance(want, delta)
if msg != "" {
return msg
}
if distance < -delta || distance > delta {
return FailureMessage(fmt.Sprintf("absolute error (distance) between numbers is greater than delta\nrelative error: %v\ndelta: %g\ngot: %v\nwant: %v", distance, delta, x.Got, want))
}
return ""
}
// NotInDelta tests that the numbers have an absolute error (distance) greater than delta.
func (x FluentNumber[T]) NotInDelta(want T, delta float64) FailureMessage {
distance, msg := x.calcDistance(want, delta)
if msg != "" {
return msg
}
if distance < -delta || distance > delta {
return ""
}
return FailureMessage(fmt.Sprintf("absolute error (distance) between numbers is lesser or equal than delta\nrelative error: %v\ndelta: %g\ngot: %v\nwant: %v", distance, delta, x.Got, want))
}
func (x FluentNumber[T]) calcDistance(want T, delta float64) (float64, FailureMessage) {
if math.IsNaN(delta) || delta < 0 {
return 0, "delta must be a non-negative number"
}
wantF := float64(want)
gotF := float64(x.Got)
if math.IsNaN(wantF) {
return 0, "want is NaN"
}
if math.IsNaN(gotF) {
return 0, "got is NaN"
}
return wantF - gotF, ""
}
// InEpsilon tests that the numbers have a relative error less or equal than epsilon.
func (x FluentNumber[T]) InEpsilon(want T, epsilon float64) FailureMessage {
relativeError, msg := x.calcRelativeError(want, epsilon)
if msg != "" {
return msg
}
if relativeError > epsilon {
return FailureMessage(fmt.Sprintf("relative error between numbers is greater than epsilon\nrelative error: %g\nepsilon: %g\ngot: %v\nwant: %v", relativeError, epsilon, x.Got, want))
}
return ""
}
// NotInEpsilon tests that the numbers have a relative error greater than epsilon.
func (x FluentNumber[T]) NotInEpsilon(want T, epsilon float64) FailureMessage {
relativeError, msg := x.calcRelativeError(want, epsilon)
if msg != "" {
return msg
}
if relativeError > epsilon {
return ""
}
return FailureMessage(fmt.Sprintf("relative error between numbers is lesser or equal than epsilon\nrelative error: %g\nepsilon: %g\ngot: %v\nto: %v", relativeError, epsilon, x.Got, want))
}
func (x FluentNumber[T]) calcRelativeError(want T, epsilon float64) (float64, FailureMessage) {
if math.IsNaN(epsilon) || epsilon < 0 {
return 0, "epsilon must be a non-negative number"
}
wantF := float64(want)
gotF := float64(x.Got)
if math.IsNaN(wantF) {
return 0, "want is NaN"
}
if math.IsNaN(gotF) {
return 0, "got is NaN"
}
if wantF == 0 {
return 0, "want must have a value other than zero to calculate the relative error"
}
relativeError := math.Abs(wantF-gotF) / math.Abs(wantF)
return relativeError, ""
}