use std::fmt::Write;
use super::comment;
use super::edges::{Case, bound_edge_cases};
use super::statements::node_statements;
use crate::bound::FilterConstants;
use crate::differences::DiffGraph;
use crate::parse::Predicate;
pub(crate) fn write_checks(
out: &mut String,
predicate: &Predicate,
graph: &DiffGraph,
constants: &FilterConstants,
notice: &str,
) {
let points = predicate.params.len();
let ty = format!("[[f64; 3]; {points}]");
out.push_str(notice);
comment(
out,
"//!",
0,
&format!(
"Checks of the predicate `{}`, for its tests: [`exact`], its exact sign, and \
[`bound_edge_cases`], inputs at the edge of the error bound of its filter.",
predicate.name
),
);
out.push_str("\nuse implicit_predicates::Sign;\nuse implicit_predicates_oracle::Dyadic;\n\n");
comment(
out,
"///",
0,
"The exact sign of the predicate at the points `p`: its main form, evaluated \
with exact dyadic numbers in the order of the definition.\n\n\
# Panics\n\n\
Panics if a coordinate is not finite.",
);
writeln!(
out,
"pub fn exact(p: &{ty}) -> Sign {{\n\
let c = p.map(|point| point.map(Dyadic::from_f64));"
)
.unwrap();
for statement in node_statements(graph, "c", true) {
writeln!(out, "{statement}").unwrap();
}
out.push_str(
"match det.sign() {\n\
1 => Sign::Positive,\n\
-1 => Sign::Negative,\n\
_ => Sign::Zero,\n\
}\n}\n\n",
);
let cases = bound_edge_cases(graph, points, constants);
comment(
out,
"///",
0,
&format!(
"Inputs on either side of the error bound of the filter for explicit points, \
in pairs: the first of each pair has an exact magnitude at most \
`EPSILON * delta^{0}`, the second above it, where `delta` is the largest \
difference of coordinates as the filter computes it; the two differ by one \
representable value in one coordinate.\n\n\
Each pair starts from points on a grid at which the predicate is zero, or \
changes sign, and moves one coordinate away from there. The scales go from \
just above `D_MIN` to just below `D_MAX`.",
constants.degree
),
);
writeln!(
out,
"pub fn bound_edge_cases() -> Vec<{ty}> {{\n\
BOUND_EDGE_CASES\n\
.into_iter()\n\
.map(|case| case.map(|point| point.map(f64::from_bits)))\n\
.collect()\n}}\n"
)
.unwrap();
comment(
out,
"///",
0,
"The bit patterns of the coordinates of [`bound_edge_cases`].",
);
writeln!(
out,
"const BOUND_EDGE_CASES: [[[u64; 3]; {points}]; {}] = [",
2 * cases.len()
)
.unwrap();
for (below, above) in &cases {
write_case(out, below);
write_case(out, above);
}
out.push_str("];\n");
}
fn write_case(out: &mut String, case: &Case) {
let points: Vec<String> = case
.iter()
.map(|point| {
let bits: Vec<String> = point
.iter()
.map(|x| format!("{:#018x}", x.to_bits()))
.collect();
format!("[{}]", bits.join(", "))
})
.collect();
writeln!(out, "[{}],", points.join(", ")).unwrap();
}