diff options
| author | Shulhan <ms@kilabit.info> | 2024-12-28 23:27:58 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2024-12-29 00:06:17 +0700 |
| commit | ec3fac32072a396a02b699065cb091ab292b9381 (patch) | |
| tree | 88c14e9d35d18fe7ef9483c386aa346c6c8afa2d | |
| parent | 7a57091317cd86b9c88f4c6b5127c76bee0f6ede (diff) | |
| download | pakakeh.go-ec3fac32072a396a02b699065cb091ab292b9381.tar.xz | |
all: merge package "lib/floats64" into "slices"
Now that Go has type parameter, we can use it to use the same function
that accept different types for working with slice of float64.
| -rw-r--r-- | lib/floats64/floats64.go | 354 | ||||
| -rw-r--r-- | lib/floats64/floats64_bench_test.go | 18 | ||||
| -rw-r--r-- | lib/floats64/floats64_test.go | 375 | ||||
| -rw-r--r-- | lib/mining/classifier/crf/crf.go | 8 | ||||
| -rw-r--r-- | lib/mining/classifier/rf/rf.go | 7 | ||||
| -rw-r--r-- | lib/mining/classifier/runtime.go | 3 | ||||
| -rw-r--r-- | lib/mining/gain/gini/gini.go | 4 | ||||
| -rw-r--r-- | lib/mining/gain/gini/ginifloat.go | 10 | ||||
| -rw-r--r-- | lib/slices/benchmark_test.go | 22 | ||||
| -rw-r--r-- | lib/slices/slices_float64_test.go | 464 |
10 files changed, 500 insertions, 765 deletions
diff --git a/lib/floats64/floats64.go b/lib/floats64/floats64.go deleted file mode 100644 index 1781abb2..00000000 --- a/lib/floats64/floats64.go +++ /dev/null @@ -1,354 +0,0 @@ -// Copyright 2018, Shulhan <ms@kilabit.info>. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package floats64 provide a library for working with slice of 64 bit float. -package floats64 - -import "git.sr.ht/~shulhan/pakakeh.go/lib/slices" - -// Count number of class in data. -func Count(d []float64, class float64) (count int) { - if len(d) == 0 { - return - } - for x := 0; x < len(d); x++ { - if d[x] == class { - count++ - } - } - return -} - -// Counts count class in data and return each of the counter. -// -// For example, if data is "[1,1,2]" and class is "[1,2]", this function will -// return "[2,1]". -func Counts(d, classes []float64) (counts []int) { - if len(classes) == 0 { - return - } - - counts = make([]int, len(classes)) - - for x, c := range classes { - counts[x] = Count(d, c) - } - return -} - -// IndirectSort sort the data and return the sorted index. -func IndirectSort(d []float64, asc bool) (sortedIdx []int) { - dlen := len(d) - - sortedIdx = make([]int, dlen) - for i := 0; i < dlen; i++ { - sortedIdx[i] = i - } - - InplaceMergesort(d, sortedIdx, 0, dlen, asc) - - return -} - -// InplaceMergesort in-place merge-sort without memory allocation. -func InplaceMergesort(d []float64, idx []int, l, r int, asc bool) { - // (0) If data length == Threshold, then - if l+7 >= r { - // (0.1) use insertion sort. - InplaceInsertionSort(d, idx, l, r, asc) - return - } - - // (1) Divide into left and right. - res := (r + l) % 2 - c := (r + l) / 2 - if res == 1 { - c++ - } - - // (2) Sort left. - InplaceMergesort(d, idx, l, c, asc) - - // (3) Sort right. - InplaceMergesort(d, idx, c, r, asc) - - // (4) Merge sorted left and right. - if asc { - if d[c-1] <= d[c] { - // (4.1) If the last element of the left is lower then - // the first element of the right, i.e. [1 2] [3 4]; - // no merging needed, return immediately. - return - } - } else { - if d[c-1] >= d[c] { - return - } - } - - inplaceMerge(d, idx, l, c, r, asc) -} - -// InplaceInsertionSort will sort the data using insertion-sort algorithm. -// -// Parameters: -// `data` is slice that will be sorted, -// `idx` is indices of data, -// `l` is starting index of slice to be sorted, and -// `r` is end index of slice to be sorted. -func InplaceInsertionSort(d []float64, ids []int, l, r int, asc bool) { - for x := l; x < r; x++ { - for y := x + 1; y < r; y++ { - if asc { - if d[x] > d[y] { - slices.Swap(ids, x, y) - Swap(d, x, y) - } - } else { - if d[x] < d[y] { - slices.Swap(ids, x, y) - Swap(d, x, y) - } - } - } - } -} - -// IsExist will return true if value `v` exist in slice of `d`, -// otherwise it will return false. -func IsExist(d []float64, v float64) bool { - for _, x := range d { - if v == x { - return true - } - } - return false -} - -// Max find the maximum value in slice and and return its value and index. -// -// If data is empty, it will return false in ok. -// -// Example, given data: [0.0 0.1 0.2 0.2 0.4], it will return 0.4 as max and 4 -// as index of maximum value. -func Max(d []float64) (v float64, i int, ok bool) { - if len(d) == 0 { - return 0, 0, false - } - v = d[0] - i = 0 - for x := 1; x < len(d); x++ { - if d[x] > v { - v = d[x] - i = x - } - } - return v, i, true -} - -// MaxRange find the (last) maximum value in slice between index "l" and "r". -// -// WARNING: This function does not check index out of range. -func MaxRange(d []float64, l, r int) (m int) { - maxv := d[l] - m = l - for l++; l < r; l++ { - if d[l] >= maxv { - maxv = d[l] - m = l - } - } - return m -} - -// MaxCountOf will count number of occurrence of each element of -// classes in data and return the class with maximum count. -// -// If `classes` is empty, it will return -1 and false. -// If `data` is empty, it will return -2 and false. -// If classes has the same count value, then the first max in the class will be -// returned. -// -// For example, given a data [5, 6, 5, 6, 5] and classes [5, 6, 7], the -// function will count 5 as 3, 6 as 2, and 7 as 0. -// Since frequency of 5 is greater than 6 and 7, then it will return `5` and -// `true`. -func MaxCountOf(d, classes []float64) (float64, bool) { - if len(classes) == 0 { - return -1, false - } - if len(d) == 0 { - return -2, false - } - - counts := Counts(d, classes) - - _, maxi := slices.Max2(counts) - - return classes[maxi], true -} - -// Min find the minimum value in slice and and return it with their index. -// -// If data is empty, return 0 in value and index, and false in ok. -// -// Example, given data: [0.0 0.1 0.2 0.2 0.4], it will return 0 as min and 0 -// as index of minimum value. -func Min(d []float64) (v float64, i int, ok bool) { - if len(d) == 0 { - return 0, 0, false - } - v = d[0] - i = 0 - for x := 1; x < len(d); x++ { - if d[x] < v { - v = d[x] - i = x - } - } - return v, i, true -} - -// MinRange find the (last) minimum value in slice between index "l" and "r". -// -// WARNING: This function does not check index out of range. -func MinRange(d []float64, l, r int) (m int) { - min := d[l] - m = l - for l++; l < r; l++ { - if d[l] <= min { - min = d[l] - m = l - } - } - return -} - -// SortByIndex sort the slice of float using sorted index. -func SortByIndex(d *[]float64, sortedListID []int) { - newd := make([]float64, len(*d)) - - for i := range sortedListID { - newd[i] = (*d)[sortedListID[i]] - } - - (*d) = newd -} - -// Sum value of slice. -func Sum(d []float64) (sum float64) { - for x := 0; x < len(d); x++ { - sum += d[x] - } - return -} - -// Swap swap two indices value of 64bit float. -func Swap(d []float64, x, y int) { - if x == y || len(d) <= 1 || x > len(d) || y > len(d) { - return - } - d[x], d[y] = d[y], d[x] -} - -// Let `x` be the first index of left-side, `y` be the first index of -// the right-side, and `r` as length of slice `d` -func inplaceMerge(d []float64, idx []int, x, y, r int, asc bool) { - var ylast int - - // (4.3) Loop until either x or y reached the maximum slice. - for x < r && y < r { - // (4.3.1) IF DATA[x] <= DATA[y] - if asc { - if d[x] <= d[y] { - x++ - - // (4.3.1.2) IF x >= y THEN GOTO 4.3.4 - if x >= y { - goto next - } - - // (4.3.1.3) GOTO 4.3 - continue - } - } else { - if d[x] >= d[y] { - x++ - - if x >= y { - goto next - } - - continue - } - } - - // (4.3.2) LET YLAST := the next DATA[y] that is less DATA[x] - ylast = moveY(d, x, y, r, asc) - - // (4.3.3) SWAP DATA, X, Y, YLAST - multiswap(d, idx, x, y, ylast) - - next: - // (4.3.4) LET Y := the minimum value between x and r on `d` - minY(d, &x, &y, r, asc) - } -} - -// (4.3.4) LET Y := the minimum value between x and r on `d`. -func minY(d []float64, x, y *int, r int, asc bool) { - for *x < r { - if asc { - *y = MinRange(d, *x, r) - } else { - *y = MaxRange(d, *x, r) - } - - if *y != *x { - break - } - (*x)++ - } -} - -func moveY(d []float64, x, y, r int, asc bool) int { - yorg := y - y++ - for y < r { - if asc { - if d[y] >= d[x] { - break - } - if d[y] < d[yorg] { - break - } - } else { - if d[y] <= d[x] { - break - } - if d[y] > d[yorg] { - break - } - } - y++ - } - return y -} - -func multiswap(d []float64, idx []int, x, y, ylast int) int { - for y < ylast { - slices.Swap(idx, x, y) - Swap(d, x, y) - x++ - y++ - if y >= ylast { - return y - } - if d[x] <= d[y] { - return y - } - } - - return y -} diff --git a/lib/floats64/floats64_bench_test.go b/lib/floats64/floats64_bench_test.go deleted file mode 100644 index 2ca4547a..00000000 --- a/lib/floats64/floats64_bench_test.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2018, Shulhan <ms@kilabit.info>. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package floats64 - -import ( - "testing" -) - -func BenchmarkInplaceMergesort(b *testing.B) { - size := len(inSorts[6]) - ids := make([]int, size) - - for i := 0; i < b.N; i++ { - InplaceMergesort(inSorts[6], ids, 0, size, true) - } -} diff --git a/lib/floats64/floats64_test.go b/lib/floats64/floats64_test.go deleted file mode 100644 index 15d5e2c2..00000000 --- a/lib/floats64/floats64_test.go +++ /dev/null @@ -1,375 +0,0 @@ -// Copyright 2018, Shulhan <ms@kilabit.info>. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package floats64 - -import ( - "fmt" - "testing" - - "git.sr.ht/~shulhan/pakakeh.go/lib/numbers" - "git.sr.ht/~shulhan/pakakeh.go/lib/test" -) - -var ( - d = [][]float64{ - {}, - {0.5, 0.6, 0.7, 0.8, 0.9, 0.0, 0.1, 0.2, 0.3, 0.4}, - {0.0, 0.1, 0.0, 0.1, 0.0, 0.1, 0.0, 0.1, 0.0}, - {1, 1, 2, 2, 3, 1, 2}, - } - dSorted = [][]float64{ - {}, - {0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9}, - {0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.1, 0.1, 0.1}, - {1, 1, 1, 2, 2, 2, 3}, - } - dSortedDesc = [][]float64{ - {}, - {0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0}, - {0.1, 0.1, 0.1, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0}, - {3, 2, 2, 2, 1, 1, 1}, - } -) - -func TestMaxEmpty(t *testing.T) { - gotv, goti, gotok := Max(d[0]) - - test.Assert(t, "", float64(0), gotv) - test.Assert(t, "", 0, goti) - test.Assert(t, "", false, gotok) -} - -func TestMax(t *testing.T) { - gotv, goti, gotok := Max(d[1]) - - test.Assert(t, "", float64(0.9), gotv) - test.Assert(t, "", 4, goti) - test.Assert(t, "", true, gotok) -} - -func TestMinEmpty(t *testing.T) { - gotv, goti, gotok := Min(d[0]) - - test.Assert(t, "", gotv, float64(0)) - test.Assert(t, "", goti, 0) - test.Assert(t, "", gotok, false) -} - -func TestMin(t *testing.T) { - gotv, goti, gotok := Min(d[1]) - - test.Assert(t, "", gotv, float64(0.0)) - test.Assert(t, "", goti, 5) - test.Assert(t, "", gotok, true) -} - -func TestSum(t *testing.T) { - got := Sum(d[1]) - - test.Assert(t, "", float64(4.5), numbers.Float64Round(got, 1)) -} - -func TestCount(t *testing.T) { - got := Count(d[0], 0) - - test.Assert(t, "", 0, got) - - got = Count(d[1], 0.1) - - test.Assert(t, "", 1, got) - - got = Count(d[2], 0.1) - - test.Assert(t, "", 4, got) - - got = Count(d[3], 0.1) - - test.Assert(t, "", 0, got) - - got = Count(d[3], 3) - - test.Assert(t, "", 1, got) -} - -func TestCountsEmpty(t *testing.T) { - classes := []float64{1, 2, 3} - exp := []int{0, 0, 0} - - got := Counts(d[0], classes) - - test.Assert(t, "", exp, got) -} - -func TestCountsEmptyClasses(t *testing.T) { - classes := []float64{} - var exp []int - - got := Counts(d[1], classes) - - test.Assert(t, "", exp, got) -} - -func TestCounts(t *testing.T) { - classes := []float64{1, 2, 3} - exp := []int{3, 3, 1} - - got := Counts(d[3], classes) - - test.Assert(t, "", exp, got) -} - -func TestMaxCountOf(t *testing.T) { - classes := []float64{0, 1} - exp := float64(0) - got, _ := MaxCountOf(d[2], classes) - - test.Assert(t, "", exp, got) - - // Swap the class values. - classes = []float64{1, 0} - got, _ = MaxCountOf(d[2], classes) - - test.Assert(t, "", exp, got) -} - -func TestSwapEmpty(t *testing.T) { - exp := []float64{} - - Swap(d[0], 1, 6) - - test.Assert(t, "", exp, d[0]) -} - -func TestSwapEqual(t *testing.T) { - in := make([]float64, len(d[1])) - copy(in, d[1]) - - exp := make([]float64, len(in)) - copy(exp, in) - - Swap(in, 1, 1) - - test.Assert(t, "", exp, in) -} - -func TestSwapOutOfRange(t *testing.T) { - in := make([]float64, len(d[1])) - copy(in, d[1]) - - exp := make([]float64, len(in)) - copy(exp, in) - - Swap(in, 1, 100) - - test.Assert(t, "", exp, in) -} - -func TestSwap(t *testing.T) { - in := make([]float64, len(d[1])) - copy(in, d[1]) - - exp := make([]float64, len(in)) - copy(exp, in) - - Swap(in, 0, len(in)-1) - - exp[0], exp[len(exp)-1] = exp[len(exp)-1], exp[0] - - test.Assert(t, "", exp, in) -} - -func TestIsExist(t *testing.T) { - got := IsExist(d[0], 0) - - test.Assert(t, "", false, got) - - got = IsExist(d[1], float64(0)) - - test.Assert(t, "", true, got) - - got = IsExist(d[1], float64(0.01)) - - test.Assert(t, "", false, got) -} - -func TestInplaceInsertionSort(t *testing.T) { - for x := range d { - data := make([]float64, len(d[x])) - - copy(data, d[x]) - - ids := make([]int, len(data)) - for x := range ids { - ids[x] = x - } - - InplaceInsertionSort(data, ids, 0, len(ids), true) - - test.Assert(t, "", dSorted[x], data) - } -} - -func TestInplaceInsertionSortDesc(t *testing.T) { - for x := range d { - data := make([]float64, len(d[x])) - - copy(data, d[x]) - - ids := make([]int, len(data)) - for x := range ids { - ids[x] = x - } - - InplaceInsertionSort(data, ids, 0, len(ids), false) - - test.Assert(t, "", dSortedDesc[x], data) - } -} - -func TestSortByIndex(t *testing.T) { - ids := [][]int{ - {}, - {5, 6, 7, 8, 9, 0, 1, 2, 3, 4}, - {0, 2, 4, 6, 8, 1, 3, 5, 7}, - {0, 1, 5, 6, 2, 3, 4}, - } - - for x := range d { - data := make([]float64, len(d[x])) - - copy(data, d[x]) - - SortByIndex(&data, ids[x]) - - test.Assert(t, "", dSorted[x], data) - } -} - -var inSorts = [][]float64{ - {9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0}, - {9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.0}, - {0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}, - {0.0, 6.0, 7.0, 8.0, 5.0, 1.0, 2.0, 3.0, 4.0, 9.0}, - {9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {5.1, 5, 5.6, 5.5, 5.5, 5.8, 5.5, 5.5, 5.8, 5.6, - 5.7, 5, 5.6, 5.9, 6.2, 6, 4.9, 6.3, 6.1, 5.6, - 5.8, 6.7, 6.1, 5.9, 6, 4.9, 5.6, 5.2, 6.1, 6.4, - 7, 5.7, 6.5, 6.9, 5.7, 6.4, 6.2, 6.6, 6.3, 6.2, - 5.4, 6.7, 6.1, 5.7, 5.5, 6, 3, 6.6, 5.7, 6, - 6.8, 6, 6.1, 6.3, 5.8, 5.8, 5.6, 5.7, 6, 6.9, - 6.9, 6.4, 6.3, 6.3, 6.7, 6.5, 5.8, 6.3, 6.4, 6.7, - 5.9, 7.2, 6.3, 6.3, 6.5, 7.1, 6.7, 7.6, 7.3, 6.4, - 6.7, 7.4, 6, 6.8, 6.5, 6.4, 6.7, 6.4, 6.5, 6.9, - 7.7, 6.7, 7.2, 7.7, 7.2, 7.7, 6.1, 7.9, 7.7, 6.8, - 6.2}, -} - -var expSorts = [][]float64{ - {3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}, - {0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}, - {0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}, - {0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}, - {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {3, 4.9, 4.9, 5, 5, 5.1, 5.2, 5.4, 5.5, 5.5, - 5.5, 5.5, 5.5, 5.6, 5.6, 5.6, 5.6, 5.6, 5.6, 5.7, - 5.7, 5.7, 5.7, 5.7, 5.7, 5.8, 5.8, 5.8, 5.8, 5.8, - 5.8, 5.9, 5.9, 5.9, 6, 6, 6, 6, 6, 6, - 6, 6.1, 6.1, 6.1, 6.1, 6.1, 6.1, 6.2, 6.2, 6.2, - 6.2, 6.3, 6.3, 6.3, 6.3, 6.3, 6.3, 6.3, 6.3, 6.4, - 6.4, 6.4, 6.4, 6.4, 6.4, 6.4, 6.5, 6.5, 6.5, 6.5, - 6.5, 6.6, 6.6, 6.7, 6.7, 6.7, 6.7, 6.7, 6.7, 6.7, - 6.7, 6.8, 6.8, 6.8, 6.9, 6.9, 6.9, 6.9, 7, 7.1, - 7.2, 7.2, 7.2, 7.3, 7.4, 7.6, 7.7, 7.7, 7.7, 7.7, - 7.9}, -} - -var expSortsDesc = [][]float64{ - {9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0}, - {9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.0}, - {9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.0}, - {9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.0}, - {9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {7.9, 7.7, 7.7, 7.7, 7.7, 7.6, 7.4, 7.3, 7.2, 7.2, - 7.2, 7.1, 7, 6.9, 6.9, 6.9, 6.9, 6.8, 6.8, 6.8, - 6.7, 6.7, 6.7, 6.7, 6.7, 6.7, 6.7, 6.7, 6.6, 6.6, - 6.5, 6.5, 6.5, 6.5, 6.5, 6.4, 6.4, 6.4, 6.4, 6.4, - 6.4, 6.4, 6.3, 6.3, 6.3, 6.3, 6.3, 6.3, 6.3, 6.3, - 6.2, 6.2, 6.2, 6.2, 6.1, 6.1, 6.1, 6.1, 6.1, 6.1, - 6, 6, 6, 6, 6, 6, 6, 5.9, 5.9, 5.9, - 5.8, 5.8, 5.8, 5.8, 5.8, 5.8, 5.7, 5.7, 5.7, 5.7, - 5.7, 5.7, 5.6, 5.6, 5.6, 5.6, 5.6, 5.6, 5.5, 5.5, - 5.5, 5.5, 5.5, 5.4, 5.2, 5.1, 5, 5, 4.9, 4.9, 3}, -} - -func TestIndirectSort(t *testing.T) { - var res, exp string - - for i := range inSorts { - IndirectSort(inSorts[i], true) - - res = fmt.Sprint(inSorts[i]) - exp = fmt.Sprint(expSorts[i]) - - test.Assert(t, "", exp, res) - } -} - -func TestIndirectSortDesc(t *testing.T) { - var res, exp string - - for i := range inSorts { - IndirectSort(inSorts[i], false) - - res = fmt.Sprint(inSorts[i]) - exp = fmt.Sprint(expSortsDesc[i]) - - test.Assert(t, "", exp, res) - } -} - -func TestIndirectSort_Stability(t *testing.T) { - exp := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} - got := IndirectSort(inSorts[5], true) - - test.Assert(t, "", exp, got) -} - -func TestIndirectSortDesc_Stability(t *testing.T) { - exp := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} - got := IndirectSort(inSorts[5], false) - - test.Assert(t, "", exp, got) -} - -func TestInplaceMergesort(t *testing.T) { - size := len(inSorts[6]) - idx := make([]int, size) - - InplaceMergesort(inSorts[6], idx, 0, size, true) - - test.Assert(t, "", expSorts[6], inSorts[6]) -} - -func TestIndirectSort_SortByIndex(t *testing.T) { - expListID := []int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0} - in1 := []float64{9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.0} - in2 := []float64{0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0} - - exp := fmt.Sprint(in1) - - sortedListID := IndirectSort(in1, true) - - test.Assert(t, "", expListID, sortedListID) - - // Reverse the sort. - SortByIndex(&in2, sortedListID) - - got := fmt.Sprint(in2) - - test.Assert(t, "", exp, got) -} diff --git a/lib/mining/classifier/crf/crf.go b/lib/mining/classifier/crf/crf.go index 1ea93e00..ceb9b605 100644 --- a/lib/mining/classifier/crf/crf.go +++ b/lib/mining/classifier/crf/crf.go @@ -15,10 +15,10 @@ import ( "math" "sort" - "git.sr.ht/~shulhan/pakakeh.go/lib/floats64" "git.sr.ht/~shulhan/pakakeh.go/lib/mining/classifier" "git.sr.ht/~shulhan/pakakeh.go/lib/mining/classifier/rf" "git.sr.ht/~shulhan/pakakeh.go/lib/numbers" + "git.sr.ht/~shulhan/pakakeh.go/lib/slices" libstrings "git.sr.ht/~shulhan/pakakeh.go/lib/strings" "git.sr.ht/~shulhan/pakakeh.go/lib/tabula" ) @@ -368,7 +368,7 @@ func (crf *Runtime) ClassifySetByWeight(samples tabula.ClasetInterface, vs := samples.GetClassValueSpace() stageProbs := make([]float64, len(vs)) stageSumProbs := make([]float64, len(vs)) - sumWeights := floats64.Sum(crf.weights) + sumWeights := slices.Sum(crf.weights) // (1) rows := samples.GetDataAsRows() @@ -400,8 +400,8 @@ func (crf *Runtime) ClassifySetByWeight(samples tabula.ClasetInterface, } // (1.3) - _, maxi, ok := floats64.Max(stageProbs) - if ok { + _, maxi := slices.Max2(stageProbs) + if maxi >= 0 { predicts = append(predicts, vs[maxi]) } diff --git a/lib/mining/classifier/rf/rf.go b/lib/mining/classifier/rf/rf.go index 07da00ea..501080e4 100644 --- a/lib/mining/classifier/rf/rf.go +++ b/lib/mining/classifier/rf/rf.go @@ -16,9 +16,9 @@ import ( "math" "slices" - "git.sr.ht/~shulhan/pakakeh.go/lib/floats64" "git.sr.ht/~shulhan/pakakeh.go/lib/mining/classifier" "git.sr.ht/~shulhan/pakakeh.go/lib/mining/classifier/cart" + libslices "git.sr.ht/~shulhan/pakakeh.go/lib/slices" libstrings "git.sr.ht/~shulhan/pakakeh.go/lib/strings" "git.sr.ht/~shulhan/pakakeh.go/lib/tabula" ) @@ -275,9 +275,8 @@ func (forest *Runtime) ClassifySet(samples tabula.ClasetInterface, // (1.2) classProbs := libstrings.FrequencyOfTokens(votes, vs, false) - _, idx, ok := floats64.Max(classProbs) - - if ok { + _, idx := libslices.Max2(classProbs) + if idx >= 0 { predicts = append(predicts, vs[idx]) } diff --git a/lib/mining/classifier/runtime.go b/lib/mining/classifier/runtime.go index eeea1621..1d649788 100644 --- a/lib/mining/classifier/runtime.go +++ b/lib/mining/classifier/runtime.go @@ -9,7 +9,6 @@ import ( "math" "git.sr.ht/~shulhan/pakakeh.go/lib/dsv" - "git.sr.ht/~shulhan/pakakeh.go/lib/floats64" "git.sr.ht/~shulhan/pakakeh.go/lib/numbers" "git.sr.ht/~shulhan/pakakeh.go/lib/slices" libstrings "git.sr.ht/~shulhan/pakakeh.go/lib/strings" @@ -310,7 +309,7 @@ func (rt *Runtime) Performance(samples tabula.ClasetInterface, // (1) actuals := samples.GetClassAsStrings() sortedListID := numbers.IntCreateSeq(0, len(probs)-1) - floats64.InplaceMergesort(probs, sortedListID, 0, len(probs), false) + slices.InplaceMergesort(probs, sortedListID, 0, len(probs), false) // (2) libstrings.SortByIndex(&actuals, sortedListID) diff --git a/lib/mining/gain/gini/gini.go b/lib/mining/gain/gini/gini.go index 68995e28..58c7fd5a 100644 --- a/lib/mining/gain/gini/gini.go +++ b/lib/mining/gain/gini/gini.go @@ -11,7 +11,7 @@ package gini import ( "fmt" - "git.sr.ht/~shulhan/pakakeh.go/lib/floats64" + "git.sr.ht/~shulhan/pakakeh.go/lib/slices" libstrings "git.sr.ht/~shulhan/pakakeh.go/lib/strings" ) @@ -167,7 +167,7 @@ func (gini *Gini) ComputeContinu(src *[]float64, target, classes *[]string) { T2 := make([]string, len(*target)) copy(T2, *target) - gini.SortedIndex = floats64.IndirectSort(A2, true) + gini.SortedIndex = slices.IndirectSort(A2, true) // sort the target attribute using sorted index. libstrings.SortByIndex(&T2, gini.SortedIndex) diff --git a/lib/mining/gain/gini/ginifloat.go b/lib/mining/gain/gini/ginifloat.go index 522ca396..e7cf0e07 100644 --- a/lib/mining/gain/gini/ginifloat.go +++ b/lib/mining/gain/gini/ginifloat.go @@ -4,9 +4,7 @@ package gini -import ( - "git.sr.ht/~shulhan/pakakeh.go/lib/floats64" -) +import "git.sr.ht/~shulhan/pakakeh.go/lib/slices" // ComputeContinuFloat Given an attribute A and the target attribute T which contain // N classes in C, compute the information gain of A. @@ -23,10 +21,10 @@ import ( func (gini *Gini) ComputeContinuFloat(src, target, classes *[]float64) { gini.IsContinu = true - gini.SortedIndex = floats64.IndirectSort(*src, true) + gini.SortedIndex = slices.IndirectSort(*src, true) // (1) - floats64.SortByIndex(target, gini.SortedIndex) + slices.SortByIndex(target, gini.SortedIndex) // (2) gini.createContinuPartition(src) @@ -53,7 +51,7 @@ func (gini *Gini) computeFloat(target, classes *[]float64) float64 { return 0 } - classCount := floats64.Counts(*target, *classes) + classCount := slices.Counts(*target, *classes) var sump2 float64 diff --git a/lib/slices/benchmark_test.go b/lib/slices/benchmark_test.go index 923f9429..5bbde4e4 100644 --- a/lib/slices/benchmark_test.go +++ b/lib/slices/benchmark_test.go @@ -48,3 +48,25 @@ func BenchmarkSort_int(b *testing.B) { }) } + +func BenchmarkInplaceMergesort_float64(b *testing.B) { + var slice = []float64{ + 5.1, 5, 5.6, 5.5, 5.5, 5.8, 5.5, 5.5, 5.8, 5.6, + 5.7, 5, 5.6, 5.9, 6.2, 6, 4.9, 6.3, 6.1, 5.6, + 5.8, 6.7, 6.1, 5.9, 6, 4.9, 5.6, 5.2, 6.1, 6.4, + 7, 5.7, 6.5, 6.9, 5.7, 6.4, 6.2, 6.6, 6.3, 6.2, + 5.4, 6.7, 6.1, 5.7, 5.5, 6, 3, 6.6, 5.7, 6, + 6.8, 6, 6.1, 6.3, 5.8, 5.8, 5.6, 5.7, 6, 6.9, + 6.9, 6.4, 6.3, 6.3, 6.7, 6.5, 5.8, 6.3, 6.4, 6.7, + 5.9, 7.2, 6.3, 6.3, 6.5, 7.1, 6.7, 7.6, 7.3, 6.4, + 6.7, 7.4, 6, 6.8, 6.5, 6.4, 6.7, 6.4, 6.5, 6.9, + 7.7, 6.7, 7.2, 7.7, 7.2, 7.7, 6.1, 7.9, 7.7, 6.8, + 6.2, + } + var size = len(slice) + var ids = make([]int, size) + + for i := 0; i < b.N; i++ { + slices.InplaceMergesort(slice, ids, 0, size, true) + } +} diff --git a/lib/slices/slices_float64_test.go b/lib/slices/slices_float64_test.go new file mode 100644 index 00000000..c15a9170 --- /dev/null +++ b/lib/slices/slices_float64_test.go @@ -0,0 +1,464 @@ +// SPDX-FileCopyrightText: 2024 M. Shulhan <ms@kilabit.info> +// +// SPDX-License-Identifier: BSD-3-Clause + +package slices_test + +import ( + "fmt" + "testing" + + "git.sr.ht/~shulhan/pakakeh.go/lib/numbers" + "git.sr.ht/~shulhan/pakakeh.go/lib/slices" + "git.sr.ht/~shulhan/pakakeh.go/lib/test" +) + +func TestCount_float64(t *testing.T) { + var listCase = []struct { + desc string + slice []float64 + val float64 + exp int + }{{ + desc: `empty`, + exp: 0, + }, { + desc: `case 1`, + slice: []float64{0.5, 0.6, 0.7, 0.8, 0.9, 0.0, 0.1, 0.2, 0.3, 0.4}, + val: 0.1, + exp: 1, + }, { + slice: []float64{0.0, 0.1, 0.0, 0.1, 0.0, 0.1, 0.0, 0.1, 0.0}, + val: 0.1, + exp: 4, + }, { + slice: []float64{1, 1, 2, 2, 3, 1, 2}, + val: 0.1, + exp: 0, + }, { + slice: []float64{1, 1, 2, 2, 3, 1, 2}, + val: 3, + exp: 1, + }} + + for _, tcase := range listCase { + var got = slices.Count(tcase.slice, tcase.val) + test.Assert(t, tcase.desc, tcase.exp, got) + } +} + +func TestCounts_float64(t *testing.T) { + var listCase = []struct { + desc string + slice []float64 + classes []float64 + exp []int + }{{ + desc: `empty slice`, + classes: []float64{1, 2, 3}, + exp: []int{0, 0, 0}, + }, { + desc: `empty classes`, + slice: []float64{0.5, 0.6, 0.7, 0.8, 0.9, 0.0, 0.1, 0.2, 0.3, 0.4}, + }, { + desc: `ok`, + slice: []float64{1, 1, 2, 2, 3, 1, 2}, + classes: []float64{1, 2, 3}, + exp: []int{3, 3, 1}, + }} + + for _, tcase := range listCase { + var got = slices.Counts(tcase.slice, tcase.classes) + test.Assert(t, tcase.desc, tcase.exp, got) + } +} + +func TestIndirectSort_float64(t *testing.T) { + var listCase = []struct { + desc string + slice []float64 + expSortedIdx []int + exp []float64 + isAsc bool + }{{ + desc: `case 1 asc`, + slice: []float64{9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0}, + exp: []float64{3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}, + expSortedIdx: []int{6, 5, 4, 3, 2, 1, 0}, + isAsc: true, + }, { + desc: `case 1 desc`, + slice: []float64{9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0}, + exp: []float64{9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0}, + expSortedIdx: []int{0, 1, 2, 3, 4, 5, 6}, + isAsc: false, + }, { + desc: `case 2 asc`, + slice: []float64{9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.0}, + exp: []float64{0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}, + expSortedIdx: []int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}, + isAsc: true, + }, { + desc: `case 2 desc`, + slice: []float64{9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.0}, + exp: []float64{9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.0}, + expSortedIdx: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + isAsc: false, + }, { + desc: `case 3 asc`, + slice: []float64{0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}, + exp: []float64{0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}, + expSortedIdx: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + isAsc: true, + }, { + desc: `case 3 desc`, + slice: []float64{0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}, + exp: []float64{9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.0}, + expSortedIdx: []int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}, + isAsc: false, + }, { + desc: `case 4 asc`, + slice: []float64{9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0}, + exp: []float64{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}, + expSortedIdx: []int{8, 7, 6, 5, 4, 3, 2, 1, 0}, + isAsc: true, + }, { + desc: `case 4 desc`, + slice: []float64{9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0}, + exp: []float64{9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0}, + expSortedIdx: []int{0, 1, 2, 3, 4, 5, 6, 7, 8}, + isAsc: false, + }, { + desc: `case 5 stable asc`, + slice: []float64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + exp: []float64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + expSortedIdx: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + isAsc: true, + }, { + desc: `case 5 stable desc`, + slice: []float64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + exp: []float64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + expSortedIdx: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + isAsc: false, + }, { + desc: `case 6 asc`, + slice: []float64{ + 5.1, 5, 5.6, 5.5, 5.5, 5.8, 5.5, 5.5, 5.8, 5.6, + 5.7, 5, 5.6, 5.9, 6.2, 6, 4.9, 6.3, 6.1, 5.6, + 5.8, 6.7, 6.1, 5.9, 6, 4.9, 5.6, 5.2, 6.1, 6.4, + 7, 5.7, 6.5, 6.9, 5.7, 6.4, 6.2, 6.6, 6.3, 6.2, + 5.4, 6.7, 6.1, 5.7, 5.5, 6, 3, 6.6, 5.7, 6, + 6.8, 6, 6.1, 6.3, 5.8, 5.8, 5.6, 5.7, 6, 6.9, + 6.9, 6.4, 6.3, 6.3, 6.7, 6.5, 5.8, 6.3, 6.4, 6.7, + 5.9, 7.2, 6.3, 6.3, 6.5, 7.1, 6.7, 7.6, 7.3, 6.4, + 6.7, 7.4, 6, 6.8, 6.5, 6.4, 6.7, 6.4, 6.5, 6.9, + 7.7, 6.7, 7.2, 7.7, 7.2, 7.7, 6.1, 7.9, 7.7, 6.8, + 6.2, + }, + exp: []float64{ + 3, 4.9, 4.9, 5, 5, 5.1, 5.2, 5.4, 5.5, 5.5, + 5.5, 5.5, 5.5, 5.6, 5.6, 5.6, 5.6, 5.6, 5.6, 5.7, + 5.7, 5.7, 5.7, 5.7, 5.7, 5.8, 5.8, 5.8, 5.8, 5.8, + 5.8, 5.9, 5.9, 5.9, 6, 6, 6, 6, 6, 6, + 6, 6.1, 6.1, 6.1, 6.1, 6.1, 6.1, 6.2, 6.2, 6.2, + 6.2, 6.3, 6.3, 6.3, 6.3, 6.3, 6.3, 6.3, 6.3, 6.4, + 6.4, 6.4, 6.4, 6.4, 6.4, 6.4, 6.5, 6.5, 6.5, 6.5, + 6.5, 6.6, 6.6, 6.7, 6.7, 6.7, 6.7, 6.7, 6.7, 6.7, + 6.7, 6.8, 6.8, 6.8, 6.9, 6.9, 6.9, 6.9, 7, 7.1, + 7.2, 7.2, 7.2, 7.3, 7.4, 7.6, 7.7, 7.7, 7.7, 7.7, + 7.9, + }, + isAsc: true, + }, { + desc: `case 6 desc`, + slice: []float64{ + 5.1, 5, 5.6, 5.5, 5.5, 5.8, 5.5, 5.5, 5.8, 5.6, + 5.7, 5, 5.6, 5.9, 6.2, 6, 4.9, 6.3, 6.1, 5.6, + 5.8, 6.7, 6.1, 5.9, 6, 4.9, 5.6, 5.2, 6.1, 6.4, + 7, 5.7, 6.5, 6.9, 5.7, 6.4, 6.2, 6.6, 6.3, 6.2, + 5.4, 6.7, 6.1, 5.7, 5.5, 6, 3, 6.6, 5.7, 6, + 6.8, 6, 6.1, 6.3, 5.8, 5.8, 5.6, 5.7, 6, 6.9, + 6.9, 6.4, 6.3, 6.3, 6.7, 6.5, 5.8, 6.3, 6.4, 6.7, + 5.9, 7.2, 6.3, 6.3, 6.5, 7.1, 6.7, 7.6, 7.3, 6.4, + 6.7, 7.4, 6, 6.8, 6.5, 6.4, 6.7, 6.4, 6.5, 6.9, + 7.7, 6.7, 7.2, 7.7, 7.2, 7.7, 6.1, 7.9, 7.7, 6.8, + 6.2, + }, + exp: []float64{ + 7.9, 7.7, 7.7, 7.7, 7.7, 7.6, 7.4, 7.3, 7.2, 7.2, + 7.2, 7.1, 7, 6.9, 6.9, 6.9, 6.9, 6.8, 6.8, 6.8, + 6.7, 6.7, 6.7, 6.7, 6.7, 6.7, 6.7, 6.7, 6.6, 6.6, + 6.5, 6.5, 6.5, 6.5, 6.5, 6.4, 6.4, 6.4, 6.4, 6.4, + 6.4, 6.4, 6.3, 6.3, 6.3, 6.3, 6.3, 6.3, 6.3, 6.3, + 6.2, 6.2, 6.2, 6.2, 6.1, 6.1, 6.1, 6.1, 6.1, 6.1, + 6, 6, 6, 6, 6, 6, 6, 5.9, 5.9, 5.9, + 5.8, 5.8, 5.8, 5.8, 5.8, 5.8, 5.7, 5.7, 5.7, 5.7, + 5.7, 5.7, 5.6, 5.6, 5.6, 5.6, 5.6, 5.6, 5.5, 5.5, + 5.5, 5.5, 5.5, 5.4, 5.2, 5.1, 5, 5, 4.9, 4.9, + 3, + }, + isAsc: false, + }} + + for _, tcase := range listCase { + var gotSortedIdx = slices.IndirectSort(tcase.slice, tcase.isAsc) + test.Assert(t, tcase.desc, tcase.exp, tcase.slice) + + if len(tcase.expSortedIdx) == 0 { + continue + } + + var expSortedIdxStr = fmt.Sprint(tcase.expSortedIdx) + var gotSortedIdxStr = fmt.Sprint(gotSortedIdx) + test.Assert(t, tcase.desc+` sortedIdx`, expSortedIdxStr, + gotSortedIdxStr) + } +} + +func TestInplaceInsertionSort_float64(t *testing.T) { + var listCase = []struct { + desc string + slice []float64 + exp []float64 + isAsc bool + }{{ + desc: `case empty`, + }, { + desc: `case 1 asc`, + slice: []float64{0.5, 0.6, 0.7, 0.8, 0.9, 0.0, 0.1, 0.2, 0.3, 0.4}, + exp: []float64{0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9}, + isAsc: true, + }, { + desc: `case 1 desc`, + slice: []float64{0.5, 0.6, 0.7, 0.8, 0.9, 0.0, 0.1, 0.2, 0.3, 0.4}, + exp: []float64{0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0}, + isAsc: false, + }, { + desc: `case 2 asc`, + slice: []float64{0.0, 0.1, 0.0, 0.1, 0.0, 0.1, 0.0, 0.1, 0.0}, + exp: []float64{0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.1, 0.1, 0.1}, + isAsc: true, + }, { + desc: `case 2 desc`, + slice: []float64{0.0, 0.1, 0.0, 0.1, 0.0, 0.1, 0.0, 0.1, 0.0}, + exp: []float64{0.1, 0.1, 0.1, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0}, + isAsc: false, + }, { + desc: `case 3 asc`, + slice: []float64{1, 1, 2, 2, 3, 1, 2}, + exp: []float64{1, 1, 1, 2, 2, 2, 3}, + isAsc: true, + }, { + desc: `case 3 desc`, + slice: []float64{1, 1, 2, 2, 3, 1, 2}, + exp: []float64{3, 2, 2, 2, 1, 1, 1}, + isAsc: false, + }} + for _, tcase := range listCase { + var ids = make([]int, len(tcase.slice)) + slices.InplaceInsertionSort(tcase.slice, ids, 0, len(ids), + tcase.isAsc) + test.Assert(t, tcase.desc, tcase.exp, tcase.slice) + } +} + +func TestInplaceMergesort_float64(t *testing.T) { + var listCase = []struct { + desc string + slice []float64 + exp []float64 + }{{ + desc: `case 1`, + slice: []float64{ + 5.1, 5, 5.6, 5.5, 5.5, 5.8, 5.5, 5.5, 5.8, 5.6, + 5.7, 5, 5.6, 5.9, 6.2, 6, 4.9, 6.3, 6.1, 5.6, + 5.8, 6.7, 6.1, 5.9, 6, 4.9, 5.6, 5.2, 6.1, 6.4, + 7, 5.7, 6.5, 6.9, 5.7, 6.4, 6.2, 6.6, 6.3, 6.2, + 5.4, 6.7, 6.1, 5.7, 5.5, 6, 3, 6.6, 5.7, 6, + 6.8, 6, 6.1, 6.3, 5.8, 5.8, 5.6, 5.7, 6, 6.9, + 6.9, 6.4, 6.3, 6.3, 6.7, 6.5, 5.8, 6.3, 6.4, 6.7, + 5.9, 7.2, 6.3, 6.3, 6.5, 7.1, 6.7, 7.6, 7.3, 6.4, + 6.7, 7.4, 6, 6.8, 6.5, 6.4, 6.7, 6.4, 6.5, 6.9, + 7.7, 6.7, 7.2, 7.7, 7.2, 7.7, 6.1, 7.9, 7.7, 6.8, + 6.2, + }, + exp: []float64{ + 3, 4.9, 4.9, 5, 5, 5.1, 5.2, 5.4, 5.5, 5.5, + 5.5, 5.5, 5.5, 5.6, 5.6, 5.6, 5.6, 5.6, 5.6, 5.7, + 5.7, 5.7, 5.7, 5.7, 5.7, 5.8, 5.8, 5.8, 5.8, 5.8, + 5.8, 5.9, 5.9, 5.9, 6, 6, 6, 6, 6, 6, + 6, 6.1, 6.1, 6.1, 6.1, 6.1, 6.1, 6.2, 6.2, 6.2, + 6.2, 6.3, 6.3, 6.3, 6.3, 6.3, 6.3, 6.3, 6.3, 6.4, + 6.4, 6.4, 6.4, 6.4, 6.4, 6.4, 6.5, 6.5, 6.5, 6.5, + 6.5, 6.6, 6.6, 6.7, 6.7, 6.7, 6.7, 6.7, 6.7, 6.7, + 6.7, 6.8, 6.8, 6.8, 6.9, 6.9, 6.9, 6.9, 7, 7.1, + 7.2, 7.2, 7.2, 7.3, 7.4, 7.6, 7.7, 7.7, 7.7, 7.7, + 7.9, + }, + }} + + for _, tcase := range listCase { + var size = len(tcase.slice) + var idx = make([]int, size) + + slices.InplaceMergesort(tcase.slice, idx, 0, size, true) + test.Assert(t, tcase.desc, tcase.exp, tcase.slice) + } +} + +func TestMax_float64(t *testing.T) { + var listCase = []struct { + desc string + slice []float64 + exp float64 + expIdx int + }{{ + expIdx: -1, + }, { + slice: []float64{0.5, 0.6, 0.7, 0.8, 0.9, 0.0, 0.1, 0.2, 0.3, 0.4}, + exp: 0.9, + expIdx: 4, + }} + + for _, tcase := range listCase { + gotMax, gotIdx := slices.Max2(tcase.slice) + + test.Assert(t, tcase.desc+` max`, tcase.exp, gotMax) + test.Assert(t, tcase.desc+` idx`, tcase.expIdx, gotIdx) + } +} + +func TestMin_float64(t *testing.T) { + var listCase = []struct { + desc string + slice []float64 + exp float64 + expIdx int + }{{ + expIdx: -1, + }, { + slice: []float64{0.5, 0.6, 0.7, 0.8, 0.9, 0.0, 0.1, 0.2, 0.3, 0.4}, + exp: 0.0, + expIdx: 5, + }} + + for _, tcase := range listCase { + gotMin, gotIdx := slices.Min2(tcase.slice) + + test.Assert(t, tcase.desc+` min`, tcase.exp, gotMin) + test.Assert(t, tcase.desc+` idx`, tcase.expIdx, gotIdx) + } +} + +func TestMaxCountOf_float64(t *testing.T) { + var listCase = []struct { + desc string + slice []float64 + classes []float64 + exp float64 + state int + }{{ + desc: `case 1`, + slice: []float64{0.0, 0.1, 0.0, 0.1, 0.0, 0.1, 0.0, 0.1, 0.0}, + classes: []float64{0, 1}, + exp: 0, + state: 0, + }, { + desc: `case 2`, + slice: []float64{0.0, 0.1, 0.0, 0.1, 0.0, 0.1, 0.0, 0.1, 0.0}, + classes: []float64{1, 0}, + exp: 0, + }} + + for _, tcase := range listCase { + got, gotState := slices.MaxCountOf(tcase.slice, tcase.classes) + test.Assert(t, tcase.desc+` got`, tcase.exp, got) + test.Assert(t, tcase.desc+` state`, tcase.state, gotState) + } +} + +func TestSortByIndex_float64(t *testing.T) { + var listCase = []struct { + desc string + slice []float64 + ids []int + exp []float64 + }{{ + desc: `case empty`, + }, { + desc: `case 1`, + slice: []float64{0.5, 0.6, 0.7, 0.8, 0.9, 0.0, 0.1, 0.2, 0.3, 0.4}, + ids: []int{5, 6, 7, 8, 9, 0, 1, 2, 3, 4}, + exp: []float64{0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9}, + }, { + desc: `case 2`, + slice: []float64{0.0, 0.1, 0.0, 0.1, 0.0, 0.1, 0.0, 0.1, 0.0}, + ids: []int{0, 2, 4, 6, 8, 1, 3, 5, 7}, + exp: []float64{0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.1, 0.1, 0.1}, + }, { + desc: `case 3`, + slice: []float64{1, 1, 2, 2, 3, 1, 2}, + ids: []int{0, 1, 5, 6, 2, 3, 4}, + exp: []float64{1, 1, 1, 2, 2, 2, 3}, + }} + + for _, tcase := range listCase { + slices.SortByIndex(&tcase.slice, tcase.ids) + test.Assert(t, tcase.desc, tcase.exp, tcase.slice) + } +} + +func TestSum_float64(t *testing.T) { + var listCase = []struct { + desc string + slice []float64 + exp float64 + }{{ + desc: `case 1`, + slice: []float64{0.5, 0.6, 0.7, 0.8, 0.9, 0.0, 0.1, 0.2, 0.3, 0.4}, + exp: 4.5, + }} + + for _, tcase := range listCase { + var got = slices.Sum(tcase.slice) + got = numbers.Float64Round(got, 1) + test.Assert(t, tcase.desc, tcase.exp, got) + } +} + +func TestSwap_float64(t *testing.T) { + var listCase = []struct { + desc string + slice []float64 + exp []float64 + idx1 int + idx2 int + }{{ + desc: `empty`, + idx1: 1, + idx2: 6, + }, { + desc: `equal indices`, + slice: []float64{0.5, 0.6, 0.7, 0.8, 0.9, 0.0, 0.1, 0.2, 0.3, 0.4}, + idx1: 1, + idx2: 1, + exp: []float64{0.5, 0.6, 0.7, 0.8, 0.9, 0.0, 0.1, 0.2, 0.3, 0.4}, + }, { + desc: `out of range indices`, + slice: []float64{0.5, 0.6, 0.7, 0.8, 0.9, 0.0, 0.1, 0.2, 0.3, 0.4}, + idx1: 1, + idx2: 100, + exp: []float64{0.5, 0.6, 0.7, 0.8, 0.9, 0.0, 0.1, 0.2, 0.3, 0.4}, + }, { + desc: `case 1`, + slice: []float64{0.5, 0.6, 0.7, 0.8, 0.9, 0.0, 0.1, 0.2, 0.3, 0.4}, + idx1: 0, + idx2: 9, + exp: []float64{0.4, 0.6, 0.7, 0.8, 0.9, 0.0, 0.1, 0.2, 0.3, 0.5}, + }} + + for _, tcase := range listCase { + slices.Swap(tcase.slice, tcase.idx1, tcase.idx2) + test.Assert(t, tcase.desc, tcase.exp, tcase.slice) + } +} |
