From b326b02e0fc6ff0195c59a13d946dda082d39786 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Romain=20Bouy=C3=A9?= Date: Mon, 6 May 2024 20:01:58 +0200 Subject: [PATCH] Fix #457 `utils.MaxSlice` and `utils.MinSlice` and add tests for slices utils Co-authored-by: Jean-Philippe Bossuat --- core/rlwe/utils.go | 2 +- .../reals_bootstrapping/slim/main.go | 4 +- utils/slices.go | 26 +++-- utils/slices_test.go | 95 +++++++++++++++++++ utils/utils.go | 2 +- 5 files changed, 117 insertions(+), 12 deletions(-) create mode 100644 utils/slices_test.go diff --git a/core/rlwe/utils.go b/core/rlwe/utils.go index 749258071..09b1ee371 100644 --- a/core/rlwe/utils.go +++ b/core/rlwe/utils.go @@ -45,7 +45,7 @@ func NoiseGaloisKey(gk *GaloisKey, sk *SecretKey, params Parameters) float64 { return NoiseEvaluationKey(&gk.EvaluationKey, skIn, skOut, params) } -// NoiseGadgetCiphertext returns the log2 of the standard devaition of the noise of the input gadget ciphertext with respect to the given plaintext, secret-key and parameters. +// NoiseGadgetCiphertext returns the log2 of the standard deviation of the noise of the input gadget ciphertext with respect to the given plaintext, secret-key and parameters. // The polynomial pt is expected to be in the NTT and Montgomery domain. func NoiseGadgetCiphertext(gct *GadgetCiphertext, pt ring.Poly, sk *SecretKey, params Parameters) float64 { diff --git a/examples/single_party/applications/reals_bootstrapping/slim/main.go b/examples/single_party/applications/reals_bootstrapping/slim/main.go index 8b6bdc515..2f4e4178d 100644 --- a/examples/single_party/applications/reals_bootstrapping/slim/main.go +++ b/examples/single_party/applications/reals_bootstrapping/slim/main.go @@ -202,7 +202,7 @@ func main() { panic(err) } - // Generate a random plaintext with values uniformely distributed in [-1, 1] for the real and imaginary part. + // Generate a random plaintext with values uniformly distributed in [-1, 1] for the real and imaginary part. valuesWant := make([]complex128, params.MaxSlots()) for i := range valuesWant { valuesWant[i] = sampling.RandComplex128(-1, 1) @@ -235,7 +235,7 @@ func main() { } // Step 2: Some circuit in the coefficient domain - // Note: the result of SlotsToCoeffs is naturaly given in bit-reversed order + // Note: the result of SlotsToCoeffs is naturally given in bit-reversed order // In this example, we multiply by the monomial X^{N/2} (which is the imaginary // unit in the slots domain) if err = eval.Evaluator.Mul(ciphertext, 1i, ciphertext); err != nil { diff --git a/utils/slices.go b/utils/slices.go index 1d60d0d38..8944fe3ec 100644 --- a/utils/slices.go +++ b/utils/slices.go @@ -20,25 +20,35 @@ func Alias2D[V any](x, y [][]V) bool { // EqualSlice checks the equality between two slices of comparables. func EqualSlice[V comparable](a, b []V) (v bool) { - v = true + if len(a) != len(b) { + return false + } for i := range a { - v = v && (a[i] == b[i]) + if a[i] != b[i] { + return false + } } - return + return true } // MaxSlice returns the maximum value in the slice. func MaxSlice[V constraints.Ordered](slice []V) (max V) { - for _, c := range slice { - max = Max(max, c) + if len(slice) != 0 { + max = slice[0] + for _, c := range slice { + max = Max(max, c) + } } return } // MinSlice returns the minimum value in the slice. func MinSlice[V constraints.Ordered](slice []V) (min V) { - for _, c := range slice { - min = Min(min, c) + if len(slice) != 0 { + min = slice[0] + for _, c := range slice { + min = Min(min, c) + } } return } @@ -110,7 +120,7 @@ func RotateSlice[V any](s []V, k int) []V { func RotateSliceAllocFree[V any](s []V, k int, sout []V) { if len(s) != len(sout) { - panic("cannot RotateUint64SliceAllocFree: s and sout of different lengths") + panic("cannot RotateSliceAllocFree: s and sout of different lengths") } if len(s) == 0 { diff --git a/utils/slices_test.go b/utils/slices_test.go new file mode 100644 index 000000000..4515c8dcb --- /dev/null +++ b/utils/slices_test.go @@ -0,0 +1,95 @@ +package utils + +import ( + "sort" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestMinSlice(t *testing.T) { + require.Equal(t, 1, MinSlice([]int{1, 2, 3, 4, 5})) + require.Equal(t, 0, MinSlice([]int{5, 4, 3, 2, 0})) + require.Equal(t, -1, MinSlice([]int{-1, 1})) + require.Equal(t, -2, MinSlice([]int{-1, -2})) +} + +func TestMaxSlice(t *testing.T) { + require.Equal(t, 5, MaxSlice([]int{1, 2, 3, 4, 5})) + require.Equal(t, 5, MaxSlice([]int{5, 4, 3, 2, 0})) + require.Equal(t, 1, MaxSlice([]int{-1, 1})) + require.Equal(t, -1, MaxSlice([]int{-1, -2})) +} + +func TestEqualSlice(t *testing.T) { + require.True(t, EqualSlice([]int{1, 2, 3}, []int{1, 2, 3})) + require.True(t, EqualSlice([]int{-1, -2, -3}, []int{-1, -2, -3})) + require.True(t, EqualSlice([]int{}, []int{})) + require.False(t, EqualSlice([]int{1, 2, 3, 4, 5}, []int{1, 2, 3, 4, 6})) + require.False(t, EqualSlice([]int{1, 2, 3, 4, 5}, []int{1, 2, 3, 4})) +} + +func TestIsInSlice(t *testing.T) { + require.True(t, IsInSlice(1, []int{1, 2, 3, 4, 5})) + require.True(t, IsInSlice(-5, []int{-1, -2, -3, -4, -5})) + require.False(t, IsInSlice(0, []int{1, 2, 3, 4, 5})) + require.False(t, IsInSlice(6, []int{1, 2, 3, 4, 5})) +} + +func TestGetSortedKeys(t *testing.T) { + m := map[int]int{1: 1, 3: 3, 2: 2} + require.Equal(t, []int{1, 2, 3}, GetSortedKeys(m)) + m = map[int]int{-1: 1, -3: 3, -2: 2} + require.Equal(t, []int{-3, -2, -1}, GetSortedKeys(m)) +} + +func TestGetDistincts(t *testing.T) { + actual := GetDistincts([]int{1, 2}) + expected := []int{1, 2} + sort.Ints(expected) + sort.Ints(actual) + require.Equal(t, expected, actual) + + actual = GetDistincts([]int{1, 2, 3, 1, 2, 3}) + expected = []int{1, 2, 3} + sort.Ints(expected) + sort.Ints(actual) + require.Equal(t, expected, actual) + + actual = GetDistincts([]int{-1, 1, 1, 1}) + expected = []int{-1, 1} + sort.Ints(expected) + sort.Ints(actual) + require.Equal(t, expected, actual) +} + +func TestRotateSlice(t *testing.T) { + actual := RotateSlice([]int{1, 2, 3, 4, 5}, 2) + expected := []int{3, 4, 5, 1, 2} + require.Equal(t, expected, actual) + + actual = RotateSlice([]int{1, 2, 3, 4, 5}, -2) + expected = []int{4, 5, 1, 2, 3} + require.Equal(t, expected, actual) + + actual = RotateSlice([]int{1, 2, 3, 4, 5}, 0) + expected = []int{1, 2, 3, 4, 5} + require.Equal(t, expected, actual) +} + +func TestRotateSliceInPlace(t *testing.T) { + slice := []int{1, 2, 3, 4, 5} + RotateSliceInPlace(slice, 2) + expected := []int{3, 4, 5, 1, 2} + require.Equal(t, expected, slice) + + slice = []int{1, 2, 3, 4, 5} + RotateSliceInPlace(slice, -2) + expected = []int{4, 5, 1, 2, 3} + require.Equal(t, expected, slice) + + slice = []int{1, 2, 3, 4, 5} + RotateSliceInPlace(slice, 0) + expected = []int{1, 2, 3, 4, 5} + require.Equal(t, expected, slice) +} diff --git a/utils/utils.go b/utils/utils.go index ebdb44637..6aefee5df 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -35,7 +35,7 @@ func BitReverse64[V uint64 | uint32 | int | int64](index V, bitLen int) uint64 { return bits.Reverse64(uint64(index)) >> (64 - bitLen) } -// HammingWeight64 returns the hammingweight if the input value. +// HammingWeight64 returns the hamming weight if the input value. func HammingWeight64[V uint64 | uint32 | int | int64](x V) V { y := uint64(x) y -= (y >> 1) & 0x5555555555555555