swisspost-evoting-go-poc/pkg/math/groupmatrix.go
saymrwulf e8b6f30871 Swiss Post E-Voting Go PoC
Proof-of-concept reimplementation of the Swiss Post e-voting
cryptographic protocol in Go. Single binary, 52 source files,
2 dependencies. Covers ElGamal encryption, Bayer-Groth verifiable
shuffles, zero-knowledge proofs, return codes, and a full
election ceremony demo.
2026-02-13 19:53:09 +01:00

280 lines
7.4 KiB
Go

package math
// GqMatrix is a matrix of GqElements.
type GqMatrix struct {
rows []*GqVector
group *GqGroup
numRows int
numCols int
}
// NewGqMatrix creates a matrix from rows.
func NewGqMatrix(rows []*GqVector) *GqMatrix {
if len(rows) == 0 {
panic("matrix must have at least one row")
}
numCols := rows[0].Size()
for i, row := range rows {
if row.Size() != numCols {
panic("all rows must have the same size")
}
if i > 0 && !row.group.Equals(rows[0].group) {
panic("all rows must be from the same group")
}
}
copied := make([]*GqVector, len(rows))
copy(copied, rows)
return &GqMatrix{rows: copied, group: rows[0].group, numRows: len(rows), numCols: numCols}
}
// GqMatrixFromColumns creates a matrix from columns.
func GqMatrixFromColumns(columns []*GqVector) *GqMatrix {
if len(columns) == 0 {
panic("matrix must have at least one column")
}
numRows := columns[0].Size()
rows := make([]*GqVector, numRows)
for i := 0; i < numRows; i++ {
elems := make([]GqElement, len(columns))
for j, col := range columns {
elems[j] = col.Get(i)
}
rows[i] = &GqVector{elements: elems, group: columns[0].group}
}
return NewGqMatrix(rows)
}
// NumRows returns the number of rows.
func (m *GqMatrix) NumRows() int {
return m.numRows
}
// NumCols returns the number of columns.
func (m *GqMatrix) NumCols() int {
return m.numCols
}
// Group returns the common group.
func (m *GqMatrix) Group() *GqGroup {
return m.group
}
// Get returns the element at (row, col).
func (m *GqMatrix) Get(row, col int) GqElement {
return m.rows[row].Get(col)
}
// GetRow returns a row as a GqVector.
func (m *GqMatrix) GetRow(i int) *GqVector {
return m.rows[i]
}
// GetColumn returns a column as a GqVector.
func (m *GqMatrix) GetColumn(j int) *GqVector {
elems := make([]GqElement, m.numRows)
for i := 0; i < m.numRows; i++ {
elems[i] = m.rows[i].Get(j)
}
return &GqVector{elements: elems, group: m.group}
}
// Transpose returns the transpose of this matrix.
func (m *GqMatrix) Transpose() *GqMatrix {
rows := make([]*GqVector, m.numCols)
for j := 0; j < m.numCols; j++ {
rows[j] = m.GetColumn(j)
}
return &GqMatrix{rows: rows, group: m.group, numRows: m.numCols, numCols: m.numRows}
}
// AppendColumn creates a new matrix with a column appended.
func (m *GqMatrix) AppendColumn(column *GqVector) *GqMatrix {
if column.Size() != m.numRows {
panic("column size must match number of rows")
}
rows := make([]*GqVector, m.numRows)
for i := 0; i < m.numRows; i++ {
rows[i] = m.rows[i].Append(column.Get(i))
}
return NewGqMatrix(rows)
}
// PrependColumn creates a new matrix with a column prepended.
func (m *GqMatrix) PrependColumn(column *GqVector) *GqMatrix {
if column.Size() != m.numRows {
panic("column size must match number of rows")
}
rows := make([]*GqVector, m.numRows)
for i := 0; i < m.numRows; i++ {
rows[i] = m.rows[i].Prepend(column.Get(i))
}
return NewGqMatrix(rows)
}
// SubColumns returns a matrix with columns [from, to).
func (m *GqMatrix) SubColumns(from, to int) *GqMatrix {
rows := make([]*GqVector, m.numRows)
for i := 0; i < m.numRows; i++ {
rows[i] = m.rows[i].SubVector(from, to)
}
return NewGqMatrix(rows)
}
// FlatElements returns all elements row by row.
func (m *GqMatrix) FlatElements() []GqElement {
result := make([]GqElement, 0, m.numRows*m.numCols)
for _, row := range m.rows {
result = append(result, row.elements...)
}
return result
}
// ZqMatrix is a matrix of ZqElements.
type ZqMatrix struct {
rows []*ZqVector
group *ZqGroup
numRows int
numCols int
}
// NewZqMatrix creates a matrix from rows.
func NewZqMatrix(rows []*ZqVector) *ZqMatrix {
if len(rows) == 0 {
panic("matrix must have at least one row")
}
numCols := rows[0].Size()
for _, row := range rows {
if row.Size() != numCols {
panic("all rows must have the same size")
}
}
copied := make([]*ZqVector, len(rows))
copy(copied, rows)
return &ZqMatrix{rows: copied, group: rows[0].group, numRows: len(rows), numCols: numCols}
}
// ZqMatrixFromColumns creates a ZqMatrix from columns.
func ZqMatrixFromColumns(columns []*ZqVector) *ZqMatrix {
if len(columns) == 0 {
panic("matrix must have at least one column")
}
numRows := columns[0].Size()
rows := make([]*ZqVector, numRows)
for i := 0; i < numRows; i++ {
elems := make([]ZqElement, len(columns))
for j, col := range columns {
elems[j] = col.Get(i)
}
rows[i] = &ZqVector{elements: elems, group: columns[0].group}
}
return NewZqMatrix(rows)
}
// NumRows returns the number of rows.
func (m *ZqMatrix) NumRows() int {
return m.numRows
}
// NumCols returns the number of columns.
func (m *ZqMatrix) NumCols() int {
return m.numCols
}
// Group returns the common group.
func (m *ZqMatrix) Group() *ZqGroup {
return m.group
}
// Get returns the element at (row, col).
func (m *ZqMatrix) Get(row, col int) ZqElement {
return m.rows[row].Get(col)
}
// GetRow returns a row as a ZqVector.
func (m *ZqMatrix) GetRow(i int) *ZqVector {
return m.rows[i]
}
// GetColumn returns a column as a ZqVector.
func (m *ZqMatrix) GetColumn(j int) *ZqVector {
elems := make([]ZqElement, m.numRows)
for i := 0; i < m.numRows; i++ {
elems[i] = m.rows[i].Get(j)
}
return &ZqVector{elements: elems, group: m.group}
}
// Transpose returns the transpose.
func (m *ZqMatrix) Transpose() *ZqMatrix {
rows := make([]*ZqVector, m.numCols)
for j := 0; j < m.numCols; j++ {
rows[j] = m.GetColumn(j)
}
return &ZqMatrix{rows: rows, group: m.group, numRows: m.numCols, numCols: m.numRows}
}
// AppendColumn creates a new matrix with a column appended.
func (m *ZqMatrix) AppendColumn(column *ZqVector) *ZqMatrix {
if column.Size() != m.numRows {
panic("column size must match number of rows")
}
rows := make([]*ZqVector, m.numRows)
for i := 0; i < m.numRows; i++ {
rows[i] = m.rows[i].Append(column.Get(i))
}
return NewZqMatrix(rows)
}
// PrependColumn creates a new matrix with a column prepended.
func (m *ZqMatrix) PrependColumn(column *ZqVector) *ZqMatrix {
if column.Size() != m.numRows {
panic("column size must match number of rows")
}
rows := make([]*ZqVector, m.numRows)
for i := 0; i < m.numRows; i++ {
rows[i] = m.rows[i].Prepend(column.Get(i))
}
return NewZqMatrix(rows)
}
// SubColumns returns a matrix with columns [from, to).
func (m *ZqMatrix) SubColumns(from, to int) *ZqMatrix {
rows := make([]*ZqVector, m.numRows)
for i := 0; i < m.numRows; i++ {
rows[i] = m.rows[i].SubVector(from, to)
}
return NewZqMatrix(rows)
}
// FlatElements returns all elements row by row.
func (m *ZqMatrix) FlatElements() []ZqElement {
result := make([]ZqElement, 0, m.numRows*m.numCols)
for _, row := range m.rows {
result = append(result, row.elements...)
}
return result
}
// VectorToGqMatrix reshapes a flat GqVector into a matrix of numRows x numCols.
func VectorToGqMatrix(v *GqVector, numRows, numCols int) *GqMatrix {
if v.Size() != numRows*numCols {
panic("vector size must equal numRows * numCols")
}
rows := make([]*GqVector, numRows)
for i := 0; i < numRows; i++ {
rows[i] = v.SubVector(i*numCols, (i+1)*numCols)
}
return NewGqMatrix(rows)
}
// VectorToZqMatrix reshapes a flat ZqVector into a matrix.
func VectorToZqMatrix(v *ZqVector, numRows, numCols int) *ZqMatrix {
if v.Size() != numRows*numCols {
panic("vector size must equal numRows * numCols")
}
rows := make([]*ZqVector, numRows)
for i := 0; i < numRows; i++ {
rows[i] = v.SubVector(i*numCols, (i+1)*numCols)
}
return NewZqMatrix(rows)
}