#[cfg(test)]
use std::fmt::Debug;
use ark_ff::Field;
#[cfg(test)]
use serde::{Deserialize, Serialize};
#[macro_export]
macro_rules! ensure {
($cond:expr, $err:expr) => {
#[allow(clippy::neg_cmp_op_on_partial_ord)]
if !$cond {
return Err($err.into());
};
};
}
pub const fn workload_size<T: Sized>() -> usize {
#[cfg(all(target_arch = "aarch64", target_os = "macos"))]
const CACHE_SIZE: usize = 1 << 17;
#[cfg(all(
target_arch = "aarch64",
any(target_os = "ios", target_os = "android", target_os = "linux")
))]
const CACHE_SIZE: usize = 1 << 16;
#[cfg(target_arch = "x86_64")]
const CACHE_SIZE: usize = 1 << 15;
#[cfg(not(any(
all(target_arch = "aarch64", target_os = "macos"),
all(
target_arch = "aarch64",
any(target_os = "ios", target_os = "android", target_os = "linux")
),
target_arch = "x86_64"
)))]
const CACHE_SIZE: usize = 1 << 15;
CACHE_SIZE / size_of::<T>()
}
#[inline]
pub fn zip_strict<A, B>(
a: impl IntoIterator<Item = A>,
b: impl IntoIterator<Item = B>,
) -> impl Iterator<Item = (A, B)> {
let mut a = a.into_iter();
let mut b = b.into_iter();
std::iter::from_fn(move || match (a.next(), b.next()) {
(Some(x), Some(y)) => Some((x, y)),
(None, None) => None,
_ => panic!("Iterators had different lengths"),
})
}
pub fn base_decomposition(mut value: usize, base: u8, n_bits: usize) -> Vec<u8> {
debug_assert!(base > 1, "Base must be at least 2");
let mut result = vec![0u8; n_bits];
for digit in result.iter_mut().rev() {
*digit = (value % (base as usize)) as u8;
value /= base as usize;
}
result
}
pub fn expand_randomness<F: Field>(base: F, len: usize) -> Vec<F> {
let mut res = Vec::with_capacity(len);
let mut acc = F::ONE;
for _ in 0..len {
res.push(acc);
acc *= base;
}
res
}
#[cfg(test)]
#[track_caller]
pub fn test_serde<T: Debug + PartialEq + Serialize + for<'a> Deserialize<'a>>(value: &T) {
let json = serde_json::to_string_pretty(value).expect("json serialization failed");
let deserialized = serde_json::from_str(&json).expect("json deserialization failed");
assert_eq!(value, &deserialized, "json serde roundtrip failed");
let bytes = {
let mut writer = Vec::new();
ciborium::into_writer(value, &mut writer).expect("ciborium serialization failed");
writer
};
let deserialized =
ciborium::from_reader(bytes.as_slice()).expect("ciborium deserialization failed");
assert_eq!(value, &deserialized, "ciborium serde roundtrip failed");
}
#[cfg(test)]
mod tests {
use ark_ff::{AdditiveGroup, Field};
use super::*;
use crate::algebra::fields::Field64;
#[test]
fn test_base_decomposition_binary() {
assert_eq!(base_decomposition(0b1011, 2, 6), vec![0, 0, 1, 0, 1, 1]);
assert_eq!(base_decomposition(5, 2, 4), vec![0, 1, 0, 1]);
assert_eq!(base_decomposition(10, 2, 4), vec![1, 0, 1, 0]);
assert_eq!(base_decomposition(0, 2, 4), vec![0, 0, 0, 0]);
}
#[test]
fn test_base_decomposition_ternary() {
assert_eq!(base_decomposition(15, 3, 3), vec![1, 2, 0]);
assert_eq!(base_decomposition(8, 3, 4), vec![0, 0, 2, 2]);
}
#[test]
fn test_base_decomposition_large_values() {
assert_eq!(base_decomposition(123, 5, 4), vec![0, 4, 4, 3]);
assert_eq!(base_decomposition(100, 7, 5), vec![0, 0, 2, 0, 2]);
}
#[test]
fn test_base_decomposition_padding() {
assert_eq!(base_decomposition(5, 4, 5), vec![0, 0, 0, 1, 1]);
assert_eq!(base_decomposition(2, 3, 4), vec![0, 0, 0, 2]);
assert_eq!(base_decomposition(0, 5, 4), vec![0, 0, 0, 0]);
}
#[test]
fn test_base_decomposition_edge_cases() {
assert_eq!(base_decomposition(15, 2, 4), vec![1, 1, 1, 1]);
assert_eq!(base_decomposition(26, 3, 3), vec![2, 2, 2]);
assert_eq!(base_decomposition(63, 4, 3), vec![3, 3, 3]);
}
#[test]
fn test_base_decomposition_truncation_behavior() {
assert_eq!(base_decomposition(15 + 81, 3, 3), vec![1, 2, 0]);
assert_eq!(base_decomposition(20 + 16, 2, 4), vec![0, 1, 0, 0]);
}
#[test]
#[should_panic]
fn test_base_decomposition_invalid_base() {
base_decomposition(10, 0, 5);
}
#[test]
fn test_expand_randomness_basic() {
let base = Field64::from(2);
let len = 5;
let expected = vec![
Field64::ONE,
Field64::from(2),
Field64::from(4),
Field64::from(8),
Field64::from(16),
];
assert_eq!(expand_randomness(base, len), expected);
}
#[test]
fn test_expand_randomness_zero_length() {
let base = Field64::from(3);
assert!(expand_randomness(base, 0).is_empty());
}
#[test]
fn test_expand_randomness_one_length() {
let base = Field64::from(5);
assert_eq!(expand_randomness(base, 1), vec![Field64::ONE]);
}
#[test]
fn test_expand_randomness_large_base() {
let base = Field64::from(10);
let len = 4;
let expected = vec![
Field64::ONE,
Field64::from(10),
Field64::from(100),
Field64::from(1000),
];
assert_eq!(expand_randomness(base, len), expected);
}
#[test]
fn test_expand_randomness_identity_case() {
let base = Field64::ONE;
let len = 6;
let expected = vec![Field64::ONE; len];
assert_eq!(expand_randomness(base, len), expected);
}
#[test]
fn test_expand_randomness_zero_base() {
let base = Field64::ZERO;
let len = 5;
let expected = vec![
Field64::ONE,
Field64::ZERO,
Field64::ZERO,
Field64::ZERO,
Field64::ZERO,
];
assert_eq!(expand_randomness(base, len), expected);
}
#[test]
fn test_expand_randomness_negative_base() {
let base = -Field64::ONE;
let len = 6;
let expected = vec![
Field64::ONE,
-Field64::ONE,
Field64::ONE,
-Field64::ONE,
Field64::ONE,
-Field64::ONE,
];
assert_eq!(expand_randomness(base, len), expected);
}
}