use std::{
collections::{HashMap, HashSet},
error::Error,
fmt::{Display, Formatter},
hash::{Hash, Hasher},
ops::RangeInclusive
};
use rand::Rng;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::{
Add, AddressingMode, CanAllocate, CanVisitInstructions as _,
CompilationError, Div, DropHighest, DropLowest, Exp, Function,
InstructionVisitor, Mod, Mul, Neg, ProgramCounter, RegisterIndex, Return,
RollCustomDice, RollRange, RollStandardDice, RollingRecordIndex, Sub,
SumRollingRecord, add, div, exp, r#mod, mul, neg, parser::ParseError,
roll_custom_dice, roll_range, roll_standard_dice, sub
};
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct RollingRecord
{
pub kind: RollingRecordKind<i32>,
pub results: Vec<i32>,
pub lowest_dropped: i32,
pub highest_dropped: i32
}
impl Display for RollingRecord
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
{
let sorted = {
let mut results = self.results.to_vec();
results.sort_unstable();
results
};
write!(f, "{}: ", self.kind)?;
write!(f, "[")?;
let start = 0;
let end = self.lowest_dropped as usize;
for i in start..end
{
if i != start
{
write!(f, ", ")?;
}
write!(f, "{}", sorted[i])?;
if i + 1 == self.lowest_dropped as usize && end != sorted.len()
{
write!(f, "; ")?;
}
}
let start = self.lowest_dropped as usize;
let end = sorted.len() - self.highest_dropped as usize;
for i in start..end
{
if i != start
{
write!(f, ", ")?;
}
write!(f, "{}", sorted[i])?;
if i + 1 == end && end != sorted.len()
{
write!(f, "; ")?;
}
}
let start = sorted.len() - self.highest_dropped as usize;
let end = sorted.len();
#[allow(clippy::needless_range_loop)]
for i in start..end
{
if i != start
{
write!(f, ", ")?;
}
write!(f, "{}", sorted[i])?;
}
write!(f, "]")
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum RollingRecordKind<T>
{
#[default]
Uninitialized,
Range
{
start: T,
end: T
},
Standard
{
count: T,
faces: T
},
Custom
{
count: T,
faces: Vec<i32>
}
}
impl<T> Display for RollingRecordKind<T>
where
T: Display
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
{
match self
{
RollingRecordKind::Uninitialized =>
{
write!(f, "uninitialized")
},
RollingRecordKind::Range { start, end } =>
{
write!(f, "[{}:{}]", start, end)
},
RollingRecordKind::Standard { count, faces } =>
{
write!(f, "{}D{}", count, faces)
},
RollingRecordKind::Custom { count, faces } =>
{
write!(f, "{}d[", count)?;
for (i, face) in faces.iter().enumerate()
{
if i != 0
{
write!(f, ", ")?;
}
write!(f, "{}", face)?;
}
write!(f, "]")
}
}
}
}
#[cfg_attr(doc, aquamarine::aquamarine)]
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Evaluator
{
pub function: Function,
pub environment: HashMap<usize, i32>
}
#[derive(Debug)]
struct EvaluatorState<'r, R>
where
R: Rng + ?Sized
{
rng: &'r mut R,
pc: ProgramCounter,
registers: Vec<i32>,
records: Vec<RollingRecord>,
result: i32
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Evaluation
{
pub result: i32,
pub records: Vec<RollingRecord>
}
impl<R> Display for EvaluatorState<'_, R>
where
R: Rng + ?Sized
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
{
writeln!(f, "evaluator:\n\tpc: {}", self.pc)?;
if !self.registers.is_empty()
{
writeln!(f, "\tregisters:")?;
for (i, register) in self.registers.iter().enumerate()
{
writeln!(f, "\t\t@{} = {}", i, register)?;
}
}
if !self.records.is_empty()
{
writeln!(f, "\trecords:")?;
for (i, record) in self.records.iter().enumerate()
{
writeln!(f, "\t\tâš…{} = {}", i, record)?;
}
}
Ok(())
}
}
impl Display for Evaluation
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
{
write!(f, "{}", self.result)?;
if !self.records.is_empty()
{
write!(f, ": (")?;
for (i, record) in self.records.iter().enumerate()
{
if i != 0
{
write!(f, ", ")?;
}
write!(f, "{}", record)?;
}
write!(f, ")")?;
}
Ok(())
}
}
impl<'r, R> From<EvaluatorState<'r, R>> for Evaluation
where
R: Rng + ?Sized
{
fn from(value: EvaluatorState<'r, R>) -> Self
{
Self {
result: value.result,
records: value.records
}
}
}
impl Evaluator
{
pub fn new(function: Function) -> Self
{
Self {
function,
environment: HashMap::new()
}
}
pub fn bind<'s>(
&mut self,
name: &'s str,
value: i32
) -> Result<(), EvaluationError<'s>>
{
let index = self
.function
.externals
.iter()
.enumerate()
.find_map(
|(index, variable)| {
if variable == name { Some(index) } else { None }
}
)
.ok_or(EvaluationError::UnrecognizedExternal(name))?;
self.environment.insert(index, value);
Ok(())
}
pub fn evaluate<R>(
&mut self,
args: impl IntoIterator<Item = i32>,
rng: &mut R
) -> Result<Evaluation, EvaluationError<'static>>
where
R: Rng + ?Sized
{
let arity = self.function.arity();
let args = args.into_iter().collect::<Vec<_>>();
if args.len() != arity
{
return Err(EvaluationError::BadArity {
expected: arity,
given: args.len()
});
}
let mut state = EvaluatorState::new(
rng,
self.function.register_count,
self.function.rolling_record_count
);
for (i, arg) in args.into_iter().enumerate()
{
state.registers[i] = arg;
}
for (index, value) in &self.environment
{
state.registers[arity + *index] = *value;
}
for instruction in &self.function.instructions
{
instruction.visit(&mut state).unwrap();
state.pc.allocate();
}
Ok(state.into())
}
}
impl Hash for Evaluator
{
fn hash<H: Hasher>(&self, state: &mut H)
{
self.function.hash(state);
self.environment.iter().for_each(|entry| entry.hash(state));
}
}
impl<R> InstructionVisitor<()> for EvaluatorState<'_, R>
where
R: Rng + ?Sized
{
fn visit_roll_range(&mut self, inst: &RollRange) -> Result<(), ()>
{
let start = self.value(inst.start);
let end = self.value(inst.end);
let record = roll_range(self.rng, start..=end);
*self.record_mut(inst.dest) = record;
Ok(())
}
fn visit_roll_standard_dice(
&mut self,
inst: &RollStandardDice
) -> Result<(), ()>
{
let count = self.value(inst.count);
let faces = self.value(inst.faces);
let record = roll_standard_dice(self.rng, count, faces);
*self.record_mut(inst.dest) = record;
Ok(())
}
fn visit_roll_custom_dice(
&mut self,
inst: &RollCustomDice
) -> Result<(), ()>
{
let count = self.value(inst.count);
let record = roll_custom_dice(self.rng, count, inst.faces.clone());
*self.record_mut(inst.dest) = record;
Ok(())
}
fn visit_drop_lowest(&mut self, inst: &DropLowest) -> Result<(), ()>
{
let count = self.value(inst.count);
let record = self.record_mut(inst.dest);
record.drop_lowest(count);
Ok(())
}
fn visit_drop_highest(&mut self, inst: &DropHighest) -> Result<(), ()>
{
let count = self.value(inst.count);
let record = self.record_mut(inst.dest);
record.drop_highest(count);
Ok(())
}
fn visit_sum_rolling_record(
&mut self,
inst: &SumRollingRecord
) -> Result<(), ()>
{
let sum = self.record_mut(inst.src).sum();
self.set_register(inst.dest, sum);
Ok(())
}
fn visit_add(&mut self, inst: &Add) -> Result<(), ()>
{
let op1 = self.value(inst.op1);
let op2 = self.value(inst.op2);
self.set_register(inst.dest, add(op1, op2));
Ok(())
}
fn visit_sub(&mut self, inst: &Sub) -> Result<(), ()>
{
let op1 = self.value(inst.op1);
let op2 = self.value(inst.op2);
self.set_register(inst.dest, sub(op1, op2));
Ok(())
}
fn visit_mul(&mut self, inst: &Mul) -> Result<(), ()>
{
let op1 = self.value(inst.op1);
let op2 = self.value(inst.op2);
self.set_register(inst.dest, mul(op1, op2));
Ok(())
}
fn visit_div(&mut self, inst: &Div) -> Result<(), ()>
{
let op1 = self.value(inst.op1);
let op2 = self.value(inst.op2);
self.set_register(inst.dest, div(op1, op2));
Ok(())
}
fn visit_mod(&mut self, inst: &Mod) -> Result<(), ()>
{
let op1 = self.value(inst.op1);
let op2 = self.value(inst.op2);
self.set_register(inst.dest, r#mod(op1, op2));
Ok(())
}
fn visit_exp(&mut self, inst: &Exp) -> Result<(), ()>
{
let op1 = self.value(inst.op1);
let op2 = self.value(inst.op2);
self.set_register(inst.dest, exp(op1, op2));
Ok(())
}
fn visit_neg(&mut self, inst: &Neg) -> Result<(), ()>
{
let op = self.value(inst.op);
self.set_register(inst.dest, neg(op));
Ok(())
}
fn visit_return(&mut self, inst: &Return) -> Result<(), ()>
{
self.result = self.value(inst.src);
Ok(())
}
}
impl<'r, R> EvaluatorState<'r, R>
where
R: Rng + ?Sized
{
fn new(rng: &'r mut R, registers: usize, records: usize) -> Self
{
Self {
rng,
pc: ProgramCounter::default(),
registers: vec![0; registers],
records: vec![RollingRecord::default(); records],
result: 0
}
}
fn value(&self, op: AddressingMode) -> i32
{
match op
{
AddressingMode::Immediate(value) => value.0,
AddressingMode::Register(reg) => self.registers[reg.0],
AddressingMode::RollingRecord(_) => unreachable!()
}
}
#[inline]
fn set_register(&mut self, reg: RegisterIndex, value: i32)
{
self.registers[reg.0] = value;
}
#[inline]
fn record_mut(&mut self, op: RollingRecordIndex) -> &mut RollingRecord
{
&mut self.records[op.0]
}
}
impl RollingRecordKind<i32>
{
pub fn count(&self) -> Option<i32>
{
match self
{
RollingRecordKind::Range { .. } => Some(1),
RollingRecordKind::Standard { count, .. } => Some(*count),
RollingRecordKind::Custom { count, .. } => Some(*count),
_ => None
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EvaluationError<'error>
{
CompilationFailed(ParseError<'error>),
OptimizationFailed,
BadArity
{
expected: usize,
given: usize
},
UnrecognizedExternal(&'error str)
}
impl Display for EvaluationError<'_>
{
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result
{
match self
{
EvaluationError::CompilationFailed(e) =>
{
write!(f, "{}", e)
},
EvaluationError::OptimizationFailed =>
{
write!(f, "optimization failed")
},
EvaluationError::BadArity { expected, given } =>
{
write!(
f,
"expected {} arguments but received {}",
expected, given
)
},
EvaluationError::UnrecognizedExternal(name) =>
{
write!(f, "unrecognized external variable: {}", name)
}
}
}
}
impl Error for EvaluationError<'_> {}
impl<'src> From<CompilationError<'src>> for EvaluationError<'src>
{
fn from(e: CompilationError<'src>) -> Self
{
match e
{
CompilationError::CompilationFailed(e) =>
{
Self::CompilationFailed(e)
},
CompilationError::OptimizationFailed => Self::OptimizationFailed
}
}
}
impl Evaluator
{
#[inline]
pub fn bounds(
&self,
args: impl IntoIterator<Item = i32>
) -> Result<Bounds, EvaluationError<'_>>
{
BoundsEvaluator::new(&self.function, &self.environment).evaluate(args)
}
}
#[derive(Debug, Clone)]
struct BoundsEvaluator<'eval>
{
function: &'eval Function,
environment: &'eval HashMap<usize, i32>,
pc: ProgramCounter,
registers: Vec<EvaluationBounds>,
sums: Vec<bool>,
records: Vec<RollingRecordBounds>,
result: EvaluationBounds,
count: Option<u128>
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct EvaluationBounds
{
pub min: i32,
pub max: i32
}
impl EvaluationBounds
{
#[inline]
pub fn contains(self, x: i32) -> bool { self.min <= x && x <= self.max }
}
impl Display for EvaluationBounds
{
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result
{
write!(f, "{}, {}", self.min, self.max)
}
}
impl From<i32> for EvaluationBounds
{
fn from(value: i32) -> Self
{
Self {
min: value,
max: value
}
}
}
impl From<(i32, i32)> for EvaluationBounds
{
fn from((min, max): (i32, i32)) -> Self { Self { min, max } }
}
impl<'eval> From<BoundsEvaluator<'eval>> for EvaluationBounds
{
fn from(evaluator: BoundsEvaluator<'eval>) -> Self { evaluator.result }
}
impl From<EvaluationBounds> for (i32, i32)
{
fn from(bounds: EvaluationBounds) -> Self { (bounds.min, bounds.max) }
}
impl From<EvaluationBounds> for RangeInclusive<i32>
{
fn from(bounds: EvaluationBounds) -> Self { bounds.min..=bounds.max }
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
struct RollingRecordBounds
{
kind: RollingRecordKind<EvaluationBounds>,
lowest_dropped: EvaluationBounds,
highest_dropped: EvaluationBounds
}
impl RollingRecordKind<EvaluationBounds>
{
pub fn count(&self) -> Option<EvaluationBounds>
{
match self
{
RollingRecordKind::Range { .. } => Some(1.into()),
RollingRecordKind::Standard { count, .. } => Some(*count),
RollingRecordKind::Custom { count, .. } => Some(*count),
_ => None
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Bounds
{
pub value: EvaluationBounds,
pub count: Option<u128>
}
impl Display for Bounds
{
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result
{
write!(
f,
"value ∈ [{}], count = {}",
self.value,
match self.count
{
Some(count) => count.to_string(),
None => "None".to_string()
}
)
}
}
impl<'eval> From<BoundsEvaluator<'eval>> for Bounds
{
fn from(evaluator: BoundsEvaluator<'eval>) -> Self
{
Self {
value: evaluator.result,
count: evaluator.count
}
}
}
impl<'eval> BoundsEvaluator<'eval>
{
fn new(
function: &'eval Function,
environment: &'eval HashMap<usize, i32>
) -> Self
{
Self {
function,
environment,
pc: ProgramCounter::default(),
registers: vec![
EvaluationBounds::default();
function.register_count
],
sums: vec![false; function.register_count],
records: vec![
RollingRecordBounds::default();
function.rolling_record_count
],
result: EvaluationBounds::default(),
count: Some(1)
}
}
pub fn evaluate(
mut self,
args: impl IntoIterator<Item = i32>
) -> Result<Bounds, EvaluationError<'eval>>
{
let arity = self.function.arity();
let args = args.into_iter().collect::<Vec<_>>();
if args.len() != arity
{
return Err(EvaluationError::BadArity {
expected: arity,
given: args.len()
});
}
for (i, arg) in args.into_iter().enumerate()
{
self.registers[i] = (arg, arg).into();
}
for (index, value) in self.environment
{
self.registers[arity + *index] = (*value, *value).into();
}
for instruction in &self.function.instructions
{
instruction.visit(&mut self).unwrap();
self.pc.allocate();
}
Ok(self.into())
}
fn value(&self, op: AddressingMode) -> EvaluationBounds
{
match op
{
AddressingMode::Immediate(value) => value.0.into(),
AddressingMode::Register(reg) => self.registers[reg.0],
AddressingMode::RollingRecord(_) => unreachable!()
}
}
fn sum(&self, op: AddressingMode) -> bool
{
match op
{
AddressingMode::Immediate(_) => false,
AddressingMode::Register(reg) => self.sums[reg.0],
AddressingMode::RollingRecord(_) => unreachable!()
}
}
#[inline]
fn set_register(
&mut self,
index: impl Into<usize>,
value: impl Into<EvaluationBounds>
)
{
self.registers[index.into()] = value.into();
}
#[inline]
fn set_sum(&mut self, index: impl Into<usize>, flag: bool)
{
self.sums[index.into()] = flag;
}
#[inline]
fn record(&self, op: RollingRecordIndex) -> &RollingRecordBounds
{
&self.records[op.0]
}
#[inline]
fn record_mut(&mut self, op: RollingRecordIndex)
-> &mut RollingRecordBounds
{
&mut self.records[op.0]
}
}
impl InstructionVisitor<()> for BoundsEvaluator<'_>
{
fn visit_roll_range(&mut self, inst: &RollRange) -> Result<(), ()>
{
let start = self.value(inst.start);
let end = self.value(inst.end);
let record = self.record_mut(inst.dest);
record.kind = RollingRecordKind::Range {
start: (start.min, start.max).into(),
end: (end.min, end.max).into()
};
if self.sum(inst.start) || self.sum(inst.end)
{
self.count = None;
}
Ok(())
}
fn visit_roll_standard_dice(
&mut self,
inst: &RollStandardDice
) -> Result<(), ()>
{
let count = self.value(inst.count);
let faces = self.value(inst.faces);
let record = self.record_mut(inst.dest);
record.kind = RollingRecordKind::Standard {
count: (count.min, count.max).into(),
faces: (faces.min, faces.max).into()
};
if self.sum(inst.count) || self.sum(inst.faces)
{
self.count = None;
}
Ok(())
}
fn visit_roll_custom_dice(
&mut self,
inst: &RollCustomDice
) -> Result<(), ()>
{
let count = self.value(inst.count);
let mut faces = inst.faces.clone();
faces.sort();
let record = self.record_mut(inst.dest);
record.kind = RollingRecordKind::Custom {
count: (count.min, count.max).into(),
faces
};
if self.sum(inst.count)
{
self.count = None;
}
Ok(())
}
fn visit_drop_lowest(&mut self, inst: &DropLowest) -> Result<(), ()>
{
let count = self.value(inst.count);
let record = self.record_mut(inst.dest);
record.lowest_dropped += count;
Ok(())
}
fn visit_drop_highest(&mut self, inst: &DropHighest) -> Result<(), ()>
{
let count = self.value(inst.count);
let record = self.record_mut(inst.dest);
record.highest_dropped += count;
Ok(())
}
fn visit_sum_rolling_record(
&mut self,
inst: &SumRollingRecord
) -> Result<(), ()>
{
let record = self.record(inst.src);
let count = record.kind.count().unwrap();
let count: EvaluationBounds =
(count.min.max(0), count.max.max(0)).into();
let lowest_dropped = record.lowest_dropped;
let highest_dropped = record.highest_dropped;
let kept =
(count - lowest_dropped - highest_dropped).clamp(0.into(), count);
let (sum, outcomes) = match record.kind
{
RollingRecordKind::Uninitialized => unreachable!(),
RollingRecordKind::Range { start, end } =>
{
match end.max < start.min
{
true =>
{
(0.into(), Some(1))
},
false => (
match start.max > end.min
{
false => kept * (start.min, end.max).into(),
true =>
{
let range: EvaluationBounds =
(start.min, end.max).into();
kept * range.union(Some(0.into())).unwrap()
}
},
self.count.map(|c| {
c.saturating_mul(
(end.max as i128)
.saturating_sub(start.min as i128)
.saturating_add(1)
.max(1) as u128
)
.max(1)
})
)
}
},
RollingRecordKind::Standard {
count: dice_count,
faces
} =>
{
(
kept * (faces.min.clamp(0, 1), faces.max.max(0)).into(),
self.count.map(|c| {
c.saturating_mul(
(faces.max.max(0) as u128)
.saturating_pow(dice_count.max.max(0) as u32)
.max(1)
)
.max(1)
})
)
},
RollingRecordKind::Custom {
count: dice_count,
ref faces
} =>
{
(
kept * (faces[0], faces[faces.len() - 1]).into(),
self.count.map(|c| {
c.saturating_mul(
(faces.len() as u128)
.saturating_pow(dice_count.max.max(0) as u32)
.max(1)
)
.max(1)
})
)
}
};
self.set_register(inst.dest, sum);
self.count = outcomes;
self.set_sum(inst.dest, true);
Ok(())
}
fn visit_add(&mut self, inst: &Add) -> Result<(), ()>
{
let op1 = self.value(inst.op1);
let op2 = self.value(inst.op2);
self.set_register(inst.dest, op1 + op2);
self.set_sum(inst.dest, self.sum(inst.op1) || self.sum(inst.op2));
Ok(())
}
fn visit_sub(&mut self, inst: &Sub) -> Result<(), ()>
{
let op1 = self.value(inst.op1);
let op2 = self.value(inst.op2);
self.set_register(inst.dest, op1 - op2);
self.set_sum(inst.dest, self.sum(inst.op1) || self.sum(inst.op2));
Ok(())
}
fn visit_mul(&mut self, inst: &Mul) -> Result<(), ()>
{
let op1 = self.value(inst.op1);
let op2 = self.value(inst.op2);
self.set_register(inst.dest, op1 * op2);
self.set_sum(inst.dest, self.sum(inst.op1) || self.sum(inst.op2));
Ok(())
}
fn visit_div(&mut self, inst: &Div) -> Result<(), ()>
{
let op1 = self.value(inst.op1);
let op2 = self.value(inst.op2);
self.set_register(inst.dest, op1 / op2);
self.set_sum(inst.dest, self.sum(inst.op1) || self.sum(inst.op2));
Ok(())
}
fn visit_mod(&mut self, inst: &Mod) -> Result<(), ()>
{
let op1 = self.value(inst.op1);
let op2 = self.value(inst.op2);
self.set_register(inst.dest, op1 % op2);
self.set_sum(inst.dest, self.sum(inst.op1) || self.sum(inst.op2));
Ok(())
}
fn visit_exp(&mut self, inst: &Exp) -> Result<(), ()>
{
let op1 = self.value(inst.op1);
let op2 = self.value(inst.op2);
self.set_register(inst.dest, op1.exp(op2));
self.set_sum(inst.dest, self.sum(inst.op1) || self.sum(inst.op2));
Ok(())
}
fn visit_neg(&mut self, inst: &Neg) -> Result<(), ()>
{
let op = self.value(inst.op);
self.set_register(inst.dest, -op);
self.set_sum(inst.dest, self.sum(inst.op));
Ok(())
}
fn visit_return(&mut self, inst: &Return) -> Result<(), ()>
{
self.result = self.value(inst.src);
Ok(())
}
}
impl std::ops::Add for EvaluationBounds
{
type Output = Self;
fn add(self, rhs: Self) -> Self::Output
{
Self {
min: add(self.min, rhs.min),
max: add(self.max, rhs.max)
}
}
}
impl std::ops::AddAssign for EvaluationBounds
{
fn add_assign(&mut self, rhs: Self) { *self = *self + rhs; }
}
impl std::ops::Sub for EvaluationBounds
{
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output
{
Self {
min: sub(self.min, rhs.max),
max: sub(self.max, rhs.min)
}
}
}
impl std::ops::SubAssign for EvaluationBounds
{
fn sub_assign(&mut self, rhs: Self) { *self = *self - rhs; }
}
impl std::ops::Mul for EvaluationBounds
{
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output
{
let min_min = mul(self.min, rhs.min);
let min_max = mul(self.min, rhs.max);
let max_min = mul(self.max, rhs.min);
let max_max = mul(self.max, rhs.max);
let min = min_min.min(min_max).min(max_min).min(max_max);
let max = min_min.max(min_max).max(max_min).max(max_max);
Self { min, max }
}
}
impl std::ops::MulAssign for EvaluationBounds
{
fn mul_assign(&mut self, rhs: Self) { *self = *self * rhs; }
}
impl std::ops::Div for EvaluationBounds
{
type Output = Self;
fn div(self, rhs: Self) -> Self::Output
{
match rhs
{
EvaluationBounds { min: 0, max: 0 } => Self { min: 0, max: 0 },
rhs if rhs.contains(0) =>
{
let mut ops = Vec::new();
if rhs.min < 0
{
ops.push(self / (rhs.min, -1).into());
}
if rhs.max > 0
{
ops.push(self / (1, rhs.max).into());
}
ops.push(self / 0.into());
let min = ops.iter().map(|b| b.min).min().unwrap();
let max = ops.iter().map(|b| b.max).max().unwrap();
Self { min, max }
},
rhs =>
{
let min_min = div(self.min, rhs.min);
let min_max = div(self.min, rhs.max);
let max_min = div(self.max, rhs.min);
let max_max = div(self.max, rhs.max);
let min = min_min.min(min_max).min(max_min).min(max_max);
let max = min_min.max(min_max).max(max_min).max(max_max);
Self { min, max }
}
}
}
}
impl std::ops::DivAssign for EvaluationBounds
{
fn div_assign(&mut self, rhs: Self) { *self = *self / rhs; }
}
impl std::ops::Rem for EvaluationBounds
{
type Output = Self;
fn rem(self, rhs: Self) -> Self::Output
{
match (self.into(), rhs.into())
{
((_, _), (0, 0)) => 0.into(),
((op1_min, op1_max), (op2_min, op2_max))
if op2_min == op2_max
&& op1_min.saturating_div(op2_min)
== op1_max.saturating_div(op2_min) =>
{
let rem1 = r#mod(op1_min, op2_min);
let rem2 = r#mod(op1_max, op2_min);
(rem1.min(rem2), rem1.max(rem2)).into()
},
((_, ..0), (op2_min @ ..0, _)) =>
{
(op2_min + 1, 0).into()
},
((_, ..0), (op2_min, _)) =>
{
(-op2_min + 1, 0).into()
},
((0.., _), (_, op2_max @ 0..)) =>
{
(0, (op2_max - 1).max(0)).into()
},
((0.., _), (_, op2_max)) =>
{
(0, (op2_max.saturating_neg() - 1).max(0)).into()
},
((_, _), (op2_min, op2_max)) if op2_min == op2_max =>
{
let abs = op2_min.saturating_abs();
(-abs + 1, abs - 1).into()
},
((_, _), (op2_min, op2_max)) =>
{
let op2_min = op2_min.saturating_abs();
let op2_max = op2_max.saturating_abs();
let abs_min = op2_min.min(op2_max);
let abs_max = op2_min.max(op2_max);
(-abs_min, abs_max - 1).into()
}
}
}
}
impl std::ops::RemAssign for EvaluationBounds
{
fn rem_assign(&mut self, rhs: Self) { *self = *self % rhs; }
}
impl std::ops::Neg for EvaluationBounds
{
type Output = Self;
fn neg(self) -> Self::Output
{
Self {
min: neg(self.max),
max: neg(self.min)
}
}
}
impl EvaluationBounds
{
fn clamp(self, min: EvaluationBounds, max: EvaluationBounds) -> Self
{
Self {
min: self.min.clamp(min.min, max.min),
max: self.max.clamp(min.max, max.max)
}
}
fn exp(self, rhs: EvaluationBounds) -> Self
{
let interesting_bases = HashSet::from([self.min, -1, 0, 1, self.max]);
let interesting_powers = HashSet::from([
rhs.min,
rhs.min.saturating_add(1),
(rhs.max.saturating_sub(1)).max(0),
rhs.max
]);
let mut result = None;
for base in interesting_bases
{
if self.contains(base)
{
for power in interesting_powers.clone()
{
if rhs.contains(power)
{
let value: EvaluationBounds = exp(base, power).into();
result = value.union(result);
}
}
}
}
result.unwrap()
}
#[inline]
fn union(self, rhs: Option<Self>) -> Option<Self>
{
match rhs
{
Some(rhs) => Some(Self {
min: self.min.min(rhs.min),
max: self.max.max(rhs.max)
}),
None => Some(self)
}
}
}