use crate::value::{
LVar, LVarId, Val,
Val::{Resolved, Var},
};
use crate::{Domain, DomainType, State};
use std::fmt::Debug;
use std::rc::Rc;
pub type ResolveFn<'a, D> = Box<dyn FnOnce(State<'a, D>) -> Option<State<'a, D>> + 'a>;
pub trait Constraint<'a, D>: Debug
where
D: Domain<'a>,
{
fn attempt(&self, state: &State<'a, D>) -> Result<ResolveFn<'a, D>, VarWatch>;
}
#[derive(Debug)]
pub struct VarWatch(pub(crate) Vec<LVarId>);
impl VarWatch {
pub fn one<A>(a: LVar<A>) -> Self {
VarWatch(vec![a.id])
}
pub fn two<A, B>(a: LVar<A>, b: LVar<B>) -> Self {
VarWatch(vec![a.id, b.id])
}
}
pub fn resolve_1<'a, A, D>(val: &Val<A>, state: &State<'a, D>) -> Result<Rc<A>, VarWatch>
where
A: Debug,
D: DomainType<'a, A>,
{
let a = state.resolve_val(val);
match a {
Resolved(a) => Ok(a.clone()),
Var(var) => Err(VarWatch::one(*var)),
}
}
pub fn resolve_2<'a, A, B, D>(
a: &Val<A>,
b: &Val<B>,
state: &State<'a, D>,
) -> Result<(Rc<A>, Rc<B>), VarWatch>
where
A: Debug,
B: Debug,
D: DomainType<'a, A> + DomainType<'a, B>,
{
let a = state.resolve_val(a);
let b = state.resolve_val(b);
match (a, b) {
(Resolved(a), Resolved(b)) => Ok((a.clone(), b.clone())),
(Var(var), _) => Err(VarWatch::one(*var)),
(_, Var(var)) => Err(VarWatch::one(*var)),
}
}
pub enum OneOfTwo<A: Debug, B: Debug> {
A(Rc<A>, Val<B>),
B(Val<A>, Rc<B>),
}
impl<A: Debug, B: Debug> OneOfTwo<A, B> {
pub fn resolve<'a, D>(
a: &Val<A>,
b: &Val<B>,
state: &State<'a, D>,
) -> Result<OneOfTwo<A, B>, VarWatch>
where
D: DomainType<'a, A> + DomainType<'a, B>,
{
let a = state.resolve_val(a);
let b = state.resolve_val(b);
match (a, b) {
(Resolved(a), b) => Ok(OneOfTwo::A(a.clone(), b.clone())),
(a, Resolved(b)) => Ok(OneOfTwo::B(a.clone(), b.clone())),
(Var(a), Var(b)) => Err(VarWatch::two(*a, *b)),
}
}
}
pub enum TwoOfThree<A: Debug, B: Debug, C: Debug> {
AB(Rc<A>, Rc<B>, Val<C>),
BC(Val<A>, Rc<B>, Rc<C>),
AC(Rc<A>, Val<B>, Rc<C>),
}
impl<A: Debug, B: Debug, C: Debug> TwoOfThree<A, B, C> {
pub fn resolve<'a, D>(
a: &Val<A>,
b: &Val<B>,
c: &Val<C>,
state: &State<'a, D>,
) -> Result<TwoOfThree<A, B, C>, VarWatch>
where
D: DomainType<'a, A> + DomainType<'a, B> + DomainType<'a, C>,
{
let a = state.resolve_val(a);
let b = state.resolve_val(b);
let c = state.resolve_val(c);
match (a, b, c) {
(Resolved(a), Resolved(b), c) => Ok(TwoOfThree::AB(a.clone(), b.clone(), c.clone())),
(a, Resolved(b), Resolved(c)) => Ok(TwoOfThree::BC(a.clone(), b.clone(), c.clone())),
(Resolved(a), b, Resolved(c)) => Ok(TwoOfThree::AC(a.clone(), b.clone(), c.clone())),
(Var(a), Var(b), _) => Err(VarWatch::two(*a, *b)),
(Var(a), _, Var(c)) => Err(VarWatch::two(*a, *c)),
(_, Var(b), Var(c)) => Err(VarWatch::two(*b, *c)),
}
}
}