use crate::{Budget, EvaluationError, Location, QueryError, QueryErrorCategory};
pub fn evaluate_native_fixed_point<V, C, E>(
mut current: V,
context: &mut C,
budget: &mut Budget,
location: Location,
mut next: impl FnMut(&V, &mut C, &mut Budget) -> Result<V, EvaluationError<E>>,
mut copy: impl FnMut(&V, &mut C, &mut Budget) -> Result<V, EvaluationError<E>>,
mut same: impl FnMut(&V, &V, &mut C, &mut Budget) -> Result<bool, EvaluationError<E>>,
) -> Result<V, EvaluationError<E>> {
let mut checkpoint = copy(¤t, context, budget)?;
let mut power = 1usize;
let mut distance = 0usize;
loop {
let following = next(¤t, context, budget)?;
budget.charge(1, location.clone())?;
if same(&following, ¤t, context, budget)? {
return Ok(current);
}
if same(&following, &checkpoint, context, budget)? {
return Err(failure(
&location,
"fixpoint query cycles without reaching an unchanged selection",
)
.into());
}
current = following;
distance = distance
.checked_add(1)
.ok_or_else(|| failure(&location, "fixpoint iteration counter overflow"))?;
if distance == power {
checkpoint = copy(¤t, context, budget)?;
power = power
.checked_mul(2)
.ok_or_else(|| failure(&location, "fixpoint iteration counter overflow"))?;
distance = 0;
}
}
}
pub fn ordered_native_equal<T, E>(
left: &[T],
right: &[T],
budget: &mut Budget,
location: Location,
mut same_item: impl FnMut(&T, &T, &mut Budget) -> Result<bool, EvaluationError<E>>,
) -> Result<bool, EvaluationError<E>> {
budget.check_items(left.len(), location.clone())?;
budget.check_items(right.len(), location.clone())?;
budget.charge(1, location.clone())?;
if left.len() != right.len() {
return Ok(false);
}
for (left, right) in left.iter().zip(right) {
budget.charge(1, location.clone())?;
if !same_item(left, right, budget)? {
return Ok(false);
}
}
Ok(true)
}
fn failure(location: &Location, message: &str) -> QueryError {
QueryError {
location: location.clone(),
message: message.into(),
category: QueryErrorCategory::Other,
}
}