diff --git a/.github/workflows/tag.yaml b/.github/workflows/tag.yaml index c0da535..073038f 100644 --- a/.github/workflows/tag.yaml +++ b/.github/workflows/tag.yaml @@ -39,5 +39,5 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} RELEASE_BRANCHES: main WITH_V: true - DEFAULT_BUMP: none + DEFAULT_BUMP: minor # PRERELEASE_SUFFIX: beta diff --git a/gows/ws.go b/gows/ws.go index d6c18cd..7926cd2 100644 --- a/gows/ws.go +++ b/gows/ws.go @@ -121,17 +121,37 @@ func (w *WS) getCDF(pdf touples) (cdf touples) { } func (w *WS) indexes(settings Settings, calculatedIndexes []int) (indexes []int) { - if settings == IgnoreIndexesForZeroPDF { - for _, v := range calculatedIndexes { - indexes = append(indexes, w.filteredPDFFromZeroElements[v].index) + if len(calculatedIndexes) == len(w.pdf) { + return calculatedIndexes + } + + indexes = w.toPDFIndexes(calculatedIndexes) + + switch settings { + case IgnoreIndexesForZeroPDF: + return indexes + case KeepIndexesForZeroPDF: + for i := 0; i < len(w.pdf); i++ { + apnd := true + for _, v := range calculatedIndexes { + if w.filteredPDFFromZeroElements[v].index == i { + apnd = false + break + } + } + if apnd { + indexes = append(indexes, i) + } } return indexes + default: + return make([]int, 0) } - for i := 0; i < len(w.pdf); i++ { - indexes = append(indexes, i) - } - for i, v := range calculatedIndexes { - indexes[i], indexes[w.filteredPDFFromZeroElements[v].index] = indexes[w.filteredPDFFromZeroElements[v].index], indexes[i] +} + +func (w *WS) toPDFIndexes(calculatedIndexes []int) (indexes []int) { + for _, v := range calculatedIndexes { + indexes = append(indexes, w.filteredPDFFromZeroElements[v].index) } return indexes } diff --git a/gows/ws_test.go b/gows/ws_test.go index 3629aab..bff444f 100644 --- a/gows/ws_test.go +++ b/gows/ws_test.go @@ -176,6 +176,7 @@ func TestSettings(t *testing.T) { {"0 0 50 0 0 50 0 0 - Ignore", []int{0, 0, 50, 0, 0, 50, 0, 0}, []int{2, 5}, IgnoreIndexesForZeroPDF}, {"0 0 50 0 0 50 0 0 - Keep", []int{0, 0, 50, 0, 0, 50, 0, 0}, []int{0, 1, 2, 3, 4, 5, 6, 7}, KeepIndexesForZeroPDF}, + {"invalid strategy", []int{100, 0}, []int{}, 5}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { @@ -189,6 +190,139 @@ func TestSettings(t *testing.T) { } } +func TestIsShuffling(t *testing.T) { + tests := []struct { + name string + pdf []int + same [][]int + maxDiffPercentage int + settings Settings + }{ + {"50-50", + []int{50, 50}, + [][]int{{0, 1}, {1, 0}}, + 10, + IgnoreIndexesForZeroPDF, + }, + {"33-33-34", + []int{33, 33, 34}, + [][]int{{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 1, 0}, {2, 0, 1}}, + 25, + IgnoreIndexesForZeroPDF, + }, + {"0-100", + []int{0, 100}, + [][]int{{1, 0}}, + 0, + IgnoreIndexesForZeroPDF, + }, + {"100-0-0", + []int{100, 0, 0}, + [][]int{{0, 1, 2}}, + 0, + IgnoreIndexesForZeroPDF, + }, + {"0-0-100", + []int{0, 0, 100}, + [][]int{{2, 0, 1}}, + 0, + IgnoreIndexesForZeroPDF, + }, + {"0-0-100-0", + []int{0, 0, 100, 0}, + [][]int{{2, 0, 1, 3}}, + 0, + IgnoreIndexesForZeroPDF, + }, + {"0-50-50", + []int{0, 50, 50}, + [][]int{{2, 1, 0}, {1, 2, 0}}, + 10, + IgnoreIndexesForZeroPDF, + }, + {"50-0-50", + []int{50, 0, 50}, + [][]int{{2, 0, 1}, {0, 2, 1}}, + 10, + IgnoreIndexesForZeroPDF, + }, + {"50-50-0", + []int{50, 50, 0}, + [][]int{{0, 1, 2}, {1, 0, 2}}, + 10, + IgnoreIndexesForZeroPDF, + }, + {"0-50-0-50-0", + []int{0, 50, 0, 50, 0}, + [][]int{{1, 3, 0, 2, 4}, {3, 1, 0, 2, 4}}, + 10, + IgnoreIndexesForZeroPDF, + }, + {"50-50-0-50-0", + []int{33, 33, 0, 34, 0}, + [][]int{{1, 3, 0, 2, 4}, {1, 0, 3, 2, 4}, {0, 1, 3, 2, 4}, {0, 3, 1, 2, 4}, {3, 0, 1, 2, 4}, {3, 1, 0, 2, 4}}, + 25, + IgnoreIndexesForZeroPDF, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + wrr, err := NewWS(test.pdf) + require.NoError(t, err) + counters := make([]int, len(test.same)) + for i := 0; i < 1000; i++ { + succeed := false + indexes := wrr.PickVector(KeepIndexesForZeroPDF) + for c, s := range test.same { + if same(s, indexes) { + counters[c]++ + succeed = true + } + } + if succeed { + continue + } + t.Fatal("invalid indexes", indexes) + } + assert.True(t, nearlyEqual(counters, test.maxDiffPercentage), + " %v are not nearly equal (expecting max %d%% diff)", + counters, test.maxDiffPercentage) + }) + } + +} + +func nearlyEqual(x []int, diff int) bool { + var r float64 + for i := 0; i < len(x); i++ { + r += float64(x[i]) + } + r = r / float64(len(x)) + da := r * float64(100-diff) / 100 + db := r * float64(100+diff) / 100 + for _, v := range x { + if float64(v) < da { + return false + } + if float64(v) > db { + return false + } + } + return true +} + +func same(a, b []int) bool { + if len(a) != len(b) { + return false + } + for i := 0; i < len(a); i++ { + if a[i] != b[i] { + return false + } + } + return true +} + // slice a contains same values as defined in slice b. // the values could be in different order but must be present in both slices func containsValues(a, b []int) bool {