use std::{num::NonZero, ops::Not};
use crate::{
IntVal,
actions::IntInspectionActions,
model::{
Decision, Model, View,
decision::integer::Domain,
view::{boolean::BoolView, integer::IntView},
},
};
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub(crate) struct Resolved<T>(pub(crate) T);
impl Decision<IntVal> {
pub(crate) fn resolve_alias(self, model: &Model) -> Resolved<View<IntVal>> {
View::from(self).resolve_alias(model)
}
}
impl Decision<bool> {
pub(crate) fn resolve_alias(self, model: &Model) -> Resolved<View<bool>> {
View::from(self).resolve_alias(model)
}
}
impl Resolved<Decision<IntVal>> {
pub(crate) fn idx(&self) -> usize {
self.0.idx()
}
}
impl<T> Resolved<T> {
pub(crate) fn into_inner(self) -> T {
self.0
}
}
impl Resolved<View<IntVal>> {
pub(crate) fn integer_decision(&self) -> Option<Resolved<Decision<IntVal>>> {
match self.0.0 {
IntView::Linear(lin) => Some(Resolved(lin.var)),
IntView::Const(_) | IntView::Bool(_) => None,
}
}
}
impl<T: Not> Not for Resolved<T> {
type Output = Resolved<T::Output>;
fn not(self) -> Self::Output {
Resolved(!self.0)
}
}
impl View<IntVal> {
pub(crate) fn resolve_alias(self, model: &Model) -> Resolved<Self> {
use IntView::*;
let mut view = self;
let mut scale = 1;
let mut offset = 0;
loop {
match view.0 {
Const(c) => {
return Resolved((c * scale + offset).into());
}
_ if scale == 0 => {
return Resolved(offset.into());
}
Linear(lin) => match model.int_vars[lin.var.idx()].domain {
Domain::Domain(_) => {
return Resolved(View(Linear(lin * NonZero::new(scale).unwrap() + offset)));
}
Domain::Alias(alias) => {
view = alias;
offset += scale * lin.offset;
scale *= lin.scale.get();
}
},
Bool(lin) => {
let var = lin.var.resolve_alias(model).into_inner();
if let BoolView::Const(b) = var.0 {
return Resolved(View(Const(
lin.transform_val(b as IntVal) * scale + offset,
)));
}
return Resolved(View(Bool(lin * NonZero::new(scale).unwrap() + offset)));
}
}
}
}
}
impl View<bool> {
pub(crate) fn resolve_alias(self, model: &Model) -> Resolved<Self> {
use BoolView::*;
let mut result = self;
while let Decision(lit) = result.0 {
if let Some(alias) = model.bool_vars[lit.idx()].alias {
debug_assert_ne!(alias, result);
debug_assert_ne!(alias, !result);
result = if lit.is_negated() { !alias } else { alias };
} else {
break;
}
}
match result.0 {
IntEq(iv, val) => {
let (lb, ub) = iv.bounds(model);
if val < lb || val > ub {
return Resolved(false.into());
} else if val == lb && val == ub {
return Resolved(true.into());
}
}
IntGreaterEq(iv, val) => {
let (lb, ub) = iv.bounds(model);
if lb >= val {
return Resolved(true.into());
} else if ub < val {
return Resolved(false.into());
}
}
IntLess(iv, val) => {
let (lb, ub) = iv.bounds(model);
if ub < val {
return Resolved(true.into());
} else if lb >= val {
return Resolved(false.into());
}
}
IntNotEq(iv, val) => {
let (lb, ub) = iv.bounds(model);
if val < lb || val > ub {
return Resolved(true.into());
} else if val == lb && val == ub {
return Resolved(false.into());
}
}
_ => {}
}
Resolved(result)
}
}