1use std::collections::BTreeMap;
4
5use sva_formula::{
6 ClosedForm, Hash, NodeId, Var, hash_closed_form, hash_closed_form_with, hash_spectral_sum,
7 normalize_closed_form,
8};
9
10use crate::error::EngineError;
11use crate::index::Round;
12use crate::typing::{Step, SumSlot, Typing, Value, When};
13
14use super::{cyclic, nodes_in, spectral_sum_of};
15
16pub fn symbolic_hash(typing: &Typing, node: NodeId, want: Var) -> Result<Hash, EngineError> {
18 spectral_sum_of(typing, node, want).map(|n| hash_spectral_sum(&n))
19}
20
21pub fn identity(typing: &Typing, node: NodeId) -> Result<Hash, EngineError> {
24 identity_in(typing, node, &mut BTreeMap::new())
25}
26
27pub(crate) fn identity_in(
29 typing: &Typing,
30 node: NodeId,
31 named: &mut BTreeMap<NodeId, Hash>,
32) -> Result<Hash, EngineError> {
33 identity_of(typing, node, &mut Vec::new(), named)
34}
35
36fn identity_of(
37 typing: &Typing,
38 node: NodeId,
39 open: &mut Vec<NodeId>,
40 named: &mut BTreeMap<NodeId, Hash>,
41) -> Result<Hash, EngineError> {
42 if let Some(held) = named.get(&node) {
43 return Ok(*held);
44 }
45 let found = match typing.sum_slots(node) {
46 Some(slots) => {
47 let mut sink = Sink::new();
48 sink.text("terms");
49 for slot in slots {
50 sink.hash(match slot {
51 SumSlot::Node(id) if *id == node => built(typing, node, open, named)?,
52 SumSlot::Node(id) => identity_of(typing, *id, open, named)?,
53 SumSlot::Retired(_) => continue,
54 });
55 }
56 sink.finish()
57 }
58 None => built(typing, node, open, named)?,
59 };
60 named.insert(node, found);
61 Ok(found)
62}
63
64pub(crate) fn formula_identity(form: &ClosedForm) -> Hash {
67 match normalize_closed_form(form) {
68 Ok(sum) => hash_spectral_sum(&sum),
69 Err(_) => hash_closed_form(form),
70 }
71}
72
73fn built(
74 typing: &Typing,
75 node: NodeId,
76 open: &mut Vec<NodeId>,
77 named: &mut BTreeMap<NodeId, Hash>,
78) -> Result<Hash, EngineError> {
79 if open.contains(&node) {
80 return Err(cyclic(typing, node));
81 }
82 open.push(node);
83 let mut sink = Sink::new();
84 match typing.value(node) {
85 Value::ClosedForm(form) if nodes_in(&form.body).is_empty() => {
86 open.pop();
87 return Ok(formula_identity(form));
88 }
89 Value::ClosedForm(form) => {
90 let mut refused = None;
91 let mut read = |id: NodeId| match identity_of(typing, id, open, named) {
92 Ok(held) => held,
93 Err(e) => {
94 refused.get_or_insert(e);
95 Hash(0, 0)
96 }
97 };
98 sink.text("closed form");
99 sink.hash(hash_closed_form_with(form, &mut read));
100 if let Some(e) = refused {
101 return Err(e);
102 }
103 }
104 Value::Cast(cast, source) => {
105 sink.text(cast.name());
106 sink.hash(identity_of(typing, *source, open, named)?);
107 }
108 Value::Read { source, at, .. } => {
109 sink.text("read");
110 sink.hash(identity_of(typing, *source, open, named)?);
111 when(&mut sink, typing, at);
112 }
113 Value::SelfAt { at, .. } => {
114 sink.text("self");
115 when(&mut sink, typing, at);
116 }
117 Value::Noise(seed) => {
118 sink.text("noise");
119 sink.word(*seed);
120 }
121 Value::Stored(held) => {
122 sink.text("stored");
123 sink.hash(held.key);
124 }
125 Value::Solver { params, varying } => {
126 let mut held = (**params).clone();
127 for (key, _) in varying {
128 *crate::lower::field(&mut held, key).expect("a varying field") = f64::NAN;
129 }
130 sink.text(&format!("{held:?}"));
131 for (key, arg) in varying {
132 sink.text(key);
133 sink.hash(identity_of(typing, *arg, open, named)?);
134 }
135 }
136 Value::Filter {
137 shape,
138 x,
139 cutoff,
140 q,
141 gain,
142 } => {
143 sink.text(shape.name());
144 for operand in [x, cutoff, q, gain] {
145 sink.hash(identity_of(typing, *operand, open, named)?);
146 }
147 }
148 Value::Op { name, args } => {
149 sink.text(name);
150 for arg in args {
151 sink.hash(identity_of(typing, *arg, open, named)?);
152 }
153 }
154 }
155 open.pop();
156 Ok(sink.finish())
157}
158
159pub(super) fn when(sink: &mut Sink, typing: &Typing, at: &When) {
161 sink.text("at");
162 match at {
163 When::At(time) => {
164 sink.text("time");
165 for q in [time.scale, time.shift] {
166 sink.word(q.num() as u64);
167 sink.word((q.num() >> 64) as u64);
168 sink.word(q.den() as u64);
169 sink.word((q.den() >> 64) as u64);
170 }
171 }
172 When::Moving(id) => moving(sink, typing, *id),
173 When::Index(index) => exact(sink, *index),
174 When::Step(step) => {
175 sink.text("step");
176 stepped(sink, typing, step);
177 }
178 }
179}
180
181fn moving(sink: &mut Sink, typing: &Typing, id: NodeId) {
182 match identity(typing, id) {
183 Ok(held) => sink.hash(held),
184 Err(_) => sink.text(typing.name(id)),
185 }
186}
187
188fn exact(sink: &mut Sink, index: crate::index::Index) {
189 round(sink, "index", index.round);
190 match index.time {
191 Some(time) => affine(sink, time),
192 None => sink.text("count"),
193 }
194 sink.word(index.plus as u64);
195}
196
197fn stepped(sink: &mut Sink, typing: &Typing, step: &Step) {
198 let each = |sink: &mut Sink, what: &str, parts: &[Step]| {
199 sink.text(what);
200 sink.word(parts.len() as u64);
201 parts.iter().for_each(|p| stepped(sink, typing, p));
202 };
203 match step {
204 Step::Index(index) => exact(sink, *index),
205 Step::Nearest(time, how) => {
206 round(sink, "nearest", *how);
207 moving(sink, typing, *time);
208 }
209 Step::Add(parts) => each(sink, "sum", parts),
210 Step::Mul(parts) => each(sink, "product", parts),
211 Step::Neg(part) => {
212 sink.text("negated");
213 stepped(sink, typing, part);
214 }
215 }
216}
217
218fn round(sink: &mut Sink, what: &str, round: Round) {
219 let how = match round {
220 Round::Even => "",
221 Round::Floor => " floor",
222 Round::Ceil => " ceil",
223 };
224 sink.text(&format!("{what}{how}"));
225}
226
227fn affine(sink: &mut Sink, time: crate::time::Affine) {
228 for q in [time.scale, time.shift] {
229 rational(sink, q);
230 }
231}
232
233fn rational(sink: &mut Sink, q: crate::time::Q) {
234 sink.word(q.num() as u64);
235 sink.word((q.num() >> 64) as u64);
236 sink.word(q.den() as u64);
237}
238
239const IDENTITY_ROTATE: u32 = 23;
240
241pub(super) struct Sink(sva_formula::Lanes<IDENTITY_ROTATE>);
242
243impl Sink {
244 pub(super) fn new() -> Sink {
245 Sink(sva_formula::Lanes::default())
246 }
247
248 pub(super) fn word(&mut self, part: u64) {
249 self.0.word(part);
250 }
251
252 pub(super) fn text(&mut self, what: &str) {
253 self.word(what.len() as u64);
254 for byte in what.as_bytes() {
255 self.word(u64::from(*byte));
256 }
257 }
258
259 pub(super) fn hash(&mut self, held: Hash) {
260 self.word(held.0);
261 self.word(held.1);
262 }
263
264 pub(super) fn finish(&self) -> Hash {
265 self.0.finish()
266 }
267}