use ndarray::Array2;
use super::Constructor;
use crate::error::{Error, Result};
use crate::gf::DynamicGf;
use crate::oa::{OA, OAParams};
use crate::utils::is_prime_power;
#[derive(Debug, Clone)]
pub struct Bush {
q: u32,
strength: u32,
field: DynamicGf,
}
impl Bush {
pub fn new(q: u32, strength: u32) -> Result<Self> {
if !is_prime_power(q) {
return Err(Error::LevelsNotPrimePower {
levels: q,
algorithm: "Bush",
});
}
if strength < 2 {
return Err(Error::InvalidStrength {
strength,
min: 2,
max: q,
algorithm: "Bush",
});
}
if strength > q {
return Err(Error::InvalidStrength {
strength,
min: 2,
max: q,
algorithm: "Bush",
});
}
let field = DynamicGf::new(q)?;
Ok(Self { q, strength, field })
}
#[must_use]
pub fn levels(&self) -> u32 {
self.q
}
#[must_use]
pub fn strength(&self) -> u32 {
self.strength
}
#[must_use]
pub fn runs(&self) -> usize {
self.q.pow(self.strength) as usize
}
#[must_use]
pub fn max_factors(&self) -> usize {
(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: "Bush",
});
}
if factors == 0 {
return Err(Error::invalid_params("factors must be at least 1"));
}
let q = self.q;
let t = self.strength;
let runs = self.runs();
let mut data = Array2::zeros((runs, factors));
let mut results = vec![0u32; factors];
let points: Vec<u32> = (0..factors as u32).collect();
for row in 0..runs {
let mut coeffs = Vec::with_capacity(t as usize);
let mut temp_row = row as u32;
for _ in 0..t {
coeffs.push(temp_row % q);
temp_row /= q;
}
let eval_count = factors.min(q as usize);
self.field
.bulk_eval_poly(&coeffs, &points[0..eval_count], &mut results[0..eval_count]);
for col in 0..eval_count {
data[[row, col]] = results[col];
}
if factors > q as usize {
data[[row, q as usize]] = coeffs[t as usize - 1];
}
}
let actual_strength = t.min(factors as u32);
let params = OAParams::new(runs, factors, q, actual_strength)?;
Ok(OA::new(data, params))
}
}
impl Constructor for Bush {
fn name(&self) -> &'static str {
"Bush"
}
fn family(&self) -> &'static str {
"OA(q^t, k, q, t), k ≤ q+1"
}
fn levels(&self) -> u32 {
self.q
}
fn strength(&self) -> u32 {
self.strength
}
fn runs(&self) -> usize {
self.q.pow(self.strength) as usize
}
fn max_factors(&self) -> usize {
(self.q + 1) as usize
}
fn construct(&self, factors: usize) -> Result<OA> {
Bush::construct(self, factors)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::oa::verify_strength;
#[test]
fn test_bush_creation() {
let bush = Bush::new(3, 3).unwrap();
assert_eq!(bush.levels(), 3);
assert_eq!(bush.strength(), 3);
assert_eq!(bush.runs(), 27);
assert_eq!(bush.max_factors(), 4);
}
#[test]
fn test_bush_invalid_levels() {
assert!(Bush::new(6, 2).is_err()); assert!(Bush::new(10, 2).is_err()); }
#[test]
fn test_bush_invalid_strength() {
assert!(Bush::new(3, 1).is_err()); assert!(Bush::new(3, 4).is_err()); assert!(Bush::new(5, 6).is_err()); }
#[test]
fn test_bush_strength_2_equals_bose() {
let bush = Bush::new(3, 2).unwrap();
let oa = bush.construct(4).unwrap();
assert_eq!(oa.runs(), 9);
assert_eq!(oa.factors(), 4);
assert_eq!(oa.levels(), 3);
assert_eq!(oa.strength(), 2);
let result = verify_strength(&oa, 2).unwrap();
assert!(
result.is_valid,
"Bush t=2 should be valid strength-2: {:?}",
result.issues
);
}
#[test]
fn test_bush_strength_3() {
let bush = Bush::new(3, 3).unwrap();
let oa = bush.construct(4).unwrap();
assert_eq!(oa.runs(), 27); assert_eq!(oa.factors(), 4);
assert_eq!(oa.levels(), 3);
assert_eq!(oa.strength(), 3);
let result = verify_strength(&oa, 2).unwrap();
assert!(
result.is_valid,
"Bush t=3 should be valid strength-2: {:?}",
result.issues
);
let result = verify_strength(&oa, 3).unwrap();
assert!(
result.is_valid,
"Bush t=3 should be valid strength-3: {:?}",
result.issues
);
}
#[test]
fn test_bush_5_3() {
let bush = Bush::new(5, 3).unwrap();
let oa = bush.construct(6).unwrap();
assert_eq!(oa.runs(), 125); assert_eq!(oa.factors(), 6);
assert_eq!(oa.levels(), 5);
assert_eq!(oa.strength(), 3);
let result = verify_strength(&oa, 3).unwrap();
assert!(
result.is_valid,
"Bush(5,3) should be valid strength-3: {:?}",
result.issues
);
}
#[test]
fn test_bush_7_2() {
let bush = Bush::new(7, 2).unwrap();
let oa = bush.construct(8).unwrap();
assert_eq!(oa.runs(), 49);
assert_eq!(oa.factors(), 8);
assert_eq!(oa.levels(), 7);
assert_eq!(oa.strength(), 2);
let result = verify_strength(&oa, 2).unwrap();
assert!(
result.is_valid,
"Bush(7,2) should be valid strength-2: {:?}",
result.issues
);
}
#[test]
fn test_bush_prime_power_4() {
let bush = Bush::new(4, 2).unwrap();
let oa = bush.construct(5).unwrap();
assert_eq!(oa.runs(), 16);
assert_eq!(oa.factors(), 5);
assert_eq!(oa.levels(), 4);
let result = verify_strength(&oa, 2).unwrap();
assert!(
result.is_valid,
"Bush(4,2) should be valid: {:?}",
result.issues
);
}
#[test]
fn test_bush_prime_power_8() {
let bush = Bush::new(8, 2).unwrap();
let oa = bush.construct(9).unwrap();
assert_eq!(oa.runs(), 64);
assert_eq!(oa.factors(), 9);
assert_eq!(oa.levels(), 8);
let result = verify_strength(&oa, 2).unwrap();
assert!(
result.is_valid,
"Bush(8,2) should be valid: {:?}",
result.issues
);
}
#[test]
fn test_bush_too_many_factors() {
let bush = Bush::new(3, 3).unwrap();
assert!(bush.construct(5).is_err()); }
#[test]
fn test_bush_zero_factors() {
let bush = Bush::new(3, 3).unwrap();
assert!(bush.construct(0).is_err());
}
#[test]
fn test_bush_single_factor() {
let bush = Bush::new(3, 3).unwrap();
let oa = bush.construct(1).unwrap();
assert_eq!(oa.factors(), 1);
assert_eq!(oa.strength(), 1);
let mut counts = [0usize; 3];
for row in 0..27 {
counts[oa.get(row, 0) as usize] += 1;
}
assert_eq!(counts, [9, 9, 9]);
}
}