#![allow(dead_code)]
use std::collections::BTreeMap;
use implicit_predicates_gen::{
Generated, Graph, Node, ParamType, PointNode, PointType, Polynomial, Position, canonical,
generate_points, parse_points,
};
pub const POINTS: &str = include_str!("../fixtures/points.pred");
pub const ORIENT3D: &str = include_str!("../fixtures/orient3d.pred");
pub const VARIED: &str = "
point Mix(a: F64x3, b: F64x3, s: f64, t: f64) = -a - b * s * 2 + (t + 3) * a - -b
where 0 <= s, 2 <= t + 1;
point Products(a: F64x3, s: f64, t: f64) = (s - t) * (a - a * s) + (1 - s) * (s * a);
point Twice(a: F64x3) = --a;
point Negated(a: F64x3, b: F64x3, s: f64) = -(a * s) + b - b * s;
point Shared(p: F64x3, q: F64x3, t: f64) = q + t * (p - q) + 0 * t * p;
";
pub fn generated(source: &str) -> Generated {
generate_points(source, "predicates/points.pred", "run the generator")
.unwrap()
.expect("the source declares points")
}
pub fn declarations() -> Vec<(PointType, String, String)> {
let mut out = Vec::new();
for source in [POINTS, VARIED] {
let g = generated(source);
for point in parse_points(source, "t.pred").unwrap() {
out.push((point, g.stages.clone(), g.checks.clone()));
}
}
out
}
pub fn expand(graph: &Graph) -> Polynomial {
canonical(graph, "t.pred", Position { line: 1, column: 1 }).unwrap()
}
pub fn declared(point: &PointType, axis: usize) -> Polynomial {
let mut graph = Graph {
nodes: Vec::new(),
root: 0,
};
let mut id = Vec::new();
for node in &point.graph.nodes {
let new = match *node {
PointNode::Coord { param, axis } => {
push(&mut graph, Node::Coord { point: param, axis })
}
PointNode::Scalar(param) => push(
&mut graph,
Node::Coord {
point: param,
axis: 0,
},
),
PointNode::Int(n) => push(&mut graph, Node::Int(n)),
PointNode::Neg(x) => {
let zero = push(&mut graph, Node::Int(0));
push(&mut graph, Node::Sub(zero, id[x]))
}
PointNode::Add(x, y) => push(&mut graph, Node::Add(id[x], id[y])),
PointNode::Sub(x, y) => push(&mut graph, Node::Sub(id[x], id[y])),
PointNode::Mul(x, y) => push(&mut graph, Node::Mul(id[x], id[y])),
};
id.push(new);
}
graph.root = id[point.graph.roots[axis]];
expand(&graph)
}
pub fn push(graph: &mut Graph, node: Node) -> usize {
graph.nodes.push(node);
graph.nodes.len() - 1
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Binding {
pub vectors: Vec<usize>,
pub scalars: Vec<usize>,
}
impl Binding {
pub fn declared(point: &PointType) -> Binding {
let of = |ty: ParamType| {
(point.params.iter().enumerate())
.filter(|(_, p)| p.ty == ty)
.map(|(i, _)| i)
.collect()
};
Binding {
vectors: of(ParamType::F64x3),
scalars: of(ParamType::F64),
}
}
pub fn from_call(
point: &PointType,
body: &str,
callee: &str,
before: &str,
prefix: &str,
) -> Binding {
let start = body
.find(&format!("{callee}({before}"))
.unwrap_or_else(|| panic!("no call of {callee} in {body}"));
let rest = &body[start + callee.len() + 1 + before.len()..];
let end = [");", ")\n"]
.iter()
.filter_map(|e| rest.find(e))
.min()
.expect("the end of the call");
let args = &rest[..end];
let arrays: Vec<&str> = args
.split("], ")
.map(|a| a.trim_start_matches('[').trim_end_matches(']'))
.collect();
let slots = |text: &str, ty: ParamType| -> Vec<usize> {
if text.is_empty() {
return Vec::new();
}
text.split(", ")
.map(|arg| {
let name = arg.strip_prefix(prefix).unwrap_or_else(|| panic!("{arg}"));
let i = (point.params.iter())
.position(|p| p.name == name)
.unwrap_or_else(|| panic!("{name} is not a parameter"));
assert_eq!(point.params[i].ty, ty, "{name} in a slot of another type");
i
})
.collect()
};
let binding = Binding {
vectors: slots(arrays[0], ParamType::F64x3),
scalars: arrays
.get(1)
.map_or(Vec::new(), |a| slots(a, ParamType::F64)),
};
let declared = Binding::declared(point);
assert_eq!(binding.vectors.len(), declared.vectors.len(), "{args}");
assert_eq!(binding.scalars.len(), declared.scalars.len(), "{args}");
binding
}
}
pub fn leaf(g: &mut Graph, binding: &Binding, text: &str) -> Option<usize> {
let text = text.strip_suffix(".clone()").unwrap_or(text);
let text = (text.strip_prefix("Dyadic::from_f64("))
.and_then(|t| t.strip_suffix(')'))
.unwrap_or(text);
if let Some(rest) = text.strip_prefix("c[") {
let (slot, rest) = rest.split_once("][")?;
let axis = rest.strip_suffix(']')?.parse().ok()?;
let param = binding.vectors[slot.parse::<usize>().ok()?];
return Some(push(g, Node::Coord { point: param, axis }));
}
if let Some(rest) = text.strip_prefix("s[") {
let param = binding.scalars[rest.strip_suffix(']')?.parse::<usize>().ok()?];
return Some(push(
g,
Node::Coord {
point: param,
axis: 0,
},
));
}
let value: f64 = text.parse().ok()?;
assert_eq!(value, value.trunc(), "{text}");
Some(push(g, Node::Int(value as i64)))
}
pub fn negate(g: &mut Graph, x: usize) -> usize {
let zero = push(g, Node::Int(0));
push(g, Node::Sub(zero, x))
}
pub fn point_method<'a>(code: &'a str, ty: &str, name: &str) -> &'a str {
method_in(code, &format!("impl Point for {ty} {{\n"), name)
}
pub fn method<'a>(code: &'a str, ty: &str, name: &str) -> &'a str {
method_in(code, &format!("impl {ty} {{\n"), name)
}
fn method_in<'a>(code: &'a str, block: &str, name: &str) -> &'a str {
let start = code.find(block).expect("the impl block");
let rest = &code[start..];
let start = rest.find(&format!("fn {name}(")).expect("the function");
let rest = &rest[start..];
&rest[..rest.find("\n}\n\n").expect("the end of the function")]
}
pub fn read_values(binding: &Binding, body: &str) -> (Graph, BTreeMap<usize, usize>) {
let mut g = Graph {
nodes: Vec::new(),
root: 0,
};
let mut values = BTreeMap::new();
for line in body.lines() {
let Some(rest) = line.strip_prefix("let v") else {
continue;
};
let (name, value) = rest.trim_end_matches(';').split_once(" = ").unwrap();
let id: usize = name.parse().unwrap();
let operand =
|text: &str| values[&text.strip_prefix('v').unwrap().parse::<usize>().unwrap()];
let node = match value.split(' ').collect::<Vec<_>>().as_slice() {
[x] if x.starts_with("-v") => {
let x = operand(&x[1..]);
negate(&mut g, x)
}
[x] => leaf(&mut g, binding, x).unwrap_or_else(|| panic!("{line}")),
[x, op, y] => {
let (x, y) = (operand(x), operand(y));
push(
&mut g,
match *op {
"+" => Node::Add(x, y),
"-" => Node::Sub(x, y),
"*" => Node::Mul(x, y),
_ => panic!("{line}"),
},
)
}
_ => panic!("{line}"),
};
values.insert(id, node);
}
(g, values)
}
pub fn approx_roots(body: &str) -> [usize; 3] {
let line = body
.lines()
.find(|l| l.starts_with("let approx = ["))
.unwrap();
let inner = line
.strip_prefix("let approx = [")
.unwrap()
.strip_suffix("];")
.unwrap();
let ids: Vec<usize> = inner.split(", ").map(|v| v[1..].parse().unwrap()).collect();
[ids[0], ids[1], ids[2]]
}