use implicit_predicates_core::eft::{PRODUCT_OPERAND_MAX, PRODUCT_OPERAND_MIN};
use implicit_predicates_gen::{
ExactRange, FilterForm, Generated, Leaves, MAX_COORD_COMPONENTS, difference_graph,
exact_plan_differences, exact_plan_raw, exact_plan_spans_translated,
exact_plan_spans_untranslated, filter_bound, filter_form, generate, parse,
};
use proc_macro2::TokenStream;
const ORIENT3D: &str = include_str!("fixtures/orient3d.pred");
const INSPHERE: &str = include_str!("fixtures/insphere.pred");
const POINTS: &str = include_str!("fixtures/points.pred");
const REGENERATE: &str = "run the tests.";
fn generated(source: &str, file: &str) -> Generated {
let mut all = generate(source, file, REGENERATE).unwrap();
assert_eq!(all.len(), 1);
all.remove(0)
}
fn constant<'a>(code: &'a str, name: &str, ty: &str) -> &'a str {
let prefix = format!("pub const {name}: {ty} = ");
let start = code
.find(&prefix)
.unwrap_or_else(|| panic!("no constant {name}"))
+ prefix.len();
let end = start + code[start..].find(';').unwrap();
code[start..end].trim()
}
fn assert_literal(text: &str, expected: f64) {
let value: f64 = text.parse().unwrap_or_else(|_| panic!("{text}"));
assert_eq!(
value.to_bits(),
expected.to_bits(),
"{text} is not {expected:e}"
);
}
fn assert_range(code: &str, name: &str, range: ExactRange) {
let text = constant(code, name, "(f64, f64)");
let inner = text.strip_prefix('(').unwrap().strip_suffix(')').unwrap();
let (min, max) = inner.split_once(',').unwrap();
assert_literal(min.trim(), range.min);
assert_literal(max.trim(), range.max);
}
#[test]
fn generation_is_deterministic() {
for (source, file) in [
(ORIENT3D, "predicates/orient3d.pred"),
(INSPHERE, "predicates/insphere.pred"),
] {
let first = generate(source, file, REGENERATE).unwrap();
let second = generate(source, file, REGENERATE).unwrap();
assert_eq!(first.len(), second.len());
for (a, b) in first.iter().zip(&second) {
assert_eq!(a.name, b.name);
assert_eq!(a.stages, b.stages);
assert_eq!(a.checks, b.checks);
}
}
}
#[test]
fn the_output_parses_as_rust() {
for (source, file) in [
(ORIENT3D, "predicates/orient3d.pred"),
(INSPHERE, "predicates/insphere.pred"),
] {
let g = generated(source, file);
g.stages
.parse::<TokenStream>()
.unwrap_or_else(|e| panic!("{file}: {e}"));
}
}
fn array(text: &str) -> Vec<&str> {
let inner = text.strip_prefix('[').unwrap().strip_suffix(']').unwrap();
inner.split(',').map(str::trim).collect()
}
#[test]
fn the_constants_in_the_output_are_the_derived_ones() {
let g = generated(ORIENT3D, "predicates/orient3d.pred");
let code = &g.stages;
let p = parse(ORIENT3D, "t.pred").unwrap().remove(0);
let diff = difference_graph(&p.main, "t.pred", p.position).unwrap();
let FilterForm::PerAxis(bound) = filter_form(&diff, "t.pred", p.position).unwrap() else {
panic!("orient3d is multihomogeneous");
};
let constants = bound.constants();
assert_eq!(constant(code, "DEGREES", "[u32; 3]"), "[1, 1, 1]");
assert_eq!(constants.degrees, [1, 1, 1]);
assert_literal(constant(code, "EPSILON", "f64"), constants.epsilon);
let kappa = array(constant(code, "KAPPA", "[f64; 3]"));
assert_eq!(kappa.len(), 3);
for (text, &value) in kappa.iter().zip(&constants.kappa) {
assert_literal(text, value);
}
assert_literal(constant(code, "D_MIN", "f64"), constants.d_min);
assert_literal(constant(code, "D_MAX", "f64"), constants.d_max);
assert!(bound.epsilon_is_u_times(46));
for axis in 0..3 {
assert!(bound.kappa_is(axis, 6), "axis {axis}");
assert!(!bound.kappa_is(axis, 5) && !bound.kappa_is(axis, 7));
}
assert_eq!(constants.d_min, 2f64.powi(-324));
assert_eq!(constants.d_max, 2f64.powi(339));
let global = filter_bound(&diff, "t.pred", p.position)
.unwrap()
.constants();
assert_eq!(
(global.d_min, global.d_max),
(constants.d_min, constants.d_max)
);
assert_eq!(global.epsilon, constants.epsilon);
let (min, max) = (PRODUCT_OPERAND_MIN, PRODUCT_OPERAND_MAX);
let one = exact_plan_differences(&diff, Leaves::One, "t.pred", p.position, min, max).unwrap();
let two = exact_plan_differences(&diff, Leaves::Two, "t.pred", p.position, min, max).unwrap();
let raw = exact_plan_raw(
p.exact.as_ref().unwrap(),
"t.pred",
p.exact_position.unwrap(),
min,
max,
)
.unwrap();
assert_range(code, "DIFFERENCES_RANGE", one.range);
assert_range(code, "TRANSLATED_RANGE", two.range);
assert_range(code, "UNTRANSLATED_RANGE", raw.range);
assert_eq!(one.range.min, 2f64.powi(-188));
assert_eq!(one.range.max, 2f64.powi(239));
assert_eq!(two.range.max, 2f64.powi(238));
assert_eq!(raw.range.max, 2f64.powi(239));
}
#[test]
fn the_constants_of_a_global_filter_are_the_derived_ones() {
let g = generated(INSPHERE, "predicates/insphere.pred");
let code = &g.stages;
let p = parse(INSPHERE, "t.pred").unwrap().remove(0);
let diff = difference_graph(&p.main, "t.pred", p.position).unwrap();
let FilterForm::Global(bound) = filter_form(&diff, "t.pred", p.position).unwrap() else {
panic!("in-sphere is not multihomogeneous");
};
let constants = bound.constants();
assert_eq!(constant(code, "DEGREE", "u32"), "5");
assert_literal(constant(code, "EPSILON", "f64"), constants.epsilon);
assert_literal(constant(code, "KAPPA", "f64"), constants.kappa);
assert_literal(constant(code, "D_MIN", "f64"), constants.d_min);
assert_literal(constant(code, "D_MAX", "f64"), constants.d_max);
assert!(bound.kappa_is(360));
assert!(!code.contains("DEGREES"));
}
#[test]
fn the_header_says_do_not_edit() {
let g = generated(ORIENT3D, "predicates/orient3d.pred");
let mut lines = g.stages.lines();
assert_eq!(
lines.next(),
Some("// Generated by implicit-predicates-gen from predicates/orient3d.pred. Do not edit.")
);
assert_eq!(lines.next(), Some("// To regenerate, run the tests."));
}
#[test]
fn a_predicate_without_an_exact_form_has_no_untranslated_stage_and_chains_the_main_form() {
let g = generated(INSPHERE, "predicates/insphere.pred");
assert_eq!(g.name, "insphere");
assert!(!g.stages.contains("expansions_untranslated"));
assert!(!g.stages.contains("UNTRANSLATED_RANGE"));
for stage in ["exact_differences", "expansions_translated("] {
assert!(!g.stages.contains(stage), "{stage}");
}
assert!(!g.stages.contains("DIFFERENCES_RANGE"));
assert!(!g.stages.contains("pub const TRANSLATED_RANGE"));
assert!(g.stages.contains("pub fn expansions_translated_in("));
let staged = &g.stages[g.stages.find("pub fn staged").unwrap()..];
let signature = &staged[..staged.find('{').unwrap()];
assert_eq!(
signature,
"pub fn staged(scratch: &mut Scratch<'_>, p: [&[f64; 3]; 5]) \
-> Result<Option<Sign>, ScratchExhausted> "
);
let body = &staged[..staged.find("\n}\n\n").unwrap()];
assert!(body.contains("filter(p, 0.0)"), "{body}");
assert!(body.contains("staged_in_arena(scratch, p)"), "{body}");
let arena = &g.stages[g.stages.find("fn staged_in_arena").unwrap()..];
let arena = &arena[..arena.find("\n}\n\n").unwrap()];
let order = [
"let mark = scratch.mark();",
"exact(scratch, p[0])?",
"expansions_translated_in(scratch, q)?",
"scratch.reset(mark);",
]
.map(|text| {
arena
.rfind(text)
.unwrap_or_else(|| panic!("{text}: {arena}"))
});
assert!(
order[1] < order[0] && order[2] < order[0] && order[0] < order[3],
"{arena}"
);
}
#[test]
fn orient3d_keeps_its_three_stages_on_the_stack_and_its_chain() {
let g = generated(ORIENT3D, "predicates/orient3d.pred");
for stage in [
"exact_differences",
"expansions_translated",
"expansions_untranslated",
"expansions_translated_in",
"expansions_untranslated_in",
] {
assert!(g.stages.contains(&format!("pub fn {stage}(")), "{stage}");
}
assert!(
g.stages
.contains("pub fn staged(p: [&[f64; 3]; 4]) -> Option<Sign> {")
);
assert!(!g.stages.contains("staged_in_arena"));
}
#[test]
fn the_ranges_over_spans_in_the_output_are_the_derived_ones() {
let g = generated(ORIENT3D, "predicates/orient3d.pred");
let code = &g.stages;
assert_eq!(constant(code, "MAX_COORD_COMPONENTS", "usize"), "16");
assert_eq!(MAX_COORD_COMPONENTS, 16);
let p = parse(ORIENT3D, "t.pred").unwrap().remove(0);
let diff = difference_graph(&p.main, "t.pred", p.position).unwrap();
let (min, max) = (PRODUCT_OPERAND_MIN, PRODUCT_OPERAND_MAX);
let translated =
exact_plan_spans_translated(&diff, 16, "t.pred", p.position, min, max).unwrap();
let exact = p.exact.as_ref().unwrap();
let untranslated =
exact_plan_spans_untranslated(exact, 16, "t.pred", p.position, min, max).unwrap();
assert_range(code, "SPANS_TRANSLATED_RANGE", translated.range);
assert_range(code, "SPANS_UNTRANSLATED_RANGE", untranslated.range);
assert_eq!(translated.range.min, 2f64.powi(-188));
assert_eq!(translated.range.max, 2f64.powi(234));
assert_eq!(untranslated.range.min, 2f64.powi(-188));
assert_eq!(untranslated.range.max, 2f64.powi(235));
}
#[test]
fn the_ranges_over_spans_do_not_depend_on_the_point_types_of_the_file() {
let alone = generated(ORIENT3D, "predicates/orient3d.pred");
let with_points = format!("{POINTS}\n{ORIENT3D}");
let mut all = generate(&with_points, "predicates/orient3d.pred", REGENERATE).unwrap();
assert_eq!(all.len(), 1);
let with_points = all.remove(0);
for name in [
"MAX_COORD_COMPONENTS",
"SPANS_TRANSLATED_RANGE",
"SPANS_UNTRANSLATED_RANGE",
] {
let ty = if name == "MAX_COORD_COMPONENTS" {
"usize"
} else {
"(f64, f64)"
};
assert_eq!(
constant(&alone.stages, name, ty),
constant(&with_points.stages, name, ty),
"{name}"
);
}
assert_eq!(alone.stages, with_points.stages);
}
#[test]
fn the_chain_tries_the_translated_form_before_the_exact_form() {
let o = generated(ORIENT3D, "predicates/orient3d.pred");
let staged = &o.stages[o.stages.find("pub fn staged").unwrap()..];
let body = &staged[..staged.find("\n}").unwrap()];
let order = [
"filter(p, [0.0; 3])",
"exact_differences(p)",
"expansions_translated(p)",
"expansions_untranslated(p)",
]
.map(|stage| {
body.find(stage)
.unwrap_or_else(|| panic!("{stage}: {body}"))
});
assert!(order.is_sorted(), "{body}");
}
#[test]
fn shared_subexpressions_are_emitted_once() {
let g = generated(ORIENT3D, "predicates/orient3d.pred");
assert_eq!(
g.stages
.matches("expansion::from_two_product(p[2][1], p[3][2],")
.count(),
1
);
let s = generated(INSPHERE, "predicates/insphere.pred");
let filter = &s.stages[s.stages.find("pub fn filter").unwrap()..];
let filter = &filter[..filter.find("\n}").unwrap()];
assert_eq!(filter.matches("p[2][1] - p[4][1]").count(), 1, "{filter}");
}
#[test]
fn a_definition_error_is_reported_with_its_position() {
let e = generate(
"fn p(a: Point3, b: Point3) -> Sign {\n a.x * b.y\n}",
"predicates/p.pred",
REGENERATE,
)
.unwrap_err();
assert_eq!(e.file, "predicates/p.pred");
assert_eq!((e.line, e.column), (1, 4), "{e}");
}
#[test]
fn the_header_names_the_file_and_tells_how_to_regenerate() {
let all = generate(
ORIENT3D,
"defs/o.pred",
"run, from the root:\nmake predicates",
)
.unwrap();
let expected = "// Generated by implicit-predicates-gen from defs/o.pred. Do not edit.\n\
// To regenerate, run, from the root:\n\
// make predicates\n\n";
for code in [&all[0].stages, &all[0].checks] {
assert!(code.starts_with(expected), "{}", &code[..200]);
}
}