swisspost-evoting-go-poc/pkg/mixnet/permutation.go

72 lines
1.5 KiB
Go
Raw Normal View History

package mixnet
import (
"math/big"
emath "github.com/user/evote/pkg/math"
)
// Permutation represents a random permutation of [0, N).
type Permutation struct {
table []int
}
// GenPermutation generates a random permutation of size N using Fisher-Yates.
func GenPermutation(n int) Permutation {
table := make([]int, n)
for i := range table {
table[i] = i
}
for i := 0; i < n; i++ {
// offset = random integer in [0, n-i)
max := big.NewInt(int64(n - i))
offset := emath.RandomBigInt(max).Int64()
// Swap table[i] with table[i+offset]
j := i + int(offset)
table[i], table[j] = table[j], table[i]
}
return Permutation{table: table}
}
// Apply returns π[i].
func (p Permutation) Apply(i int) int {
return p.table[i]
}
// Size returns N.
func (p Permutation) Size() int {
return len(p.table)
}
// Table returns a copy of the permutation table.
func (p Permutation) Table() []int {
t := make([]int, len(p.table))
copy(t, p.table)
return t
}
// GetMatrixDimensions computes size-optimal m×n dimensions for a vector of size N.
// Returns (m, n) where m <= n, m*n = N, and m is the largest factor ≤ √N.
func GetMatrixDimensions(vectorSize int) (int, int) {
if vectorSize < 2 {
panic("size must be >= 2")
}
m := 1
n := vectorSize
sqrtN := isqrt(vectorSize)
for i := sqrtN; i > 1; i-- {
if vectorSize%i == 0 {
m = i
n = vectorSize / i
break
}
}
return m, n
}
func isqrt(n int) int {
x := int64(n)
s := new(big.Int).Sqrt(big.NewInt(x))
return int(s.Int64())
}