use crate::*;
use core::mem::size_of;
#[derive(Debug, Clone)]
pub struct TransformMatrix {
n : usize, k : usize, array : Vec<u8>, read_pointer : usize,
}
impl TransformMatrix {
fn new(n : usize, k : usize) -> Self {
let array = vec![0; n * k];
let read_pointer = 0;
Self { n, k, array, read_pointer }
}
fn fill(&mut self, slice : &[u8]) -> &Self {
self.array.copy_from_slice(slice);
self
}
}
impl Iterator for TransformMatrix {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
let val = self.array[self.read_pointer];
self.read_pointer += 1;
if self.read_pointer >= self.n * self.k {
self.read_pointer -= self.n * self.k;
}
Some(val)
}
}
#[derive(Debug, Clone)]
pub struct InputMatrix {
k : usize, c : usize, array : Vec<u8>, read_pointer : usize,
}
impl InputMatrix {
fn new(k : usize, c : usize) -> Self {
let array = vec![0; k * c];
let read_pointer = 0;
Self { k, c, array, read_pointer }
}
fn fill(&mut self, slice : &[u8]) -> &mut Self {
self.array.copy_from_slice(slice);
self
}
}
impl Iterator for InputMatrix {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
let val = self.array[self.read_pointer];
self.read_pointer += 1;
if self.read_pointer >= self.c * self.k {
self.read_pointer -= self.c * self.k;
}
Some(val)
}
}
use guff::*;
pub struct MultiplyStream<'a> {
xform : &'a mut Iterator<Item=u8>,
input : &'a mut Iterator<Item=u8>,
field : &'a F8, }
impl<'a> Iterator for MultiplyStream<'a> {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
let a = self.xform.next().unwrap();
let b = self.input.next().unwrap();
Some(self.field.mul(a,b))
}
}
#[derive(Debug)]
pub struct OutputMatrix {
n : usize, c : usize, array : Vec<u8>, row : usize,
col : usize,
rowwise : bool
}
impl OutputMatrix {
fn new(n : usize, c : usize, rowwise : bool) -> Self {
let array = vec![0; n * c];
let row = 0;
let col = 0;
Self { n, c, array, row, col, rowwise }
}
fn new_rowwise(n : usize, c : usize) -> Self {
Self::new(n, c, true)
}
fn new_colwise(n : usize, c : usize) -> Self {
Self::new(n, c, false)
}
fn write_next(&mut self, e : u8) {
let size = self.n * self.c;
if self.rowwise {
self.array[self.row * self.c + self.col] = e;
} else {
self.array[self.row + self.col * self.n] = e;
}
self.row += 1;
if self.row == self.n { self.row = 0 }
self.col += 1;
if self.col == self.c { self.col = 0 }
}
}
pub fn warm_multiply(xform : &mut TransformMatrix,
input : &mut InputMatrix,
output : &mut OutputMatrix) {
let c = input.c;
let n = xform.n;
let k = xform.k;
assert!(k > 0);
assert!(n > 0);
assert!(c > 0);
assert_eq!(input.k, k);
assert_eq!(output.c, c);
assert_eq!(output.n, n);
assert_ne!(k, gcd(k,c));
let xiter = xform.into_iter();
let iiter = input.into_iter();
let mut mstream = MultiplyStream {
xform : xiter,
input : iiter,
field : &guff::new_gf8(0x11b,0x1b),
};
let mut dp_counter = 0;
let mut partial_sum = 0u8;
let mut m = mstream.take(n * k * c);
loop {
let p = m.next();
if p == None { break }
let p = p.unwrap();
partial_sum ^= p;
dp_counter += 1;
if dp_counter == k {
output.write_next(partial_sum);
partial_sum = 0u8;
dp_counter = 0;
}
}
}
pub fn interleave_streams(dest : &mut [u8], slices : &Vec<&[u8]>) {
let cols = dest.len() / slices.len();
let mut dest = dest.iter_mut();
let mut slice_iters : Vec::<_> = Vec::with_capacity(slices.len());
for s in slices {
let mut iter = s.iter();
slice_iters.push(iter);
}
for _ in 0 .. cols {
for mut slice in &mut slice_iters {
*dest.next().unwrap() = *slice.next().unwrap();
}
}
}
pub trait Simd {
type E;
type V;
fn cross_product(a : Self, b : Self) -> Self;
fn sum_across_n(m0 : Self, m1 : Self, n : usize, off : usize)
-> (Self::E, Self);
}
#[derive(Debug, Clone, Copy)]
pub struct SimSimd {
vec : [u8; 8],
}
impl Simd for SimSimd {
type V = [u8; 8];
type E = u8;
fn cross_product(a : Self, b : Self) -> Self {
let mut prod = [0u8; 8];
let f = new_gf8(0x11b,0x1b);
for i in 0..8 {
prod[i] = f.mul(a.vec[i], b.vec[i])
}
Self { vec : prod }
}
fn sum_across_n(m0 : Self, m1 : Self, mut n : usize, off : usize) -> (Self::E, Self) {
assert!(n <= 8);
let mut sum = 0u8;
if off + n >= 8 { for i in off .. 8 { sum ^= m0.vec[i] }
n -= 8 - off; for i in 0 .. n { sum ^= m1.vec[i] }
return (sum, m1)
} else { for i in off .. off + n { sum ^= m0.vec[i] }
return (sum, m0)
}
}
}
#[derive(Debug)]
pub struct SimSimdInputMatrix {
k : usize, c : usize, array : Vec<u8>, read_pointer : usize, }
impl SimSimdInputMatrix {
fn new(k : usize, c : usize) -> Self {
let array = vec![0; k * c];
let read_pointer = 0;
Self { k, c, array, read_pointer }
}
fn fill(&mut self, slice : &[u8]) -> &mut Self {
self.array.copy_from_slice(slice);
self
}
}
impl Iterator for SimSimdInputMatrix {
type Item = SimSimd;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
let mut val = [0u8;8];
let mut offset = self.read_pointer;
for i in 0..8 {
val[i] = self.array[offset];
offset += 1;
if offset == self.k * self.c {
offset = 0;
}
}
self.read_pointer = offset;
Some(SimSimd{vec : val})
}
}
#[derive(Debug, Clone)]
pub struct SimSimdTransformMatrix {
n : usize, k : usize, array : Vec<u8>, read_pointer : usize,
}
impl SimSimdTransformMatrix {
fn new(n : usize, k : usize) -> Self {
let array = vec![0; n * k];
let read_pointer = 0;
Self { n, k, array, read_pointer }
}
fn fill(&mut self, slice : &[u8]) -> &Self {
self.array.copy_from_slice(slice);
self
}
}
impl Iterator for SimSimdTransformMatrix {
type Item = SimSimd;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
let mut val = [0u8;8];
let mut offset = self.read_pointer;
for i in 0..8 {
val[i] = self.array[offset];
offset += 1;
if offset == self.n * self.k {
offset = 0;
}
}
self.read_pointer = offset;
Some(SimSimd{vec : val})
}
}
pub fn simsimd_warm_multiply(xform : &mut SimSimdTransformMatrix,
input : &mut SimSimdInputMatrix,
output : &mut OutputMatrix) {
let c = input.c;
let n = xform.n;
let k = xform.k;
assert!(k > 0);
assert!(n > 0);
assert!(c > 0);
assert_eq!(input.k, k);
assert_eq!(output.c, c);
assert_eq!(output.n, n);
if k != 1 { assert_ne!(k, gcd(k,c)) }
let xiter = xform.into_iter();
let iiter = input.into_iter();
let field = guff::new_gf8(0x11b,0x1b);
let mut dp_counter = 0;
let mut sum = 0u8;
let mut i0 : SimSimd;
let mut x0 : SimSimd;
x0 = xiter.next().unwrap();
i0 = iiter.next().unwrap();
let mut m0 = SimSimd::cross_product(x0,i0);
x0 = xiter.next().unwrap();
i0 = iiter.next().unwrap();
let mut m1 = SimSimd::cross_product(x0,i0);
let mut offset_mod_simd = 0;
let mut total_dps = 0;
let target = n * k * c;
while total_dps < target {
while dp_counter + 8 <= k {
let (part, new_m) = SimSimd::sum_across_n(m0,m1,8,offset_mod_simd);
sum ^= part;
m0 = new_m;
x0 = xiter.next().unwrap();
i0 = iiter.next().unwrap();
m1 = SimSimd::cross_product(x0,i0); dp_counter += 8;
}
if dp_counter < k { let want = k - dp_counter;
let (part, new_m) = SimSimd::sum_across_n(m0,m1,want,offset_mod_simd);
sum ^= part;
if offset_mod_simd + want >= 8 {
m0 = new_m; x0 = xiter.next().unwrap();
i0 = iiter.next().unwrap();
m1 = SimSimd::cross_product(x0,i0); } else {
m0 = new_m;
}
offset_mod_simd += want;
if offset_mod_simd >= 8 { offset_mod_simd -= 8 }
}
eprintln!("Sum: {}", sum);
output.write_next(sum);
sum = 0u8;
dp_counter = 0;
total_dps += 1;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn make_transform() {
let mut input = TransformMatrix::new(4,3);
let vec : Vec<u8> = (1u8..=12).collect();
input.fill(&vec[..]);
let elem = input.next();
assert_eq!(elem, Some(1));
let mut part : Vec<u8> = Vec::with_capacity(24);
for _ in 1..=5 { part.push(input.next().unwrap()) }
assert_eq!(part, [2,3,4,5,6]);
part.truncate(0);
for _ in 1..=12 { part.push(input.next().unwrap()) }
assert_eq!(part, [7,8,9,10,11,12,1,2,3,4,5,6]);
}
#[test]
fn make_input() {
let mut input = InputMatrix::new(4,3);
let vec : Vec<u8> = (1u8..=12).collect();
input.fill(&vec[..]);
let elem = input.next();
assert_eq!(elem, Some(1));
let mut part : Vec<u8> = Vec::with_capacity(24);
for _ in 1..=5 { part.push(input.next().unwrap()) }
assert_eq!(part, [2,3,4,5,6]);
part.truncate(0);
for _ in 1..=12 { part.push(input.next().unwrap()) }
assert_eq!(part, [7,8,9,10,11,12,1,2,3,4,5,6]);
}
#[test]
fn identity_multiply_colwise() {
let identity = [1,0,0, 0,1,0, 0,0,1];
let mut transform = TransformMatrix::new(3,3);
transform.fill(&identity[..]);
let mut input = InputMatrix::new(3,4);
let vec : Vec<u8> = (1u8..=12).collect();
input.fill(&vec[..]);
let mut output = OutputMatrix::new_colwise(3,4);
warm_multiply(&mut transform, &mut input, &mut output);
assert_eq!(output.array, vec);
}
#[test]
fn identity_multiply_rowwise() {
let identity = [1,0,0, 0,1,0, 0,0,1];
let mut transform = TransformMatrix::new(3,3);
transform.fill(&identity[..]);
let mut input = InputMatrix::new(3,4);
let vec : Vec<u8> = (1u8..=12).collect();
input.fill(&vec[..]);
let mut output = OutputMatrix::new_rowwise(3,4);
warm_multiply(&mut transform, &mut input, &mut output);
let mut transposed = vec![0u8; 12];
let transposed = [ vec[0], vec[3], vec [6], vec[9],
vec[1], vec[4], vec [7], vec[10],
vec[2], vec[5], vec [8], vec[11], ];
assert_eq!(output.array, transposed);
}
#[test]
fn test_interleave() {
let a0 = [0, 3, 6, 9];
let a1 = [1, 4, 7, 10];
let a2 = [2, 5, 8, 11];
let vec = vec![&a0[..], &a1[..], &a2[..] ];
let mut dest = vec![0 ; 12];
interleave_streams(&mut dest, &vec);
assert_eq!(dest, [0,1,2,3,4,5,6,7,8,9,10,11]);
}
#[test]
fn make_simd_transform() {
let mut input = SimSimdTransformMatrix::new(4,3);
let vec : Vec<u8> = (0u8..12).collect();
input.fill(&vec[..]);
let mut elem = input.next(); assert_eq!(elem.unwrap().vec, [0u8,1,2,3,4,5,6,7]);
elem = input.next(); assert_eq!(elem.unwrap().vec, [8u8,9,10,11,0,1,2,3]);
}
#[test]
fn make_simd_input() {
let mut input = SimSimdInputMatrix::new(4,3);
let vec : Vec<u8> = (0u8..12).collect();
input.fill(&vec[..]);
let mut elem = input.next(); assert_eq!(elem.unwrap().vec, [0u8,1,2,3,4,5,6,7]);
elem = input.next(); assert_eq!(elem.unwrap().vec, [8u8,9,10,11,0,1,2,3]);
}
#[test]
fn simd_identity_multiply_colwise() {
let identity = [1,0,0, 0,1,0, 0,0,1];
let mut transform = SimSimdTransformMatrix::new(3,3);
transform.fill(&identity[..]);
let mut input = SimSimdInputMatrix::new(3,4);
let vec : Vec<u8> = (1u8..=12).collect();
input.fill(&vec[..]);
let mut output = OutputMatrix::new_colwise(3,4);
simsimd_warm_multiply(&mut transform, &mut input, &mut output);
assert_eq!(output.array, vec);
}
#[test]
fn simd_identity_multiply_rowwise() {
let identity = [1,0,0, 0,1,0, 0,0,1];
let mut transform = SimSimdTransformMatrix::new(3,3);
transform.fill(&identity[..]);
let mut input = SimSimdInputMatrix::new(3,4);
let vec : Vec<u8> = (1u8..=12).collect();
input.fill(&vec[..]);
let mut output = OutputMatrix::new_rowwise(3,4);
simsimd_warm_multiply(&mut transform, &mut input, &mut output);
let mut transposed = vec![0u8; 12];
let transposed = [ vec[0], vec[3], vec [6], vec[9],
vec[1], vec[4], vec [7], vec[10],
vec[2], vec[5], vec [8], vec[11], ];
assert_eq!(output.array, transposed);
}
#[test]
fn simd_identity_k8_multiply_colwise() {
let identity = [
1,0,0,0 ,0,0,0,0,
0,1,0,0 ,0,0,0,0,
0,0,1,0 ,0,0,0,0,
0,0,0,1 ,0,0,0,0,
0,0,0,0 ,1,0,0,0,
0,0,0,0 ,0,1,0,0,
0,0,0,0 ,0,0,1,0,
0,0,0,0 ,0,0,0,1,
];
let mut transform = SimSimdTransformMatrix::new(8,8);
transform.fill(&identity[..]);
let mut input = SimSimdInputMatrix::new(8,7);
let vec : Vec<u8> = (1u8..=56).collect();
input.fill(&vec[..]);
let mut output = OutputMatrix::new_colwise(8,7);
simsimd_warm_multiply(&mut transform, &mut input, &mut output);
assert_eq!(output.array, vec);
}
#[test]
fn simd_identity_k9_multiply_colwise() {
let identity = [
1,0,0, 0,0,0, 0,0,0,
0,1,0, 0,0,0, 0,0,0,
0,0,1, 0,0,0, 0,0,0,
0,0,0, 1,0,0, 0,0,0,
0,0,0, 0,1,0, 0,0,0,
0,0,0, 0,0,1, 0,0,0,
0,0,0, 0,0,0, 1,0,0,
0,0,0, 0,0,0, 0,1,0,
0,0,0, 0,0,0, 0,0,1,
];
let mut transform = SimSimdTransformMatrix::new(9,9);
transform.fill(&identity[..]);
let mut input = SimSimdInputMatrix::new(9,17);
let vec : Vec<u8> = (1u8..=9 * 17).collect();
input.fill(&vec[..]);
let mut output = OutputMatrix::new_colwise(9,17);
simsimd_warm_multiply(&mut transform, &mut input, &mut output);
assert_eq!(output.array, vec);
}
#[test]
fn simd_identity_k1_multiply_colwise() {
let identity = [ 1, ];
let mut transform = SimSimdTransformMatrix::new(1,1);
transform.fill(&identity[..]);
let mut input = SimSimdInputMatrix::new(1,2);
let vec : Vec<u8> = (1u8..=2).collect();
input.fill(&vec[..]);
let mut output = OutputMatrix::new_colwise(1,2);
simsimd_warm_multiply(&mut transform, &mut input, &mut output);
assert_eq!(output.array, vec);
}
#[test]
fn simd_identity_k2_multiply_colwise() {
let identity = [
1,0,
0,1,
];
let mut transform = SimSimdTransformMatrix::new(2,2);
transform.fill(&identity[..]);
let mut input = SimSimdInputMatrix::new(2,7);
let vec : Vec<u8> = (1u8..=14).collect();
input.fill(&vec[..]);
let mut output = OutputMatrix::new_colwise(2,7);
simsimd_warm_multiply(&mut transform, &mut input, &mut output);
assert_eq!(output.array, vec);
}
}