use crate::Error::{EmptyFills, InvalidCellCageIndex};
use crate::fill::Fill;
use crate::memo::Memo;
use crate::operator::{ArithmeticConstraint, CommutativeOperator, NonCommutativeOperator};
use crate::tuples::{Tuple, Tuples};
use crate::{Error, N, T};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Table {
n: N,
constraint: ArithmeticConstraint,
tuples: Vec<Vec<N>>,
fills: Vec<Fill>,
}
impl Table {
#[allow(dead_code)]
pub fn commutative(
n: N,
k: N,
operator: CommutativeOperator,
target: T,
) -> Result<Self, Error> {
let constraint = ArithmeticConstraint::CommutativeConstraint(operator, target);
Self::build(
n,
constraint,
Tuples::commutative(n, k, operator, target).collect(),
)
}
pub fn non_commutative(
n: N,
operator: NonCommutativeOperator,
target: T,
) -> Result<Self, Error> {
let constraint = ArithmeticConstraint::NonCommutativeConstraint(operator, target);
Self::build(
n,
constraint,
Tuples::non_commutative(n, operator, target).collect(),
)
}
pub(crate) fn tuples(&self) -> &[Vec<N>] {
&self.tuples
}
fn build(n: N, constraint: ArithmeticConstraint, tuples: Vec<Vec<N>>) -> Result<Self, Error> {
let fills = fills_from_tuples(&tuples)?;
Ok(Self {
n,
constraint,
tuples,
fills,
})
}
}
impl Memo for Table {
fn get(&self, index: usize) -> Result<Fill, Error> {
self.fills
.get(index)
.copied()
.ok_or(InvalidCellCageIndex(index))
}
fn narrow(&self, support: &[Fill]) -> Result<Self, Error> {
let tuples = self
.tuples
.iter()
.filter(|tuple| {
tuple
.iter()
.enumerate()
.all(|(i, &v)| support[i].contains(v))
})
.cloned()
.collect::<Vec<_>>();
Self::build(self.n, self.constraint, tuples)
}
}
pub fn fills_from_tuples(tuples: &[Tuple]) -> Result<Vec<Fill>, Error> {
if tuples.is_empty() {
return Err(EmptyFills);
}
let k = tuples[0].len();
let fills: Vec<Fill> = (0..k)
.map(|i| Fill::from(&tuples.iter().map(|t| t[i]).collect::<Tuple>()))
.collect();
if fills.iter().any(|f| f.is_empty()) {
return Err(EmptyFills);
}
Ok(fills)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Error::EmptyFills;
use crate::operator::CommutativeOperator::{Add, Multiply};
use crate::operator::NonCommutativeOperator::{Divide, Subtract};
#[test]
fn add_fills_are_union_of_column_values() {
let t = Table::commutative(4, 2, Add, 6).unwrap();
assert_eq!(t.get(0).unwrap(), Fill::from(&[2, 3, 4]));
assert_eq!(t.get(1).unwrap(), Fill::from(&[2, 3, 4]));
}
#[test]
fn multiply_fills_contain_expected_values() {
let t = Table::commutative(6, 2, Multiply, 6).unwrap();
assert_eq!(t.get(0).unwrap(), Fill::from(&[1, 2, 3, 6]));
assert_eq!(t.get(1).unwrap(), Fill::from(&[1, 2, 3, 6]));
}
#[test]
fn subtract_fills_contain_expected_values() {
let t = Table::non_commutative(4, Subtract, 1).unwrap();
assert_eq!(t.get(0).unwrap(), Fill::from(&[1, 2, 3, 4]));
assert_eq!(t.get(1).unwrap(), Fill::from(&[1, 2, 3, 4]));
}
#[test]
fn divide_fills_contain_expected_values() {
let t = Table::non_commutative(4, Divide, 2).unwrap();
assert_eq!(t.get(0).unwrap(), Fill::from(&[1, 2, 4]));
assert_eq!(t.get(1).unwrap(), Fill::from(&[1, 2, 4]));
}
#[test]
fn commutative_no_solutions_returns_empty_fills_error() {
assert!(matches!(Table::commutative(4, 2, Add, 9), Err(EmptyFills)));
}
#[test]
fn fill_out_of_bounds_returns_index_error() {
let t = Table::commutative(4, 2, Add, 5).unwrap();
assert!(matches!(t.get(2), Err(InvalidCellCageIndex(2))));
}
#[test]
fn narrow_with_full_support_is_identity() {
let t = Table::commutative(4, 2, Add, 5).unwrap();
let full = vec![Fill::all(4), Fill::all(4)];
assert_eq!(t.narrow(&full).unwrap(), t);
}
#[test]
fn narrow_filters_tuples_and_updates_fills() {
let t = Table::commutative(4, 2, Add, 5).unwrap();
let narrowed = t
.narrow(&[Fill::from(&[1, 2]), Fill::from(&[1, 2, 3, 4])])
.unwrap();
assert_eq!(narrowed.get(0).unwrap(), Fill::from(&[1, 2]));
assert_eq!(narrowed.get(1).unwrap(), Fill::from(&[3, 4]));
}
#[test]
fn narrow_eliminating_all_tuples_returns_empty_fills_error() {
let t = Table::commutative(4, 2, Add, 5).unwrap();
assert!(matches!(
t.narrow(&[Fill::from(&[1]), Fill::from(&[1])]),
Err(EmptyFills)
));
}
}