mod chain;
mod checks;
mod edges;
mod exact;
mod filter;
mod program;
mod statements;
#[cfg(test)]
mod tests;
use std::fmt::Write;
use implicit_predicates_core::eft::{PRODUCT_OPERAND_MAX, PRODUCT_OPERAND_MIN};
use crate::bound::{FilterConstants, MAX_DEGREE, filter_bound};
use crate::differences::difference_graph;
use crate::error::Error;
use crate::parse::{Predicate, parse};
use crate::polynomial::check_same_polynomial;
use crate::range::{ExactRange, Leaves, exact_plan_differences, exact_plan_raw};
use exact::{Check, Stage, write_in_range, write_product, write_stage};
use program::{Program, Step};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Generated {
pub name: String,
pub stages: String,
pub checks: String,
}
pub fn generate(source: &str, file: &str, regenerate: &str) -> Result<Vec<Generated>, Error> {
let notice = notice(file, regenerate);
parse(source, file)?
.iter()
.map(|predicate| generate_one(predicate, file, ¬ice))
.collect()
}
struct Forms {
constants: FilterConstants,
differences: (Program, ExactRange),
translated: (Program, ExactRange),
untranslated: Option<(Program, ExactRange)>,
}
fn generate_one(predicate: &Predicate, file: &str, notice: &str) -> Result<Generated, Error> {
let position = predicate.position;
let graph = difference_graph(&predicate.main, file, position)?;
if let Some(exact) = &predicate.exact {
let degree = exact.degree_bound();
if degree > u64::from(MAX_DEGREE) {
return Err(Error::at(
file,
predicate.exact_position.unwrap_or(position),
format!(
"the exact form of `{}` has a degree of up to {degree}, above {MAX_DEGREE}, \
the highest degree the filter accepts",
predicate.name
),
));
}
}
let constants = filter_bound(&graph, file, position)?.constants();
check_same_polynomial(predicate, file)?;
if graph.nodes.len() > edges::MAX_NODES {
return Err(Error::at(
file,
position,
format!(
"the main form of `{}` has {} distinct subexpressions, more than the {} \
for which the checks of its tests are generated: their edge cases take \
thousands of exact evaluations of the whole form",
predicate.name,
graph.nodes.len(),
edges::MAX_NODES
),
));
}
let (min, max) = (PRODUCT_OPERAND_MIN, PRODUCT_OPERAND_MAX);
let plan = |leaves| {
exact_plan_differences(&graph, leaves, file, position, min, max).map(|plan| {
(
Program::differences(&graph, leaves, &plan.lengths),
plan.range,
)
})
};
let differences = plan(Leaves::One)?;
let translated = plan(Leaves::Two)?;
let untranslated = match &predicate.exact {
Some(exact) => {
let position = predicate.exact_position.unwrap_or(position);
let plan = exact_plan_raw(exact, file, position, min, max)?;
Some((Program::raw(exact, &plan.lengths), plan.range))
}
None => None,
};
let forms = Forms {
constants,
differences,
translated,
untranslated,
};
let points = predicate.params.len();
let mut out = String::new();
write_header(&mut out, predicate, notice);
write_constants(&mut out, &forms);
let uses_max = filter::write_filter(&mut out, &graph, points, constants.degree);
write_exact_stages(&mut out, predicate, &forms);
chain::write_chain(&mut out, points, forms.untranslated.is_some());
if uses_max {
filter::write_max(&mut out);
}
write_in_range(&mut out);
let programs = [&forms.differences, &forms.translated]
.into_iter()
.chain(&forms.untranslated);
if programs
.flat_map(|(program, _)| &program.steps)
.any(|step| matches!(step, Step::Product { .. }))
{
write_product(&mut out);
}
let mut checks = String::new();
checks::write_checks(&mut checks, predicate, &graph, &constants, notice);
Ok(Generated {
name: predicate.name.clone(),
stages: out,
checks,
})
}
fn notice(file: &str, regenerate: &str) -> String {
let mut out = format!("// Generated by implicit-predicates-gen from {file}. Do not edit.\n");
for (i, line) in regenerate.lines().enumerate() {
let line = if i == 0 {
format!("To regenerate, {line}")
} else {
line.to_string()
};
writeln!(out, "// {line}").unwrap();
}
out.push('\n');
out
}
fn write_header(out: &mut String, predicate: &Predicate, notice: &str) {
out.push_str(notice);
comment(
out,
"//!",
0,
&format!(
"The stages of the predicate `{}`.\n\n\
Every stage takes the points {} of the definition as `p`, in that order, \
and returns the sign of the predicate, or `None` when it cannot decide. \
[`staged`] runs them in order.",
predicate.name,
list(&predicate.params)
),
);
out.push_str("\nuse implicit_predicates_core::{Sign, eft, expansion};\n\n");
}
fn list(names: &[String]) -> String {
let quoted: Vec<String> = names.iter().map(|n| format!("`{n}`")).collect();
match quoted.split_last() {
Some((last, rest)) if !rest.is_empty() => format!("{} and {last}", rest.join(", ")),
_ => quoted.concat(),
}
}
fn write_constants(out: &mut String, forms: &Forms) {
let c = &forms.constants;
let constant = |out: &mut String, doc: &str, name: &str, ty: &str, value: String| {
comment(out, "///", 0, doc);
writeln!(out, "pub const {name}: {ty} = {value};\n").unwrap();
};
constant(
out,
"The degree `k` of the predicate in the differences of coordinates.",
"DEGREE",
"u32",
c.degree.to_string(),
);
constant(
out,
"The coefficient of `D^k` in the error bound of [`filter`], \
`(EPSILON * D + KAPPA * E) * D^(k - 1)`, rounded up. `E` is the `e` given to \
the filter and `D` is `E` plus the largest magnitude of a difference of \
coordinates.",
"EPSILON",
"f64",
literal(c.epsilon),
);
constant(
out,
"The coefficient of `E * D^(k - 1)` in the error bound of [`filter`], rounded up.",
"KAPPA",
"f64",
literal(c.kappa),
);
constant(
out,
&format!(
"The smallest `D` for which [`filter`] decides, `2^{}`: below it, underflow \
could exceed the error bound.",
power_of_two(c.d_min)
),
"D_MIN",
"f64",
literal(c.d_min),
);
constant(
out,
&format!(
"The largest `D` for which [`filter`] decides, `2^{}`: above it, a computed \
value could overflow.",
power_of_two(c.d_max)
),
"D_MAX",
"f64",
literal(c.d_max),
);
let range = |out: &mut String, name: &str, what: &str, range: ExactRange| {
constant(
out,
&format!(
"`(min, max)`, `(2^{}, 2^{})`: {what} has a magnitude in `min..=max`.",
power_of_two(range.min),
power_of_two(range.max)
),
name,
"(f64, f64)",
format!("({}, {})", literal(range.min), literal(range.max)),
);
};
range(
out,
"DIFFERENCES_RANGE",
"[`exact_differences`] decides when every difference of coordinates is exact \
in `f64` and every nonzero one",
forms.differences.1,
);
range(
out,
"TRANSLATED_RANGE",
"[`expansions_translated`] decides when every nonzero coordinate",
forms.translated.1,
);
if let Some((_, r)) = &forms.untranslated {
range(
out,
"UNTRANSLATED_RANGE",
"[`expansions_untranslated`] decides when every nonzero coordinate",
*r,
);
}
}
fn write_exact_stages(out: &mut String, predicate: &Predicate, forms: &Forms) {
let points = predicate.params.len();
let doc = "The exact sign of the predicate: its main form evaluated with expansions on \
the differences of coordinates, each computed in `f64`.\n\n\
`None` means that this stage cannot decide: some difference is not exact in \
`f64`, or some nonzero difference has a magnitude outside [`DIFFERENCES_RANGE`], \
as when a coordinate is not finite.";
write_stage(
out,
&Stage {
name: "exact_differences",
doc,
program: &forms.differences.0,
check: Check::Differences("DIFFERENCES_RANGE"),
},
points,
);
let doc = "The exact sign of the predicate: its main form evaluated with expansions, \
each difference of coordinates as an exact expansion of two components.\n\n\
`None` means that this stage cannot decide: some nonzero coordinate has a \
magnitude outside [`TRANSLATED_RANGE`] or is not finite.";
write_stage(
out,
&Stage {
name: "expansions_translated",
doc,
program: &forms.translated.0,
check: Check::Coordinates("TRANSLATED_RANGE"),
},
points,
);
if let Some((program, _)) = &forms.untranslated {
let doc = "The exact sign of the predicate: its form for exact evaluation, marked \
`#[exact]` in the definition, evaluated with expansions on the coordinates.\n\n\
`None` means that this stage cannot decide: some nonzero coordinate has a \
magnitude outside [`UNTRANSLATED_RANGE`] or is not finite.";
write_stage(
out,
&Stage {
name: "expansions_untranslated",
doc,
program,
check: Check::Coordinates("UNTRANSLATED_RANGE"),
},
points,
);
}
}
const COMMENT_WIDTH: usize = 80;
pub(crate) fn comment(out: &mut String, marker: &str, indent: usize, text: &str) {
let room = COMMENT_WIDTH.saturating_sub(indent + marker.len() + 1);
for (i, paragraph) in text.split("\n\n").enumerate() {
if i > 0 {
writeln!(out, "{marker}").unwrap();
}
let mut line = String::new();
for word in paragraph.split_whitespace() {
if !line.is_empty() && line.len() + 1 + word.len() > room {
writeln!(out, "{marker} {line}").unwrap();
line.clear();
}
if !line.is_empty() {
line.push(' ');
}
line.push_str(word);
}
writeln!(out, "{marker} {line}").unwrap();
}
}
fn power_of_two(x: f64) -> i64 {
debug_assert!(x.is_normal() && x.to_bits() & ((1 << 52) - 1) == 0);
i64::try_from(x.to_bits() >> 52).expect("11 bits") - 1023
}
pub(crate) fn literal(x: f64) -> String {
debug_assert!(x.is_finite());
format!("{x:?}")
}