|  | package vec32 | 
|  |  | 
|  | import ( | 
|  | "math" | 
|  | "testing" | 
|  |  | 
|  | "github.com/stretchr/testify/assert" | 
|  | ) | 
|  |  | 
|  | const ( | 
|  | e = MissingDataSentinel | 
|  | ) | 
|  |  | 
|  | func near(a, b float32) bool { | 
|  | return math.Abs(float64(a-b)) < 0.001 | 
|  | } | 
|  |  | 
|  | func vecNear(a, b []float32) bool { | 
|  | if len(a) != len(b) { | 
|  | return false | 
|  | } | 
|  | for i, x := range a { | 
|  | if !near(x, b[i]) { | 
|  | return false | 
|  | } | 
|  | } | 
|  | return true | 
|  | } | 
|  |  | 
|  | func TestNew(t *testing.T) { | 
|  | v := New(0) | 
|  | assert.Len(t, v, 0) | 
|  |  | 
|  | v = New(1) | 
|  | assert.Len(t, v, 1) | 
|  | assert.Equal(t, MissingDataSentinel, v[0]) | 
|  |  | 
|  | v = New(2) | 
|  | assert.Len(t, v, 2) | 
|  | assert.Equal(t, MissingDataSentinel, v[0]) | 
|  | assert.Equal(t, MissingDataSentinel, v[1]) | 
|  | } | 
|  |  | 
|  | func TestNorm(t *testing.T) { | 
|  | testCases := []struct { | 
|  | In  []float32 | 
|  | Out []float32 | 
|  | }{ | 
|  | { | 
|  | In:  []float32{1.0, -1.0, e}, | 
|  | Out: []float32{1.0, -1.0, e}, | 
|  | }, | 
|  | { | 
|  | In:  []float32{e, 2.0, -2.0}, | 
|  | Out: []float32{e, 1.0, -1.0}, | 
|  | }, | 
|  | { | 
|  | In:  []float32{e}, | 
|  | Out: []float32{e}, | 
|  | }, | 
|  | { | 
|  | In:  []float32{}, | 
|  | Out: []float32{}, | 
|  | }, | 
|  | { | 
|  | In:  []float32{2.0}, | 
|  | Out: []float32{0.0}, | 
|  | }, | 
|  | { | 
|  | In:  []float32{0.0, 0.1}, | 
|  | Out: []float32{-0.05, 0.05}, | 
|  | }, | 
|  | } | 
|  | for _, tc := range testCases { | 
|  | Norm(tc.In, 0.1) | 
|  | if got, want := tc.In, tc.Out; !vecNear(tc.Out, tc.In) { | 
|  | t.Errorf("Norm: Got %#v Want %#v", got, want) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestFill(t *testing.T) { | 
|  | testCases := []struct { | 
|  | In  []float32 | 
|  | Out []float32 | 
|  | }{ | 
|  | { | 
|  | In:  []float32{e, e, 2, 3, e, 5}, | 
|  | Out: []float32{2, 2, 2, 3, 5, 5}, | 
|  | }, | 
|  | { | 
|  | In:  []float32{e, 3, e}, | 
|  | Out: []float32{3, 3, 3}, | 
|  | }, | 
|  | { | 
|  | In:  []float32{e, e}, | 
|  | Out: []float32{0, 0}, | 
|  | }, | 
|  | { | 
|  | In:  []float32{e}, | 
|  | Out: []float32{0}, | 
|  | }, | 
|  | { | 
|  | In:  []float32{}, | 
|  | Out: []float32{}, | 
|  | }, | 
|  | } | 
|  | for _, tc := range testCases { | 
|  | Fill(tc.In) | 
|  | if got, want := tc.In, tc.Out; !vecNear(tc.Out, tc.In) { | 
|  | t.Errorf("Fill: Got %#v Want %#v", got, want) | 
|  | } | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | func TestFillAtErrors(t *testing.T) { | 
|  | testCases := []struct { | 
|  | Slice []float32 | 
|  | Idx   int | 
|  | }{ | 
|  | { | 
|  | Slice: []float32{e, e, 2, 3, e, 5}, | 
|  | Idx:   6, | 
|  | }, | 
|  | { | 
|  | Slice: []float32{}, | 
|  | Idx:   0, | 
|  | }, | 
|  | { | 
|  | Slice: []float32{4}, | 
|  | Idx:   -1, | 
|  | }, | 
|  | } | 
|  | for _, tc := range testCases { | 
|  | _, err := FillAt(tc.Slice, tc.Idx) | 
|  | if err == nil { | 
|  | t.Fatalf("Expected \"%v\" to fail FillAt.", tc) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestDup(t *testing.T) { | 
|  | a := []float32{1, 2, MissingDataSentinel, 0} | 
|  | b := Dup(a) | 
|  | assert.Equal(t, a, b) | 
|  | b[0] = 2 | 
|  | assert.NotEqual(t, a, b) | 
|  |  | 
|  | a = []float32{} | 
|  | b = Dup(a) | 
|  | assert.Equal(t, a, b) | 
|  | } | 
|  |  | 
|  | func TestMean_ReturnsZeroIfAllMissing(t *testing.T) { | 
|  |  | 
|  | assert.Equal(t, float32(1), Mean([]float32{1, 2, e, 0})) | 
|  | assert.Equal(t, float32(0), Mean([]float32{})) | 
|  | assert.Equal(t, float32(0), Mean([]float32{e})) | 
|  | assert.Equal(t, float32(0), Mean([]float32{e, e})) | 
|  | assert.Equal(t, float32(3), Mean([]float32{1, 5})) | 
|  | } | 
|  |  | 
|  | func TestMeanE_ReturnsMissingIfAllMissing(t *testing.T) { | 
|  |  | 
|  | assert.Equal(t, float32(1), MeanE([]float32{1, 2, e, 0})) | 
|  | assert.Equal(t, float32(e), MeanE([]float32{})) | 
|  | assert.Equal(t, float32(e), MeanE([]float32{e})) | 
|  | assert.Equal(t, float32(e), MeanE([]float32{e, e})) | 
|  | assert.Equal(t, float32(3), MeanE([]float32{1, 5})) | 
|  | } | 
|  |  | 
|  | func TestSSE(t *testing.T) { | 
|  | testCases := []struct { | 
|  | Slice []float32 | 
|  | Base  float32 | 
|  | SSE   float32 | 
|  | }{ | 
|  | { | 
|  | Slice: []float32{1, 1, e, 0}, | 
|  | Base:  0.0, | 
|  | SSE:   2.0, | 
|  | }, | 
|  | { | 
|  | Slice: []float32{1, 1, e, 0}, | 
|  | Base:  1.0, | 
|  | SSE:   1.0, | 
|  | }, | 
|  | { | 
|  | Slice: []float32{}, | 
|  | Base:  1.0, | 
|  | SSE:   0.0, | 
|  | }, | 
|  | { | 
|  | Slice: []float32{e}, | 
|  | Base:  3.0, | 
|  | SSE:   0.0, | 
|  | }, | 
|  | } | 
|  | for _, tc := range testCases { | 
|  | if got, want := SSE(tc.Slice, tc.Base), tc.SSE; !near(got, want) { | 
|  | t.Errorf("SSE(%v, %f) Got %v Want %v", tc.Slice, tc.Base, got, want) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestFillMeanMissing(t *testing.T) { | 
|  | testCases := []struct { | 
|  | Slice []float32 | 
|  | Mean  []float32 | 
|  | }{ | 
|  | { | 
|  | Slice: []float32{1, 2, e, 0}, | 
|  | Mean:  []float32{1.0, 1.0, 1.0, 1.0}, | 
|  | }, | 
|  | { | 
|  | Slice: []float32{e, e, e, e}, | 
|  | Mean:  []float32{e, e, e, e}, | 
|  | }, | 
|  | { | 
|  | Slice: []float32{e}, | 
|  | Mean:  []float32{e}, | 
|  | }, | 
|  | { | 
|  | Slice: []float32{}, | 
|  | Mean:  []float32{}, | 
|  | }, | 
|  | { | 
|  | Slice: []float32{2.0}, | 
|  | Mean:  []float32{2.0}, | 
|  | }, | 
|  | } | 
|  | for _, tc := range testCases { | 
|  | v := Dup(tc.Slice) | 
|  | FillMeanMissing(v) | 
|  | if got, want := v, tc.Mean; !vecNear(got, want) { | 
|  | t.Errorf("Mean(%v) Got %v Want %v", tc.Slice, got, want) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestFillStdDev(t *testing.T) { | 
|  | testCases := []struct { | 
|  | Slice []float32 | 
|  | Mean  []float32 | 
|  | }{ | 
|  | { | 
|  | Slice: []float32{0, 1, 4, 9}, | 
|  | Mean:  []float32{3.5, 3.5, 3.5, 3.5}, | 
|  | }, | 
|  | { | 
|  | Slice: []float32{e, e, e, e}, | 
|  | Mean:  []float32{e, e, e, e}, | 
|  | }, | 
|  | { | 
|  | Slice: []float32{e}, | 
|  | Mean:  []float32{e}, | 
|  | }, | 
|  | { | 
|  | Slice: []float32{}, | 
|  | Mean:  []float32{}, | 
|  | }, | 
|  | { | 
|  | Slice: []float32{2.0}, | 
|  | Mean:  []float32{0.0}, | 
|  | }, | 
|  | } | 
|  | for _, tc := range testCases { | 
|  | v := Dup(tc.Slice) | 
|  | FillStdDev(v) | 
|  | if got, want := v, tc.Mean; !vecNear(got, want) { | 
|  | t.Errorf("Mean(%v) Got %v Want %v", tc.Slice, got, want) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestFillCov(t *testing.T) { | 
|  | testCases := []struct { | 
|  | Slice []float32 | 
|  | Mean  []float32 | 
|  | }{ | 
|  | { | 
|  | Slice: []float32{0, 1, 4, 9}, | 
|  | Mean:  []float32{1, 1, 1, 1}, | 
|  | }, | 
|  | { | 
|  | Slice: []float32{e, e, e, e}, | 
|  | Mean:  []float32{e, e, e, e}, | 
|  | }, | 
|  | { | 
|  | Slice: []float32{e}, | 
|  | Mean:  []float32{e}, | 
|  | }, | 
|  | { | 
|  | Slice: []float32{}, | 
|  | Mean:  []float32{}, | 
|  | }, | 
|  | { | 
|  | Slice: []float32{2.0}, | 
|  | Mean:  []float32{0.0}, | 
|  | }, | 
|  | } | 
|  | for _, tc := range testCases { | 
|  | v := Dup(tc.Slice) | 
|  | FillCov(v) | 
|  | if got, want := v, tc.Mean; !vecNear(got, want) { | 
|  | t.Errorf("Mean(%v) Got %v Want %v", tc.Slice, got, want) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestScaleBy(t *testing.T) { | 
|  | testCases := []struct { | 
|  | Slice    []float32 | 
|  | Scale    float32 | 
|  | Expected []float32 | 
|  | }{ | 
|  | { | 
|  | Slice:    []float32{e, 0, 2, 3}, | 
|  | Scale:    math.SmallestNonzeroFloat32, | 
|  | Expected: []float32{e, 0, e, e}, | 
|  | }, | 
|  | { | 
|  | Slice:    []float32{e, 0, -1, 2}, | 
|  | Scale:    0, | 
|  | Expected: []float32{e, e, e, e}, | 
|  | }, | 
|  | { | 
|  | Slice:    []float32{e, 0, -2, 2}, | 
|  | Scale:    2, | 
|  | Expected: []float32{e, 0, -1, 1}, | 
|  | }, | 
|  | } | 
|  | for _, tc := range testCases { | 
|  | v := Dup(tc.Slice) | 
|  | ScaleBy(v, tc.Scale) | 
|  | if got, want := v, tc.Expected; !vecNear(got, want) { | 
|  | t.Errorf("Mean(%v) Got %v Want %v", tc.Slice, got, want) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestFillStep(t *testing.T) { | 
|  | testCases := []struct { | 
|  | Slice []float32 | 
|  | Step  []float32 | 
|  | }{ | 
|  | { | 
|  | Slice: []float32{1, 1, 2, 2, 2}, | 
|  | Step:  []float32{0.5, 0.5, 0.5, 0.5, 0.5}, | 
|  | }, | 
|  | { | 
|  | Slice: []float32{1, 1, 0, 0, 0}, | 
|  | Step:  []float32{e, e, e, e, e}, | 
|  | }, | 
|  | { | 
|  | Slice: []float32{3, 5, 2, 2, 2}, | 
|  | Step:  []float32{2, 2, 2, 2, 2}, | 
|  | }, | 
|  | { | 
|  | Slice: []float32{3, 5, e, 2, 2}, | 
|  | Step:  []float32{2, 2, 2, 2, 2}, | 
|  | }, | 
|  | { | 
|  | Slice: []float32{3, 5, e, e, 2}, | 
|  | Step:  []float32{2, 2, 2, 2, 2}, | 
|  | }, | 
|  | { | 
|  | Slice: []float32{4, e, e, e, 2}, | 
|  | Step:  []float32{2, 2, 2, 2, 2}, | 
|  | }, | 
|  | { | 
|  | Slice: []float32{3, 5, e, e, e}, | 
|  | Step:  []float32{e, e, e, e, e}, | 
|  | }, | 
|  | { | 
|  | Slice: []float32{e, e, e, e}, | 
|  | Step:  []float32{e, e, e, e}, | 
|  | }, | 
|  | { | 
|  | Slice: []float32{e}, | 
|  | Step:  []float32{e}, | 
|  | }, | 
|  | { | 
|  | Slice: []float32{}, | 
|  | Step:  []float32{}, | 
|  | }, | 
|  | { | 
|  | Slice: []float32{1.0}, | 
|  | Step:  []float32{e}, | 
|  | }, | 
|  | } | 
|  | for _, tc := range testCases { | 
|  | v := Dup(tc.Slice) | 
|  | FillStep(v) | 
|  | if got, want := v, tc.Step; !vecNear(got, want) { | 
|  | t.Errorf("Mean(%v) Got %v Want %v", tc.Slice, got, want) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestStdDev(t *testing.T) { | 
|  | type args struct { | 
|  | xs   []float32 | 
|  | base float32 | 
|  | } | 
|  | tests := []struct { | 
|  | name string | 
|  | args args | 
|  | want float32 | 
|  | }{ | 
|  | { | 
|  | name: "zero length", | 
|  | args: args{ | 
|  | xs:   []float32{}, | 
|  | base: 0, | 
|  | }, | 
|  | want: 0, | 
|  | }, | 
|  | { | 
|  | name: "length one", | 
|  | args: args{ | 
|  | xs:   []float32{1}, | 
|  | base: 1, | 
|  | }, | 
|  | want: 0, | 
|  | }, | 
|  | { | 
|  | name: "length two", | 
|  | args: args{ | 
|  | xs:   []float32{1, 1}, | 
|  | base: 1, | 
|  | }, | 
|  | want: 0, | 
|  | }, | 
|  | { | 
|  | name: "length two off base", | 
|  | args: args{ | 
|  | xs:   []float32{1, 1}, | 
|  | base: 0, | 
|  | }, | 
|  | want: math.Sqrt2, | 
|  | }, | 
|  | { | 
|  | name: "length two off base", | 
|  | args: args{ | 
|  | xs:   []float32{1, 1}, | 
|  | base: 0, | 
|  | }, | 
|  | want: math.Sqrt2, | 
|  | }, | 
|  | { | 
|  | name: "length two off base after removing sentinels", | 
|  | args: args{ | 
|  | xs:   []float32{1, e, 1}, | 
|  | base: 0, | 
|  | }, | 
|  | want: math.Sqrt2, | 
|  | }, | 
|  | { | 
|  | name: "length two with negatives off base after removing sentinels", | 
|  | args: args{ | 
|  | xs:   []float32{1, e, -1}, | 
|  | base: 0, | 
|  | }, | 
|  | want: math.Sqrt2, | 
|  | }, | 
|  | } | 
|  | for _, tt := range tests { | 
|  | t.Run(tt.name, func(t *testing.T) { | 
|  | if got := StdDev(tt.args.xs, tt.args.base); got != tt.want { | 
|  | t.Errorf("StdDev() = %v, want %v", got, tt.want) | 
|  | } | 
|  | }) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestTwoSidedStdDev(t *testing.T) { | 
|  | tests := []struct { | 
|  | name         string | 
|  | arr          []float32 | 
|  | median       float32 | 
|  | lower, upper float32 | 
|  | hasError     bool | 
|  | }{ | 
|  | { | 
|  | "EmptyArray_Error", | 
|  | []float32{}, | 
|  | 0, | 
|  | 0, 0, | 
|  | true, | 
|  | }, | 
|  | { | 
|  | "InsufficientNonMissingData_Error", | 
|  | []float32{e, 1, 1, 2}, | 
|  | 0, | 
|  | 0, 0, | 
|  | true, | 
|  | }, | 
|  | { | 
|  | "ConstantArray_ZeroStdDevs", | 
|  | []float32{1, 1, 1, 1}, | 
|  | 1, | 
|  | 0, 0, | 
|  | false, | 
|  | }, | 
|  | { | 
|  | "ConfirmSorting_UpperHasNonZeroStdDev", | 
|  | []float32{1, 2, 1, 1}, | 
|  | 1, | 
|  | 0, 1, | 
|  | false, | 
|  | }, | 
|  | { | 
|  | "MissingDataValuesAreIgnored", | 
|  | []float32{1, 2, 1, 1, e, e, e}, | 
|  | 1, | 
|  | 0, 1, | 
|  | false, | 
|  | }, | 
|  | { | 
|  | "AnOddNumberOfValuesInArray", | 
|  | []float32{2, 2, 1, 1, 1}, | 
|  | 1, | 
|  | 0, 1, | 
|  | false, | 
|  | }, | 
|  | } | 
|  | for _, tt := range tests { | 
|  | t.Run(tt.name, func(t *testing.T) { | 
|  | median, lower, upper, err := TwoSidedStdDev(tt.arr) | 
|  | if tt.hasError { | 
|  | assert.Error(t, err) | 
|  | } else { | 
|  | assert.NoError(t, err) | 
|  | } | 
|  | assert.Equal(t, tt.median, median, "median") | 
|  | assert.Equal(t, tt.lower, lower, "lower") | 
|  | assert.Equal(t, tt.upper, upper, "upper") | 
|  | }) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestRemoveMissingDataSentinel_Success(t *testing.T) { | 
|  |  | 
|  | assert.Equal(t, []float32{1, 2}, RemoveMissingDataSentinel([]float32{e, 1, e, 2, e})) | 
|  | } | 
|  |  | 
|  | func TestRemoveMissingDataSentinel_Empty_Success(t *testing.T) { | 
|  |  | 
|  | assert.Equal(t, []float32{}, RemoveMissingDataSentinel([]float32{})) | 
|  | } | 
|  |  | 
|  | func TestStdDevRatio(t *testing.T) { | 
|  |  | 
|  | tests := []struct { | 
|  | name          string | 
|  | arg           []float32 | 
|  | stddevRatio   float32 | 
|  | median        float32 | 
|  | lower         float32 | 
|  | upper         float32 | 
|  | wantError     bool | 
|  | errorContains string | 
|  | }{ | 
|  | { | 
|  | name:          "Too little data, needs at least 5 data points", | 
|  | arg:           []float32{0, 0, 0, 0}, | 
|  | wantError:     true, | 
|  | errorContains: "Insufficient", | 
|  | }, | 
|  | { | 
|  | name:          "Too little data after removing MissingDataSentinels", | 
|  | arg:           []float32{e, e, e, e, e, e, 1.0}, | 
|  | wantError:     true, | 
|  | errorContains: "Insufficient", | 
|  | }, | 
|  | { | 
|  | name:          "Catches NaN results", | 
|  | arg:           []float32{0, 0, 0, 0, 0}, | 
|  | wantError:     true, | 
|  | errorContains: "NaN", | 
|  | }, | 
|  | { | 
|  | name:          "Catches MissingDataSentinel as last point", | 
|  | arg:           []float32{0, 0, 0, 0, e}, | 
|  | wantError:     true, | 
|  | errorContains: "MissingDataSentinel", | 
|  | }, | 
|  |  | 
|  | { | 
|  | name:        "-Inf result is forced to -maxStdDevRatio", | 
|  | arg:         []float32{1, 1, 1, 1, -1.1}, | 
|  | stddevRatio: -maxStdDevRatio, | 
|  | median:      1, | 
|  | lower:       0, | 
|  | upper:       0, | 
|  | }, | 
|  | { | 
|  | name:        "Inf result is forced to -maxStdDevRatio", | 
|  | arg:         []float32{1, 1, 1, 1, 1.1}, | 
|  | stddevRatio: maxStdDevRatio, | 
|  | median:      1, | 
|  | lower:       0, | 
|  | upper:       0, | 
|  | }, | 
|  | { | 
|  | name:        "Last point equals the median", | 
|  | arg:         []float32{1, 2, 4, 5, 3}, | 
|  | stddevRatio: 0, | 
|  | median:      3, | 
|  | lower:       2.236068, | 
|  | upper:       2.236068, | 
|  | }, | 
|  | { | 
|  | name:        "Last point equals the twice the median", | 
|  | arg:         []float32{11, 12, 13, 14, 15, 16, 17, 20}, | 
|  | stddevRatio: 2.7774603, | 
|  | median:      14, | 
|  | lower:       2.6457512, | 
|  | upper:       2.1602468, | 
|  | }, | 
|  | } | 
|  | for _, tt := range tests { | 
|  | t.Run(tt.name, func(t *testing.T) { | 
|  | stddevRatio, median, lower, upper, err := StdDevRatio(tt.arg) | 
|  | if tt.wantError { | 
|  | assert.Error(t, err) | 
|  | assert.Contains(t, err.Error(), tt.errorContains) | 
|  | return | 
|  | } | 
|  | assert.Equal(t, tt.stddevRatio, stddevRatio, "stddevRatio") | 
|  | assert.Equal(t, tt.median, median, "median") | 
|  | assert.Equal(t, tt.lower, lower, "lower") | 
|  | assert.Equal(t, tt.upper, upper, "upper") | 
|  | }) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestToFloat64(t *testing.T) { | 
|  | assert.Equal(t, []float64{}, ToFloat64(nil)) | 
|  | assert.Equal(t, []float64{}, ToFloat64([]float32{})) | 
|  | assert.Equal(t, []float64{1.0, 2.0}, ToFloat64([]float32{1.0, 2.0})) | 
|  | } | 
|  |  | 
|  | func TestIQRR(t *testing.T) { | 
|  | tests := []struct { | 
|  | name     string | 
|  | args     []float32 | 
|  | expected []float32 | 
|  | }{ | 
|  | { | 
|  | name:     "Handles empty arrays.", | 
|  | args:     []float32{}, | 
|  | expected: []float32{}, | 
|  | }, | 
|  | { | 
|  | name:     "Handles arrays with NaN for quartile values.", | 
|  | args:     []float32{1}, | 
|  | expected: []float32{1}, | 
|  | }, | 
|  | { | 
|  | name:     "Handles missing data sentinels.", | 
|  | args:     []float32{e}, | 
|  | expected: []float32{e}, | 
|  | }, | 
|  | { | 
|  | name:     "Handles arrays with Inf values.", | 
|  | args:     []float32{float32(math.Inf(0))}, | 
|  | expected: []float32{float32(math.Inf(0))}, | 
|  | }, | 
|  | { | 
|  | name:     "Outliers are removed", | 
|  | args:     []float32{5, 7, 10, 15, 19, 21, 21, 22, 22, 23, 23, 23, 23, 23, 24, 24, 24, 24, 25}, | 
|  | expected: []float32{e, e, e, 15, 19, 21, 21, 22, 22, 23, 23, 23, 23, 23, 24, 24, 24, 24, 25}, | 
|  | }, | 
|  | { | 
|  | name:     "Outliers are removed even in the presence of missing data sentinels", | 
|  | args:     []float32{5, 7, 10, 15, 19, 21, 21, 22, 22, e, 23, 23, 23, 23, 23, 24, 24, 24, 24, e, 25}, | 
|  | expected: []float32{e, e, e, 15, 19, 21, 21, 22, 22, e, 23, 23, 23, 23, 23, 24, 24, 24, 24, e, 25}, | 
|  | }, | 
|  | } | 
|  | for _, tt := range tests { | 
|  | t.Run(tt.name, func(t *testing.T) { | 
|  | IQRR(tt.args) | 
|  | assert.Equal(t, tt.expected, tt.args) | 
|  | }) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestSum(t *testing.T) { | 
|  | assert.Equal(t, float32(0), Sum(nil)) | 
|  | assert.Equal(t, float32(0), Sum([]float32{e})) | 
|  | assert.Equal(t, float32(3), Sum([]float32{e, 1, 2})) | 
|  | } | 
|  |  | 
|  | func TestGeo_ReturnsZeroIfAllMissing(t *testing.T) { | 
|  | assert.Equal(t, float32(4), Geo([]float32{2, 8}), "4 = sqrt(2*8)") | 
|  | assert.Equal(t, float32(0), Geo([]float32{-2, -8}), "Return 0 on all ignored.") | 
|  | assert.Equal(t, float32(8), Geo([]float32{-2, 8}), "Ignore negative numbers.") | 
|  | assert.Equal(t, float32(2), Geo([]float32{2, e}), "Ingore MissingDataSentinels.") | 
|  | assert.Equal(t, float32(0), Geo([]float32{}), "Return 0 on empty vector.") | 
|  | } | 
|  |  | 
|  | func TestGeoE_ReturnsMissingIfAllMissing(t *testing.T) { | 
|  | assert.Equal(t, float32(4), GeoE([]float32{2, 8}), "4 = sqrt(2*8)") | 
|  | assert.Equal(t, float32(e), GeoE([]float32{-2, -8}), "Return 0 on all ignored.") | 
|  | assert.Equal(t, float32(8), GeoE([]float32{-2, 8}), "Ignore negative numbers.") | 
|  | assert.Equal(t, float32(2), GeoE([]float32{2, e}), "Ingore MissingDataSentinels.") | 
|  | assert.Equal(t, float32(e), GeoE([]float32{}), "Return 0 on empty vector.") | 
|  | } | 
|  |  | 
|  | func TestCount(t *testing.T) { | 
|  | assert.Equal(t, float32(0), Count([]float32{}), "Empty returns 0.") | 
|  | assert.Equal(t, float32(0), Count([]float32{e}), "MissingDataSentinels are ignored") | 
|  | assert.Equal(t, float32(2), Count([]float32{1, 3}), "Counts all non-MissingDataSentinels values") | 
|  | assert.Equal(t, float32(2), Count([]float32{1, e, 3}), "Ingores MissingDataSentinels") | 
|  | } | 
|  |  | 
|  | func TestMin(t *testing.T) { | 
|  | assert.Equal(t, float32(math.MaxFloat32), Min([]float32{})) | 
|  | assert.Equal(t, float32(math.MaxFloat32), Min([]float32{e})) | 
|  | assert.Equal(t, float32(2), Min([]float32{2})) | 
|  | assert.Equal(t, float32(3), Min([]float32{5, e, 3})) | 
|  | } | 
|  |  | 
|  | func TestMax(t *testing.T) { | 
|  | assert.Equal(t, float32(-math.MaxFloat32), Max([]float32{})) | 
|  | assert.Equal(t, float32(-math.MaxFloat32), Max([]float32{e})) | 
|  | assert.Equal(t, float32(2), Max([]float32{2})) | 
|  | assert.Equal(t, float32(5), Max([]float32{5, e, 3})) | 
|  | } |