implicit-predicates-gen 0.0.1

Code generator for exact geometric predicates over explicit and implicit points
Documentation
//! Tests for the code generated for a predicate.

use implicit_predicates_core::eft::{PRODUCT_OPERAND_MAX, PRODUCT_OPERAND_MIN};
use implicit_predicates_gen::{
    ExactRange, Generated, Leaves, difference_graph, exact_plan_differences, exact_plan_raw,
    filter_bound, generate, parse,
};
use proc_macro2::TokenStream;

const ORIENT3D: &str = include_str!("fixtures/orient3d.pred");
const INSPHERE: &str = include_str!("fixtures/insphere.pred");

/// How the tests say to regenerate the code.
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)
}

/// The text after `pub const NAME: TYPE = ` up to the next `;`.
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()
}

/// Parses a float literal of the output and checks it is exactly `expected`.
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() {
    // The output is lexed here; that it is valid Rust is shown by the command
    // line tool, which formats it with rustfmt, and by the generated file,
    // which is compiled as part of the workspace.
    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}"));
    }
}

#[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 constants = filter_bound(&diff, "t.pred", p.position)
        .unwrap()
        .constants();
    assert_eq!(
        constant(code, "DEGREE", "u32"),
        constants.degree.to_string()
    );
    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);
    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);
    // The values themselves, as derived for orient3d.
    assert_eq!(constants.degree, 3);
    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_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.")
    );
    // The regeneration text given to `generate`, after "To regenerate,".
    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"));
    assert!(g.stages.contains("pub fn expansions_translated"));
    let staged = &g.stages[g.stages.find("pub fn staged").unwrap()..];
    let body = &staged[..staged.find("\n}").unwrap()];
    let filter = body.find("filter(p, 0.0)").unwrap();
    let differences = body.find("exact_differences(p)").unwrap();
    let translated = body.find("expansions_translated(p)").unwrap();
    assert!(filter < differences && differences < translated, "{body}");

    assert!(!body.contains("expansions_untranslated(p)"), "{body}");
}

#[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)",
        "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() {
    // `c.y * d.z` is shared by two cofactors of the exact form of orient3d.
    let g = generated(ORIENT3D, "predicates/orient3d.pred");
    assert_eq!(
        g.stages
            .matches("expansion::from_two_product(p[2][1], p[3][2],")
            .count(),
        1
    );
    // In-sphere: the minor `cy * dz - cz * dy` is shared by two cofactors,
    // and the leaf `c.y - e.y` by many products.
    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]);
    }
}