raptorq/src/operation_vector.rs
Slesarew 5a720829fa
feat: support no_std (#143)
* feat: support no_std

`metal` feature supports `no_std` in configuration `default-features = false, features = ["metal"]`.
Float calculation is done via `micromath` crate.

All previously available functionality remains under default `std` feature.

Some tweaking of `python` and `wasm` features was done to compile tests.

* feat: get rid of floats (#2)

* feat: remove conversion to f64, fix features

* chore: uncomment symbols_required checker, fmt

* revert: add cdylib target for python support

* fix: generalize crate type

---------

Co-authored-by: varovainen <99664267+varovainen@users.noreply.github.com>
2023-02-02 18:07:41 -08:00

211 lines
6.3 KiB
Rust

#[cfg(feature = "std")]
use std::vec::Vec;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use crate::octet::Octet;
use crate::symbol::Symbol;
use crate::util::get_both_indices;
#[cfg(feature = "serde_support")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
#[allow(clippy::upper_case_acronyms)]
pub enum SymbolOps {
AddAssign {
dest: usize,
src: usize,
},
MulAssign {
dest: usize,
scalar: Octet,
},
FMA {
dest: usize,
src: usize,
scalar: Octet,
},
Reorder {
order: Vec<usize>,
},
}
pub fn perform_op(op: &SymbolOps, symbols: &mut Vec<Symbol>) {
match op {
SymbolOps::AddAssign { dest, src } => {
let (dest, temp) = get_both_indices(symbols, *dest, *src);
*dest += temp;
}
SymbolOps::MulAssign { dest, scalar } => {
symbols[*dest].mulassign_scalar(scalar);
}
SymbolOps::FMA { dest, src, scalar } => {
let (dest, temp) = get_both_indices(symbols, *dest, *src);
dest.fused_addassign_mul_scalar(temp, scalar);
}
SymbolOps::Reorder { order } => {
/* TODO: Reorder is the last step of the algorithm. It should be
* possible to move reorder to be the first step and use when
* creating D (place all rows in correct position before
* calculations). This will however force an update on all
* row-numbers used in all other "Operations". */
let mut temp_symbols: Vec<Option<Symbol>> = symbols.drain(..).map(Some).collect();
for row_index in order.iter() {
symbols.push(temp_symbols[*row_index].take().unwrap());
}
}
}
}
#[cfg(feature = "std")]
#[cfg(test)]
mod tests {
use rand::Rng;
use std::vec::Vec;
use crate::octet::Octet;
use crate::operation_vector::{perform_op, SymbolOps};
use crate::symbol::Symbol;
#[test]
fn test_add() {
let rows = 2;
let symbol_size = 1316;
let mut data: Vec<Symbol> = Vec::with_capacity(rows);
for _i in 0..rows {
let mut symbol_data: Vec<u8> = vec![0; symbol_size];
for j in 0..symbol_size {
symbol_data[j] = rand::thread_rng().gen();
}
let symbol = Symbol::new(symbol_data);
data.push(symbol);
}
let mut data0: Vec<u8> = vec![0; symbol_size];
let mut data1: Vec<u8> = vec![0; symbol_size];
let mut result: Vec<u8> = vec![0; symbol_size];
for i in 0..symbol_size {
data0[i] = data[0].as_bytes()[i];
data1[i] = data[1].as_bytes()[i];
result[i] = data0[i] ^ data1[i];
}
let mut symbol0 = Symbol::new(data0);
let symbol1 = Symbol::new(data1);
symbol0 += &symbol1;
perform_op(&SymbolOps::AddAssign { dest: 0, src: 1 }, &mut data);
assert_eq!(result, data[0].as_bytes());
}
#[test]
fn test_add_mul() {
let rows = 2;
let symbol_size = 1316;
let mut data: Vec<Symbol> = Vec::with_capacity(rows);
for _i in 0..rows {
let mut symbol_data: Vec<u8> = vec![0; symbol_size];
for j in 0..symbol_size {
symbol_data[j] = rand::thread_rng().gen();
}
let symbol = Symbol::new(symbol_data);
data.push(symbol);
}
let value = 173;
let mut data0: Vec<u8> = vec![0; symbol_size];
let mut data1: Vec<u8> = vec![0; symbol_size];
let mut result: Vec<u8> = vec![0; symbol_size];
for i in 0..symbol_size {
data0[i] = data[0].as_bytes()[i];
data1[i] = data[1].as_bytes()[i];
result[i] = data0[i] ^ (Octet::new(data1[i]) * Octet::new(value)).byte();
}
perform_op(
&SymbolOps::FMA {
dest: 0,
src: 1,
scalar: Octet::new(value),
},
&mut data,
);
assert_eq!(result, data[0].as_bytes());
}
#[test]
fn test_mul() {
let rows = 1;
let symbol_size = 1316;
let mut data: Vec<Symbol> = Vec::with_capacity(rows);
for _i in 0..rows {
let mut symbol_data: Vec<u8> = vec![0; symbol_size];
for j in 0..symbol_size {
symbol_data[j] = rand::thread_rng().gen();
}
let symbol = Symbol::new(symbol_data);
data.push(symbol);
}
let value = 215;
let mut data0: Vec<u8> = vec![0; symbol_size];
let mut result: Vec<u8> = vec![0; symbol_size];
for i in 0..symbol_size {
data0[i] = data[0].as_bytes()[i];
result[i] = (Octet::new(data0[i]) * Octet::new(value)).byte();
}
perform_op(
&SymbolOps::MulAssign {
dest: 0,
scalar: Octet::new(value),
},
&mut data,
);
assert_eq!(result, data[0].as_bytes());
}
#[test]
fn test_reorder() {
let rows = 10;
let symbol_size = 10;
let mut data: Vec<Symbol> = Vec::with_capacity(rows);
for i in 0..rows {
let mut symbol_data: Vec<u8> = vec![0; symbol_size];
for j in 0..symbol_size {
symbol_data[j] = i as u8;
}
let symbol = Symbol::new(symbol_data);
data.push(symbol);
}
assert_eq!(data[0].as_bytes()[0], 0);
assert_eq!(data[1].as_bytes()[0], 1);
assert_eq!(data[2].as_bytes()[0], 2);
assert_eq!(data[9].as_bytes()[0], 9);
perform_op(
&SymbolOps::Reorder {
order: vec![9, 7, 5, 3, 1, 8, 0, 6, 2, 4],
},
&mut data,
);
assert_eq!(data[0].as_bytes()[0], 9);
assert_eq!(data[1].as_bytes()[0], 7);
assert_eq!(data[2].as_bytes()[0], 5);
assert_eq!(data[3].as_bytes()[0], 3);
assert_eq!(data[4].as_bytes()[0], 1);
assert_eq!(data[5].as_bytes()[0], 8);
assert_eq!(data[6].as_bytes()[0], 0);
assert_eq!(data[7].as_bytes()[0], 6);
assert_eq!(data[8].as_bytes()[0], 2);
assert_eq!(data[9].as_bytes()[0], 4);
}
}