1mod chain;
10mod check_fns;
11mod checks;
12mod edges;
13mod exact;
14mod filter;
15mod filter_axis;
16mod generic;
17mod grid;
18mod point_checks;
19mod point_eval;
20mod point_exact;
21mod point_rules;
22mod point_types;
23mod points;
24mod program;
25mod spans;
26mod stages;
27mod statements;
28mod text;
29
30#[cfg(test)]
31mod tests;
32
33use std::fmt::Write;
34
35use implicit_predicates_core::eft::{PRODUCT_OPERAND_MAX, PRODUCT_OPERAND_MIN};
36
37use crate::bound::{FilterForm, MAX_DEGREE, filter_form};
38use crate::differences::difference_graph;
39use crate::error::Error;
40use crate::parse::{Predicate, parse};
41use crate::polynomial::check_same_polynomial;
42use crate::range::{
43 Leaves, MAX_COORD_COMPONENTS, exact_plan_differences, exact_plan_raw,
44 exact_plan_spans_translated, exact_plan_spans_untranslated,
45};
46use chain::ChainStage;
47use exact::{write_in_range, write_product};
48use filter::FilterKind;
49pub use points::generate_points;
50use program::{Program, Step};
51use spans::write_span_in_range;
52use stages::{Forms, fits_on_stack, write_constants, write_exact_stages};
53pub(crate) use text::{comment, literal, power_of_two};
54
55#[derive(Clone, Debug, PartialEq, Eq)]
57pub struct Generated {
58 pub name: String,
60 pub stages: String,
62 pub checks: String,
68}
69
70pub fn generate(source: &str, file: &str, regenerate: &str) -> Result<Vec<Generated>, Error> {
139 let notice = notice(file, regenerate);
140 parse(source, file)?
141 .iter()
142 .map(|predicate| generate_one(predicate, file, ¬ice))
143 .collect()
144}
145
146fn generate_one(predicate: &Predicate, file: &str, notice: &str) -> Result<Generated, Error> {
148 let position = predicate.position;
153 let graph = difference_graph(&predicate.main, file, position)?;
154 if let Some(exact) = &predicate.exact {
155 let degree = exact.degree_bound();
156 if degree > u64::from(MAX_DEGREE) {
157 return Err(Error::at(
158 file,
159 predicate.exact_position.unwrap_or(position),
160 format!(
161 "the exact form of `{}` has a degree of up to {degree}, above {MAX_DEGREE}, \
162 the highest degree the filter accepts",
163 predicate.name
164 ),
165 ));
166 }
167 }
168 let filter = match filter_form(&graph, file, position)? {
169 FilterForm::PerAxis(bound) => FilterKind::PerAxis(bound.constants(), bound.certain_zero()),
170 FilterForm::Global(bound) => FilterKind::Global(bound.constants()),
171 };
172 check_same_polynomial(predicate, file)?;
173 if graph.nodes.len() > edges::MAX_NODES {
174 return Err(Error::at(
175 file,
176 position,
177 format!(
178 "the main form of `{}` has {} distinct subexpressions, more than the {} \
179 for which the checks of its tests are generated: their edge cases take \
180 thousands of exact evaluations of the whole form",
181 predicate.name,
182 graph.nodes.len(),
183 edges::MAX_NODES
184 ),
185 ));
186 }
187 let (min, max) = (PRODUCT_OPERAND_MIN, PRODUCT_OPERAND_MAX);
188 let stack = |leaves| {
190 exact_plan_differences(&graph, leaves, file, position, min, max).map(|plan| {
191 fits_on_stack(plan.total).then(|| {
192 (
193 Program::differences(&graph, leaves, &plan.lengths),
194 plan.range,
195 )
196 })
197 })
198 };
199 let differences = stack(Leaves::One)?;
200 let translated = stack(Leaves::Two)?;
201 let spans =
202 exact_plan_spans_translated(&graph, MAX_COORD_COMPONENTS, file, position, min, max)?;
203 let (untranslated, spans_untranslated) = match &predicate.exact {
204 Some(exact) => {
205 let position = predicate.exact_position.unwrap_or(position);
206 let plan = exact_plan_raw(exact, file, position, min, max)?;
207 let program = |lengths: &[usize]| Program::raw(exact, lengths);
208 let stack = fits_on_stack(plan.total).then(|| (program(&plan.lengths), plan.range));
209 let spans = exact_plan_spans_untranslated(
210 exact,
211 MAX_COORD_COMPONENTS,
212 file,
213 position,
214 min,
215 max,
216 )?;
217 (stack, Some((exact, spans.range)))
218 }
219 None => (None, None),
220 };
221 let forms = Forms {
222 filter,
223 differences,
224 translated,
225 untranslated,
226 spans_translated: (&graph, spans.range),
227 spans_untranslated,
228 };
229 let points = predicate.params.len();
230 let mut out = String::new();
231 write_header(&mut out, predicate, notice, &forms);
232 write_constants(&mut out, &forms);
233 let (uses_max, explicit) = match &forms.filter {
234 FilterKind::Global(c) => (
235 filter::write_filter(&mut out, &graph, points, c.degree),
236 "0.0",
237 ),
238 FilterKind::PerAxis(c, zero) => (
239 filter_axis::write_filter(&mut out, &graph, points, c, *zero),
240 "[0.0; 3]",
241 ),
242 };
243 write_exact_stages(&mut out, points, &forms);
244 let explicit_stages = chain_stages(&forms);
245 chain::write_chain(&mut out, points, &explicit_stages, explicit);
246 let spans: &[&str] = if forms.spans_untranslated.is_some() {
247 &["expansions_translated_in", "expansions_untranslated_in"]
248 } else {
249 &["expansions_translated_in"]
250 };
251 generic::write_generic_chain(
252 &mut out,
253 &generic::GenericChain {
254 points,
255 per_axis: matches!(forms.filter, FilterKind::PerAxis(..)),
256 explicit: &explicit_stages,
257 spans,
258 },
259 );
260 if uses_max {
261 filter::write_max(&mut out);
262 }
263 write_in_range(&mut out);
264 write_span_in_range(&mut out);
265 if forms
266 .stack_stages()
267 .flat_map(|(_, program)| &program.steps)
268 .any(|step| matches!(step, Step::Product { .. }))
269 {
270 write_product(&mut out);
271 }
272 let mut checks = String::new();
273 checks::write_checks(&mut checks, predicate, &graph, &forms.filter, notice);
274 Ok(Generated {
275 name: predicate.name.clone(),
276 stages: out,
277 checks,
278 })
279}
280
281pub(crate) fn notice(file: &str, regenerate: &str) -> String {
285 let mut out = format!("// Generated by implicit-predicates-gen from {file}. Do not edit.\n");
286 for (i, line) in regenerate.lines().enumerate() {
287 let line = if i == 0 {
288 format!("To regenerate, {line}")
289 } else {
290 line.to_string()
291 };
292 writeln!(out, "// {line}").unwrap();
293 }
294 out.push('\n');
295 out
296}
297
298fn write_header(out: &mut String, predicate: &Predicate, notice: &str, forms: &Forms) {
299 out.push_str(notice);
300 comment(
301 out,
302 "//!",
303 0,
304 &format!(
305 "The stages of the predicate `{}`.\n\n\
306 Every stage takes the points {} of the definition as `p`, in that order, \
307 and returns the sign of the predicate, or `None` when it cannot decide. \
308 [`staged`] runs them in order on explicit points, and [`staged_in`] on points \
309 of any type.",
310 predicate.name,
311 list(&predicate.params)
312 ),
313 );
314 let mut names = vec![
316 "ExactCoords",
317 "ExactError",
318 "Point",
319 "Scratch",
320 "ScratchExhausted",
321 "Sign",
322 "Span",
323 "Weight",
324 ];
325 if forms.differences.is_some() {
326 names.push("eft");
327 }
328 if forms.stack_stages().next().is_some() {
329 names.push("expansion");
330 }
331 writeln!(
332 out,
333 "\nuse implicit_predicates_core::{{{}}};\n",
334 names.join(", ")
335 )
336 .unwrap();
337}
338
339fn chain_stages(forms: &Forms) -> Vec<ChainStage> {
343 let mut stages = Vec::new();
344 if forms.differences.is_some() {
345 stages.push(ChainStage::Stack("exact_differences"));
346 }
347 stages.push(match forms.translated {
348 Some(_) => ChainStage::Stack("expansions_translated"),
349 None => ChainStage::Spans("expansions_translated_in"),
350 });
351 if forms.spans_untranslated.is_some() {
352 stages.push(match forms.untranslated {
353 Some(_) => ChainStage::Stack("expansions_untranslated"),
354 None => ChainStage::Spans("expansions_untranslated_in"),
355 });
356 }
357 stages
358}
359
360pub(crate) fn list(names: &[String]) -> String {
362 let quoted: Vec<String> = names.iter().map(|n| format!("`{n}`")).collect();
363 match quoted.split_last() {
364 Some((last, rest)) if !rest.is_empty() => format!("{} and {last}", rest.join(", ")),
365 _ => quoted.concat(),
366 }
367}