use std::fmt::Write;
use super::exact::{Check, Stage, write_stage};
use super::filter::{self, FilterKind};
use super::filter_axis;
use super::program::Program;
use super::spans::{SpanForm, SpanStage, write_span_stage};
use super::{comment, literal, power_of_two};
use crate::differences::DiffGraph;
use crate::graph::Graph;
use crate::range::{ExactRange, MAX_COORD_COMPONENTS};
pub(crate) const STACK_LIMIT: usize = 4096;
pub(crate) fn fits_on_stack(total: usize) -> bool {
total <= STACK_LIMIT
}
pub(crate) struct Forms<'a> {
pub(crate) filter: FilterKind,
pub(crate) differences: Option<(Program, ExactRange)>,
pub(crate) translated: Option<(Program, ExactRange)>,
pub(crate) untranslated: Option<(Program, ExactRange)>,
pub(crate) spans_translated: (&'a DiffGraph, ExactRange),
pub(crate) spans_untranslated: Option<(&'a Graph, ExactRange)>,
}
impl Forms<'_> {
pub(crate) fn stack_stages(&self) -> impl Iterator<Item = (&'static str, &Program)> {
[
("exact_differences", &self.differences),
("expansions_translated", &self.translated),
("expansions_untranslated", &self.untranslated),
]
.into_iter()
.filter_map(|(name, stage)| stage.as_ref().map(|(program, _)| (name, program)))
}
}
pub(crate) fn write_constants(out: &mut String, forms: &Forms) {
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();
};
match &forms.filter {
FilterKind::Global(c) => filter::write_constants(out, c),
FilterKind::PerAxis(c, _) => filter_axis::write_constants(out, c),
}
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)),
);
};
if let Some((_, r)) = &forms.differences {
range(
out,
"DIFFERENCES_RANGE",
"[`exact_differences`] decides when every difference of coordinates is exact \
in `f64` and every nonzero one",
*r,
);
}
if let Some((_, r)) = &forms.translated {
range(
out,
"TRANSLATED_RANGE",
"[`expansions_translated`] decides when every nonzero coordinate",
*r,
);
}
if let Some((_, r)) = &forms.untranslated {
range(
out,
"UNTRANSLATED_RANGE",
"[`expansions_untranslated`] decides when every nonzero coordinate",
*r,
);
}
let stages = if forms.spans_untranslated.is_some() {
"[`expansions_translated_in`] and [`expansions_untranslated_in`] accept"
} else {
"[`expansions_translated_in`] accepts"
};
constant(
out,
&format!("The most components of a coordinate that {stages}."),
"MAX_COORD_COMPONENTS",
"usize",
MAX_COORD_COMPONENTS.to_string(),
);
let what =
|name: &str| format!("[`{name}`] decides when every nonzero component of every coordinate");
range(
out,
"SPANS_TRANSLATED_RANGE",
&what("expansions_translated_in"),
forms.spans_translated.1,
);
if let Some((_, r)) = forms.spans_untranslated {
range(
out,
"SPANS_UNTRANSLATED_RANGE",
&what("expansions_untranslated_in"),
r,
);
}
}
pub(crate) fn write_exact_stages(out: &mut String, points: usize, forms: &Forms) {
if let Some((program, _)) = &forms.differences {
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.";
let check = Check::Differences("DIFFERENCES_RANGE");
let name = "exact_differences";
write_stage(
out,
&Stage {
name,
doc,
program,
check,
},
points,
);
}
if let Some((program, _)) = &forms.translated {
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.";
let check = Check::Coordinates("TRANSLATED_RANGE");
let name = "expansions_translated";
write_stage(
out,
&Stage {
name,
doc,
program,
check,
},
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.";
let check = Check::Coordinates("UNTRANSLATED_RANGE");
let name = "expansions_untranslated";
write_stage(
out,
&Stage {
name,
doc,
program,
check,
},
points,
);
}
let decline = |range: &str| {
format!(
"`Ok(None)` means that this stage cannot decide: some point has a weight other \
than [`Weight::One`], or some coordinate has more than \
[`MAX_COORD_COMPONENTS`] components or a nonzero component of magnitude \
outside [`{range}`] or not finite.\n\n\
Requirement: every coordinate is a valid expansion of `scratch`: nonzero \
components of strictly increasing magnitude that do not overlap, as \
[`Point::exact`](implicit_predicates_core::Point::exact) writes them. This is \
not checked, and the result is unspecified otherwise.\n\n\
The arena is left as it was, on success and on error.\n\n\
# Errors\n\n\
[`ScratchExhausted`] if `scratch` runs out of room."
)
};
let doc = format!(
"The exact sign of the predicate for points given by their exact coordinates: its \
main form evaluated with expansions in `scratch`, each difference of coordinates \
computed exactly.\n\n{}",
decline("SPANS_TRANSLATED_RANGE")
);
let stage = SpanStage {
name: "expansions_translated_in",
doc: &doc,
form: SpanForm::Translated(forms.spans_translated.0),
range: "SPANS_TRANSLATED_RANGE",
};
write_span_stage(out, &stage, points);
if let Some((graph, _)) = forms.spans_untranslated {
let doc = format!(
"The exact sign of the predicate for points given by their exact coordinates: \
its form for exact evaluation, marked `#[exact]` in the definition, evaluated \
with expansions in `scratch` on the coordinates.\n\n{}",
decline("SPANS_UNTRANSLATED_RANGE")
);
let stage = SpanStage {
name: "expansions_untranslated_in",
doc: &doc,
form: SpanForm::Untranslated(graph),
range: "SPANS_UNTRANSLATED_RANGE",
};
write_span_stage(out, &stage, points);
}
}