use crate::basis::Basis;
use crate::rns::RnsInt;
#[derive(Clone, Debug)]
pub struct RnsBatch {
pub data: Vec<u32>,
pub batch_size: usize,
pub basis: Basis,
}
impl RnsBatch {
pub fn new(batch_size: usize, basis: Basis) -> Self {
Self::zeros(batch_size, basis)
}
pub fn zeros(batch_size: usize, basis: Basis) -> Self {
let k = basis.len();
RnsBatch { data: vec![0; batch_size * k], batch_size, basis }
}
#[inline]
pub fn channels_len(&self) -> usize {
self.basis.len()
}
#[inline]
pub fn get(&self, b: usize, c: usize) -> u32 {
self.data[b * self.basis.len() + c]
}
#[inline]
pub fn set(&mut self, b: usize, c: usize, val: u32) {
let k = self.basis.len();
self.data[b * k + c] = val;
}
pub fn from_rns_ints(items: &[RnsInt]) -> Self {
assert!(!items.is_empty(), "cannot build an RnsBatch from zero items");
let basis = items[0].basis.clone();
let k = basis.len();
let mut data = Vec::with_capacity(items.len() * k);
for item in items {
debug_assert_eq!(item.basis, basis, "all items must share the basis");
debug_assert_eq!(item.residues.len(), k);
data.extend_from_slice(&item.residues);
}
RnsBatch { data, batch_size: items.len(), basis }
}
pub fn to_rns_ints(&self) -> Vec<RnsInt> {
let k = self.basis.len();
(0..self.batch_size)
.map(|b| {
let residues = self.data[b * k..(b + 1) * k].to_vec();
RnsInt::from_residues(residues, self.basis.clone())
})
.collect()
}
pub fn as_u32_bytes(&self) -> Vec<u8> {
bytemuck::cast_slice(&self.data).to_vec()
}
pub fn from_u32(values: &[u32], batch_size: usize, basis: Basis) -> Self {
RnsBatch { data: values.to_vec(), batch_size, basis }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn get_set_roundtrip() {
let b = Basis::standard();
let mut batch = RnsBatch::zeros(4, b);
batch.set(2, 3, 7);
assert_eq!(batch.get(2, 3), 7);
assert_eq!(batch.get(0, 0), 0);
}
#[test]
fn pack_unpack_roundtrip() {
let b = Basis::standard();
let items = vec![
RnsInt::from_i64(123, b.clone()),
RnsInt::from_i64(456, b.clone()),
RnsInt::from_i64(789, b.clone()),
];
let batch = RnsBatch::from_rns_ints(&items);
assert_eq!(batch.batch_size, 3);
let back = batch.to_rns_ints();
for (a, bb) in items.iter().zip(back.iter()) {
assert_eq!(a.to_bigint(), bb.to_bigint());
}
}
#[test]
fn u32_byte_layout() {
let b = Basis::standard();
let mut batch = RnsBatch::zeros(1, b);
batch.set(0, 0, 1);
batch.set(0, 1, 2);
let bytes = batch.as_u32_bytes();
assert_eq!(bytes[0], 1);
assert_eq!(bytes[4], 2);
}
}