use super::filter::FilterKind;
use super::grid::{FINITE_KEYS, GRID, SplitMix64, exponent, from_key, grid_points, key, pow2};
use crate::bound::AxisConstants;
use crate::differences::{DiffGraph, DiffNode};
use crate::dyadic::Dyadic;
pub(crate) type Case = Vec<[f64; 3]>;
pub(crate) const MAX_NODES: usize = 256;
const PER_SCALE: usize = 6;
const ATTEMPTS_PER_SCALE: usize = 64;
const UNCERTAIN_PER_SCALE: usize = 2;
const UNCERTAIN_ATTEMPTS_PER_SCALE: usize = 16;
pub(crate) fn bound_edge_cases(
graph: &DiffGraph,
points: usize,
filter: &FilterKind,
) -> Vec<(Case, Case)> {
let form = Form { graph, filter };
let mut random = SplitMix64(0x2545_f491_4f6c_dd1d);
let mut cases = Vec::new();
for scale in form.scales() {
let found = form.search(
&mut random,
points,
scale,
[0.0; 3],
PER_SCALE,
ATTEMPTS_PER_SCALE,
);
cases.extend(found);
}
cases
}
pub(crate) fn uncertainty_edge_cases(
graph: &DiffGraph,
points: usize,
constants: &AxisConstants,
) -> Vec<([f64; 3], Case, Case)> {
let filter = FilterKind::PerAxis(*constants, false);
let form = Form {
graph,
filter: &filter,
};
let axes: Vec<usize> = (0..3).filter(|&k| constants.degrees[k] >= 1).collect();
let mut choices: Vec<Vec<usize>> = axes.iter().map(|&k| vec![k]).collect();
if axes.len() > 1 {
choices.push(axes.clone());
}
let mut random = SplitMix64(0x9fb2_1c65_1e98_df25);
let mut cases = Vec::new();
for scale in form.scales() {
for choice in &choices {
let mut e = [0.0; 3];
for &k in choice {
e[k] = pow2(scale - 8 - random.below(33) as i32);
}
let found = form.search(
&mut random,
points,
scale,
e,
UNCERTAIN_PER_SCALE,
UNCERTAIN_ATTEMPTS_PER_SCALE,
);
cases.extend(found.into_iter().map(|(below, above)| (e, below, above)));
}
}
cases
}
struct Form<'a> {
graph: &'a DiffGraph,
filter: &'a FilterKind,
}
impl Form<'_> {
fn range(&self) -> (i32, i32) {
let (d_min, d_max) = match self.filter {
FilterKind::Global(c) => (c.d_min, c.d_max),
FilterKind::PerAxis(c, _) => (c.d_min, c.d_max),
};
(exponent(d_min), exponent(d_max))
}
fn scales(&self) -> Vec<i32> {
let (low, high) = self.range();
[low + 1, low + 40, -100, -30, 0, 30, 100, high - 12]
.into_iter()
.filter(|&s| low < s && s + 8 <= high)
.collect()
}
fn search(
&self,
random: &mut SplitMix64,
points: usize,
scale: i32,
e: [f64; 3],
wanted: usize,
attempts: usize,
) -> Vec<(Case, Case)> {
let mut cases = Vec::new();
for _ in 0..attempts {
if cases.len() == wanted {
break;
}
let start = grid_points(random, points, scale);
let moved: Vec<(usize, usize)> = (0..points)
.flat_map(|i| (0..3).map(move |axis| (i, axis)))
.filter(|&(i, axis)| start[i][axis] != 0.0)
.collect();
if moved.is_empty() {
continue;
}
let (point, axis) = moved[random.below(moved.len() as u64) as usize];
let up = random.below(2) == 0;
let line = Line {
form: self,
start,
point,
axis,
e,
};
if let Some(pair) = line.edge_pair(scale, up) {
cases.push(pair);
}
}
cases
}
fn value(&self, p: &[[f64; 3]]) -> Dyadic {
let mut values: Vec<Dyadic> = Vec::with_capacity(self.graph.nodes.len());
for node in &self.graph.nodes {
let value = match *node {
DiffNode::Leaf {
axis,
minuend,
subtrahend,
} => {
let axis = usize::from(axis);
&Dyadic::from_f64(p[minuend][axis]) - &Dyadic::from_f64(p[subtrahend][axis])
}
DiffNode::Neg(x) => &Dyadic::integer(0) - &values[x],
DiffNode::Add(x, y) => &values[x] + &values[y],
DiffNode::Sub(x, y) => &values[x] - &values[y],
DiffNode::Mul(x, y) => &values[x] * &values[y],
};
values.push(value);
}
values.swap_remove(self.graph.root)
}
fn above_bound(&self, p: &[[f64; 3]], e: [f64; 3]) -> Option<bool> {
let mut delta = [0.0f64; 3];
for node in &self.graph.nodes {
if let DiffNode::Leaf {
axis,
minuend,
subtrahend,
} = *node
{
let k = usize::from(axis);
delta[k] = delta[k].max((p[minuend][k] - p[subtrahend][k]).abs());
}
}
let bound = match self.filter {
FilterKind::Global(c) => {
let delta = delta[0].max(delta[1]).max(delta[2]);
if !(c.d_min..=c.d_max).contains(&delta) {
return None;
}
let delta = Dyadic::from_f64(delta);
let mut bound = Dyadic::from_f64(c.epsilon);
for _ in 0..c.degree {
bound = &bound * δ
}
bound
}
FilterKind::PerAxis(c, _) => axis_bound(c, delta, e)?,
};
let value = self.value(p);
Some(value > bound || value < &Dyadic::integer(0) - &bound)
}
fn sign(&self, p: &[[f64; 3]]) -> i8 {
match self.value(p).cmp(&Dyadic::integer(0)) {
std::cmp::Ordering::Less => -1,
std::cmp::Ordering::Equal => 0,
std::cmp::Ordering::Greater => 1,
}
}
}
struct Line<'a> {
form: &'a Form<'a>,
start: Case,
point: usize,
axis: usize,
e: [f64; 3],
}
impl Line<'_> {
fn at(&self, key: i128) -> Case {
let mut p = self.start.clone();
p[self.point][self.axis] = from_key(key);
p
}
fn edge_pair(&self, scale: i32, up: bool) -> Option<(Case, Case)> {
let (below, above) = self.zero(scale)?;
let (origin, step) = if up { (above, 1) } else { (below, -1) };
let side = |n: i128| -> Option<bool> {
let key = origin + step * n;
if !(-FINITE_KEYS..=FINITE_KEYS).contains(&key) {
return None;
}
self.form.above_bound(&self.at(key), self.e)
};
if side(0)? {
return None;
}
let mut high: i128 = 1;
while !side(high)? {
high = high.checked_mul(2).filter(|&h| h <= FINITE_KEYS)?;
}
let mut low = high / 2;
while high - low > 1 {
let middle = low + (high - low) / 2;
if side(middle)? {
high = middle;
} else {
low = middle;
}
}
Some((self.at(origin + step * low), self.at(origin + step * high)))
}
fn zero(&self, scale: i32) -> Option<(i128, i128)> {
let sign = |key: i128| self.form.sign(&self.at(key));
let start = key(self.start[self.point][self.axis]);
let first = sign(start);
if first == 0 {
return Some((start, start));
}
let other = (-4 * GRID..=4 * GRID)
.map(|n| key(n as f64 * pow2(scale)))
.filter(|&k| sign(k) != first)
.min_by_key(|&k| (k - start).abs())?;
let (mut same, mut changed) = (start, other);
while (changed - same).abs() > 1 {
let middle = same + (changed - same) / 2;
match sign(middle) {
0 => return Some((middle, middle)),
s if s == first => same = middle,
_ => changed = middle,
}
}
if sign(changed) == 0 {
Some((changed, changed))
} else {
Some((same.min(changed), same.max(changed)))
}
}
}
fn axis_bound(c: &AxisConstants, delta: [f64; 3], e: [f64; 3]) -> Option<Dyadic> {
let mut d = [Dyadic::integer(0), Dyadic::integer(0), Dyadic::integer(0)];
for k in (0..3).filter(|&k| c.degrees[k] >= 1) {
let dk = delta[k] + e[k];
if !(c.d_min..=c.d_max).contains(&dk) {
return None;
}
d[k] = Dyadic::from_f64(dk);
}
let power = |skip: Option<usize>| {
let mut x = Dyadic::integer(1);
for (k, dk) in d.iter().enumerate() {
for _ in 0..c.degrees[k] - u32::from(skip == Some(k)) {
x = &x * dk;
}
}
x
};
let mut bound = &Dyadic::from_f64(c.epsilon) * &power(None);
for k in (0..3).filter(|&k| c.degrees[k] >= 1) {
let term = &(&Dyadic::from_f64(c.kappa[k]) * &Dyadic::from_f64(e[k])) * &power(Some(k));
bound = &bound + &term;
}
Some(bound)
}