use crate::{constraints::props::{Propagate, Prune}, variables::{VarId, Val}, variables::views::{Context, View}};
#[derive(Clone, Copy, Debug)]
#[doc(hidden)]
pub struct Div<U, V> {
x: U,
y: V,
s: VarId,
}
impl<U, V> Div<U, V> {
pub const fn new(x: U, y: V, s: VarId) -> Self {
Self { x, y, s }
}
}
impl<U: View, V: View> Prune for Div<U, V> {
fn prune(&self, ctx: &mut Context) -> Option<()> {
let x_min = self.x.min(ctx);
let x_max = self.x.max(ctx);
let y_min = self.y.min(ctx);
let y_max = self.y.max(ctx);
if Val::range_contains_unsafe_divisor(y_min, y_max) {
return Some(());
}
let mut s_candidates = Vec::new();
let x_samples = if x_min == x_max {
vec![x_min]
} else {
vec![x_min, x_max]
};
let y_samples = if y_min == y_max {
vec![y_min]
} else {
vec![y_min, y_max]
};
for &x_val in &x_samples {
for &y_val in &y_samples {
if let Some(div_result) = x_val.safe_div(y_val) {
match div_result {
Val::ValF(f) if f.is_finite() => s_candidates.push(div_result),
Val::ValI(_) => s_candidates.push(div_result),
_ => {} }
}
}
}
if !s_candidates.is_empty() {
let s_min = s_candidates.iter().fold(s_candidates[0], |acc, &x| if x < acc { x } else { acc });
let s_max = s_candidates.iter().fold(s_candidates[0], |acc, &x| if x > acc { x } else { acc });
let _min = self.s.try_set_min(s_min, ctx)?;
let _max = self.s.try_set_max(s_max, ctx)?;
}
let s_min = self.s.min(ctx);
let s_max = self.s.max(ctx);
let mut x_candidates = Vec::new();
for &s_val in &[s_min, s_max] {
for &y_val in &[y_min, y_max] {
let x_val = s_val * y_val;
x_candidates.push(x_val);
}
}
if !x_candidates.is_empty() {
let x_new_min = x_candidates.iter().fold(x_candidates[0], |acc, &x| if x < acc { x } else { acc });
let x_new_max = x_candidates.iter().fold(x_candidates[0], |acc, &x| if x > acc { x } else { acc });
let _min = self.x.try_set_min(x_new_min, ctx)?;
let _max = self.x.try_set_max(x_new_max, ctx)?;
}
if !Val::range_contains_unsafe_divisor(s_min, s_max) {
let mut y_candidates = Vec::new();
for &x_val in &[x_min, x_max] {
for &s_val in &[s_min, s_max] {
if let Some(y_val) = x_val.safe_div(s_val) {
match y_val {
Val::ValF(f) if f.is_finite() => y_candidates.push(y_val),
Val::ValI(_) => y_candidates.push(y_val),
_ => {} }
}
}
}
if !y_candidates.is_empty() {
let y_new_min = y_candidates.iter().fold(y_candidates[0], |acc, &x| if x < acc { x } else { acc });
let y_new_max = y_candidates.iter().fold(y_candidates[0], |acc, &x| if x > acc { x } else { acc });
let _min = self.y.try_set_min(y_new_min, ctx)?;
let _max = self.y.try_set_max(y_new_max, ctx)?;
}
}
Some(())
}
}
impl<U: View, V: View> Propagate for Div<U, V> {
fn list_trigger_vars(&self) -> impl Iterator<Item = VarId> {
core::iter::once(self.s)
.chain(self.x.get_underlying_var())
.chain(self.y.get_underlying_var())
}
}