use ndarray::Array2;
use super::Constructor;
use crate::error::{Error, Result};
use crate::gf::DynamicGf;
use crate::oa::{OA, OAParams};
use crate::utils::factor_prime_power;
#[derive(Debug, Clone)]
pub struct BoseBush {
q: u32,
_m: u32,
field_q: DynamicGf,
_field_2q: DynamicGf,
}
impl BoseBush {
pub fn new(q: u32) -> Result<Self> {
let factorization = factor_prime_power(q).ok_or(Error::RequiresPowerOfTwo {
levels: q,
algorithm: "BoseBush",
})?;
if factorization.prime != 2 {
return Err(Error::RequiresPowerOfTwo {
levels: q,
algorithm: "BoseBush",
});
}
let m = factorization.exponent;
if q > 2 {
return Err(Error::invalid_params(
"BoseBush currently only supports q=2. Support for q=4,8,16,... is planned.",
));
}
let field_q = DynamicGf::new(q)?;
let _field_2q = DynamicGf::new(2 * q)?;
Ok(Self {
q,
_m: m,
field_q,
_field_2q,
})
}
#[must_use]
pub fn levels(&self) -> u32 {
self.q
}
#[must_use]
pub fn runs(&self) -> usize {
(2 * self.q * self.q) as usize
}
#[must_use]
pub fn max_factors(&self) -> usize {
(2 * self.q + 1) as usize
}
pub fn construct(&self, factors: usize) -> Result<OA> {
let max = self.max_factors();
if factors > max {
return Err(Error::TooManyFactors {
factors,
max,
algorithm: "BoseBush",
});
}
if factors == 0 {
return Err(Error::invalid_params("factors must be at least 1"));
}
let q = self.q;
let runs = self.runs();
let mut data = Array2::zeros((runs, factors));
for b in 0..2u32 {
for i in 0..q {
for j in 0..q {
let row = (b * q * q + i * q + j) as usize;
let elem_i = self.field_q.element(i);
let elem_j = self.field_q.element(j);
for c in 0..factors {
let col = c as u32;
if col == 0 {
data[[row, c]] = j;
} else if col <= q {
let k = col % q; let k_elem = self.field_q.element(k);
let val = elem_i.add(k_elem.mul(elem_j.clone())).to_u32();
data[[row, c]] = val;
} else {
let k = col - q;
let k_elem = self.field_q.element(k % q);
let base_val = elem_i.add(k_elem.mul(elem_j.clone()));
let b_elem = self.field_q.element(b);
let val = base_val.add(b_elem).to_u32();
data[[row, c]] = val;
}
}
}
}
}
let strength = 2.min(factors as u32);
let params = OAParams::new(runs, factors, q, strength)?;
Ok(OA::new(data, params))
}
}
impl Constructor for BoseBush {
fn name(&self) -> &'static str {
"BoseBush"
}
fn family(&self) -> &'static str {
"OA(2q², k, q, 2), q=2^m, k ≤ 2q+1"
}
fn levels(&self) -> u32 {
self.q
}
fn strength(&self) -> u32 {
2
}
fn runs(&self) -> usize {
(2 * self.q * self.q) as usize
}
fn max_factors(&self) -> usize {
(2 * self.q + 1) as usize
}
fn construct(&self, factors: usize) -> Result<OA> {
BoseBush::construct(self, factors)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::oa::verify_strength;
#[test]
fn test_bose_bush_creation() {
let bb = BoseBush::new(2).unwrap();
assert_eq!(bb.levels(), 2);
assert_eq!(bb.runs(), 8);
assert_eq!(bb.max_factors(), 5);
}
#[test]
fn test_bose_bush_invalid() {
assert!(BoseBush::new(3).is_err());
assert!(BoseBush::new(5).is_err());
assert!(BoseBush::new(6).is_err());
assert!(BoseBush::new(7).is_err());
assert!(BoseBush::new(9).is_err());
}
#[test]
fn test_bose_bush_unsupported_q() {
assert!(BoseBush::new(4).is_err());
assert!(BoseBush::new(8).is_err());
assert!(BoseBush::new(16).is_err());
}
#[test]
fn test_bose_bush_q2_valid() {
assert!(BoseBush::new(2).is_ok());
}
#[test]
fn test_bose_bush_q2() {
let bb = BoseBush::new(2).unwrap();
let oa = bb.construct(5).unwrap();
assert_eq!(oa.runs(), 8);
assert_eq!(oa.factors(), 5);
assert_eq!(oa.levels(), 2);
assert_eq!(oa.strength(), 2);
let result = verify_strength(&oa, 2).unwrap();
assert!(
result.is_valid,
"BoseBush(2) should be valid: {:?}",
result.issues
);
}
#[test]
fn test_bose_bush_fewer_factors() {
let bb = BoseBush::new(2).unwrap();
let oa = bb.construct(3).unwrap();
assert_eq!(oa.runs(), 8);
assert_eq!(oa.factors(), 3);
assert_eq!(oa.levels(), 2);
let result = verify_strength(&oa, 2).unwrap();
assert!(
result.is_valid,
"BoseBush(2) with 3 factors should be valid: {:?}",
result.issues
);
}
#[test]
fn test_bose_bush_too_many_factors() {
let bb = BoseBush::new(2).unwrap();
assert!(bb.construct(6).is_err()); }
#[test]
fn test_bose_bush_zero_factors() {
let bb = BoseBush::new(2).unwrap();
assert!(bb.construct(0).is_err());
}
#[test]
fn test_bose_bush_single_factor() {
let bb = BoseBush::new(2).unwrap();
let oa = bb.construct(1).unwrap();
assert_eq!(oa.factors(), 1);
assert_eq!(oa.runs(), 8);
let mut counts = [0usize; 2];
for row in 0..8 {
counts[oa.get(row, 0) as usize] += 1;
}
assert_eq!(counts, [4, 4]);
}
}