use super::descriptor::COORDINATES;
use crate::field::BinaryField;
use crate::field::Field;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Relation {
coordinates: [u64; COORDINATES],
len: u8,
}
impl Relation {
#[must_use]
#[allow(clippy::cast_possible_truncation)]
pub const fn from_coordinates(coordinates: &[u64]) -> Self {
assert!(
coordinates.len() <= COORDINATES,
"relation exceeds the coordinate capacity"
);
let mut out = [0; COORDINATES];
let mut i = 0;
while i < coordinates.len() {
out[i] = coordinates[i];
i += 1;
}
Self {
coordinates: out,
len: coordinates.len() as u8,
}
}
#[must_use]
#[allow(clippy::cast_lossless, clippy::cast_possible_truncation)]
pub const fn from_binary_words<B: BinaryField>(words: &[u64]) -> Self {
let degree = <B as Field>::DEGREE;
let Some(total) = (words.len() as u64).checked_mul(degree as u64) else {
panic!("relation exceeds the coordinate capacity");
};
assert!(
total <= COORDINATES as u64,
"relation exceeds the coordinate capacity"
);
let mut out = [0; COORDINATES];
let mut i = 0;
while i < words.len() {
let word = words[i];
assert!(
degree >= 64 || word >> degree == 0,
"relation word has bits at or above the base degree"
);
let mut j = 0;
while j < degree as usize {
out[i * degree as usize + j] = (word >> j) & 1;
j += 1;
}
i += 1;
}
Self {
coordinates: out,
len: total as u8,
}
}
#[must_use]
pub const fn to_coordinates(&self) -> &[u64] {
self.coordinates.split_at(self.len as usize).0
}
}
#[cfg(test)]
mod tests {
use super::COORDINATES;
use super::Relation;
use crate::field::FiniteField;
use crate::field::Polynomial;
type Gf4 = FiniteField<2, 2, Polynomial<7>>;
#[test]
fn const_construction_reads_back() {
const RELATION: Relation = Relation::from_coordinates(&[4, 0]);
const BINARY: Relation = Relation::from_binary_words::<Gf4>(&[2, 1]);
const _: () = assert!(BINARY.to_coordinates().len() == 4);
const _: () = assert!(BINARY.to_coordinates()[0] == 0);
let runtime = Relation::from_coordinates(&[4, 0]);
assert_eq!(runtime.to_coordinates(), &[4, 0]);
let runtime_binary = Relation::from_binary_words::<Gf4>(&[2, 1]);
assert_eq!(runtime_binary.to_coordinates(), &[0, 1, 1, 0]);
assert_eq!(RELATION.to_coordinates(), &[4, 0]);
assert_eq!(BINARY.to_coordinates(), &[0, 1, 1, 0]);
}
#[test]
#[should_panic(expected = "relation exceeds the coordinate capacity")]
fn capacity_panics() {
const OVER: [u64; COORDINATES + 1] = [1; COORDINATES + 1];
let relation = Relation::from_coordinates(&OVER);
assert_eq!(relation.to_coordinates().len(), COORDINATES + 1);
}
#[test]
#[should_panic(expected = "relation word has bits at or above the base degree")]
fn binary_word_above_degree_panics() {
const BAD: [u64; 1] = [4];
let relation = Relation::from_binary_words::<Gf4>(&BAD);
assert_eq!(relation.to_coordinates().len(), 2);
}
#[test]
#[should_panic(expected = "relation exceeds the coordinate capacity")]
fn binary_expansion_capacity_panics() {
const LONG: [u64; 60] = [1; 60];
let relation = Relation::from_binary_words::<Gf4>(&LONG);
assert_eq!(relation.to_coordinates().len(), 120);
}
}