Files
lattigo/utils/structs/matrix.go
Christian Mouchet 28d498ffc7 improved Vector, Matrix and Map types
Vector and Matrix now use []T and [][]T as underlying types, which makes them more versatile and easy to cast to from slices.
Map still uses map[K]V as map values cannot be addressed and this is anoying to use.
I also got rid of the Codec[T] type as it can be replaced with a 3-liner (i.e., equivalent to calling the Codex and checking the error).
There is also a small change of the OperendQ.Encode method that now uses OperandQ.WriteTo instead of OperandQ.Encode (since it is a bit fasterfor some reason...). This is an experiment and more work on the serialization is needed.
2023-06-07 20:18:13 +02:00

194 lines
4.6 KiB
Go

package structs
import (
"bufio"
"bytes"
"encoding/binary"
"fmt"
"io"
"github.com/tuneinsight/lattigo/v4/utils/buffer"
)
// Matrix is a struct storing a vector of Vector.
type Matrix[T any] [][]T
func (m Matrix[T]) CopyNew() *Matrix[T] {
if c, isCopiable := any(new(T)).(CopyNewer[T]); !isCopiable {
panic(fmt.Errorf("vector component of type %T does not comply to %T", new(T), c))
}
mcpy := Matrix[T](make([][]T, len(m)))
for i := range m {
mcpy[i] = make([]T, len(m[i]))
for j := range m[i] {
mcpy[i][j] = *any(&m[i][j]).(CopyNewer[T]).CopyNew()
}
}
return &mcpy
}
// WriteTo writes the object on an io.Writer.
// To ensure optimal efficiency and minimal allocations, the user is encouraged
// to provide a struct implementing the interface buffer.Writer, which defines
// a subset of the method of the bufio.Writer.
// If w is not compliant to the buffer.Writer interface, it will be wrapped in
// a new bufio.Writer.
// For additional information, see lattigo/utils/buffer/writer.go.
func (m *Matrix[T]) WriteTo(w io.Writer) (n int64, err error) {
if w, isWritable := any(new(T)).(io.WriterTo); !isWritable {
return 0, fmt.Errorf("vector component of type %T does not comply to %T", new(T), w)
}
switch w := w.(type) {
case buffer.Writer:
var inc int
if inc, err = buffer.WriteInt(w, len(*m)); err != nil {
return int64(inc), err
}
n += int64(inc)
for _, v := range *m {
vec := Vector[T](v)
inc, err := vec.WriteTo(w)
n += int64(inc)
if err != nil {
return n, err
}
}
return n, w.Flush()
default:
return m.WriteTo(bufio.NewWriter(w))
}
}
// ReadFrom reads on the object from an io.Writer.
// To ensure optimal efficiency and minimal allocations, the user is encouraged
// to provide a struct implementing the interface buffer.Reader, which defines
// a subset of the method of the bufio.Reader.
// If r is not compliant to the buffer.Reader interface, it will be wrapped in
// a new bufio.Reader.
// For additional information, see lattigo/utils/buffer/reader.go.
func (m *Matrix[T]) ReadFrom(r io.Reader) (n int64, err error) {
if r, isReadable := any(new(T)).(io.ReaderFrom); !isReadable {
return 0, fmt.Errorf("vector component of type %T does not comply to %T", new(T), r)
}
switch r := r.(type) {
case buffer.Reader:
var size, n int
if n, err = buffer.ReadInt(r, &size); err != nil {
return int64(n), fmt.Errorf("cannot read matrix size: %w", err)
}
if cap(*m) < size {
*m = make([][]T, size)
}
*m = (*m)[:size]
for i := range *m {
inc, err := (*Vector[T])(&(*m)[i]).ReadFrom(r)
n += int(inc)
if err != nil {
return int64(n), err
}
}
return int64(n), nil
default:
return m.ReadFrom(bufio.NewReader(r))
}
}
// BinarySize returns the size in bytes of the object
// when encoded using Encode.
func (m Matrix[T]) BinarySize() (size int) {
if s, isSizable := any(new(T)).(BinarySizer); !isSizable {
panic(fmt.Errorf("vector component of type %T does not comply to %T", new(T), s))
}
size += 8
for _, v := range m {
size += (*Vector[T])(&v).BinarySize()
}
return
}
// Encode encodes the object into a binary form on a preallocated slice of bytes
// and returns the number of bytes written.
func (m Matrix[T]) Encode(b []byte) (n int, err error) {
if e, isEncodable := any(new(T)).(Encoder); !isEncodable {
panic(fmt.Errorf("vector component of type %T does not comply to %T", new(T), e))
}
binary.LittleEndian.PutUint64(b[n:], uint64(len(m)))
n += 8
for _, v := range m {
inc, err := (*Vector[T])(&v).Encode(b)
n += inc
if err != nil {
return n, err
}
}
return n, nil
}
// Decode decodes a slice of bytes generated by Encode
// on the object and returns the number of bytes read.
func (m *Matrix[T]) Decode(p []byte) (n int, err error) {
if d, isDecodable := any(new(T)).(Decoder); !isDecodable {
panic(fmt.Errorf("vector component of type %T does not comply to %T", new(T), d))
}
size := int(binary.LittleEndian.Uint64(p))
n += 8
if cap(*m) < size {
*m = make([][]T, size)
}
*m = (*m)[:size]
for i := range *m {
inc, err := (*Vector[T])(&(*m)[i]).Decode(p[n:])
n += inc
if err != nil {
return n, err
}
}
return n, nil
}
// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes.
func (m *Matrix[T]) MarshalBinary() (p []byte, err error) {
buf := bytes.NewBuffer([]byte{})
_, err = m.WriteTo(buf)
return buf.Bytes(), err
}
// UnmarshalBinary decodes a slice of bytes generated by
// MarshalBinary or WriteTo on the object.
func (m *Matrix[T]) UnmarshalBinary(p []byte) (err error) {
_, err = m.ReadFrom(bytes.NewBuffer(p))
return
}