1use crate::Error;
9use crate::context::{Context, IntoNode, Node};
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12
13#[derive(
22 Copy,
23 Clone,
24 Debug,
25 Hash,
26 Eq,
27 PartialEq,
28 Ord,
29 PartialOrd,
30 Serialize,
31 Deserialize,
32)]
33pub enum Var {
34 X,
36 Y,
38 Z,
40 V(VarIndex),
42}
43
44#[derive(
46 Copy,
47 Clone,
48 Debug,
49 Hash,
50 Eq,
51 PartialEq,
52 Ord,
53 PartialOrd,
54 Serialize,
55 Deserialize,
56)]
57#[serde(transparent)]
58pub struct VarIndex(u64);
59
60impl Var {
61 #[allow(clippy::new_without_default)]
67 pub fn new() -> Self {
68 let v: u64 = rand::random();
69 Var::V(VarIndex(v))
70 }
71
72 pub fn index(&self) -> Option<VarIndex> {
74 if let Var::V(i) = *self { Some(i) } else { None }
75 }
76}
77
78impl std::fmt::Display for Var {
79 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80 match self {
81 Var::X => write!(f, "X"),
82 Var::Y => write!(f, "Y"),
83 Var::Z => write!(f, "Z"),
84 Var::V(VarIndex(v)) if *v < 256 => write!(f, "v_{v}"),
85 Var::V(VarIndex(v)) => write!(f, "V({v:x})"),
86 }
87 }
88}
89
90impl IntoNode for Var {
91 fn into_node(self, ctx: &mut Context) -> Result<Node, Error> {
92 Ok(ctx.var(self))
93 }
94}
95
96#[derive(Default, Serialize, Deserialize)]
106pub struct VarMap {
107 x: Option<usize>,
108 y: Option<usize>,
109 z: Option<usize>,
110 v: HashMap<VarIndex, usize>,
111}
112
113#[allow(missing_docs)]
114impl VarMap {
115 pub fn new() -> Self {
116 Self::default()
117 }
118 pub fn len(&self) -> usize {
119 self.x.is_some() as usize
120 + self.y.is_some() as usize
121 + self.z.is_some() as usize
122 + self.v.len()
123 }
124 pub fn is_empty(&self) -> bool {
125 self.x.is_none()
126 && self.y.is_none()
127 && self.z.is_none()
128 && self.v.is_empty()
129 }
130 pub fn get(&self, v: &Var) -> Option<usize> {
131 match v {
132 Var::X => self.x,
133 Var::Y => self.y,
134 Var::Z => self.z,
135 Var::V(v) => self.v.get(v).cloned(),
136 }
137 }
138 pub fn insert(&mut self, v: Var) {
142 let next = self.len();
143 match v {
144 Var::X => self.x.get_or_insert(next),
145 Var::Y => self.y.get_or_insert(next),
146 Var::Z => self.z.get_or_insert(next),
147 Var::V(v) => self.v.entry(v).or_insert(next),
148 };
149 }
150
151 pub fn check_tracing_arguments<T>(&self, vars: &[T]) -> Result<(), Error> {
153 if vars.len() < self.len() {
154 Err(Error::BadVarSlice(vars.len(), self.len()))
157 } else {
158 Ok(())
159 }
160 }
161
162 pub fn check_bulk_arguments<T, V: std::ops::Deref<Target = [T]>>(
164 &self,
165 vars: &[V],
166 ) -> Result<(), Error> {
167 if vars.len() < self.len() {
170 Err(Error::BadVarSlice(vars.len(), self.len()))
171 } else {
172 let Some(n) = vars.first().map(|v| v.len()) else {
173 return Ok(());
174 };
175 if vars.iter().any(|v| v.len() == n) {
176 Ok(())
177 } else {
178 Err(Error::MismatchedSlices)
179 }
180 }
181 }
182}
183
184impl std::ops::Index<&Var> for VarMap {
185 type Output = usize;
186 fn index(&self, v: &Var) -> &Self::Output {
187 match v {
188 Var::X => self.x.as_ref().unwrap(),
189 Var::Y => self.y.as_ref().unwrap(),
190 Var::Z => self.z.as_ref().unwrap(),
191 Var::V(v) => &self.v[v],
192 }
193 }
194}
195
196#[cfg(test)]
197mod test {
198 use super::*;
199
200 #[test]
201 fn var_identity() {
202 let v1 = Var::new();
203 let v2 = Var::new();
204 assert_ne!(v1, v2);
205 }
206
207 #[test]
208 fn var_map() {
209 let v = Var::new();
210 let mut m = VarMap::new();
211 assert!(m.get(&v).is_none());
212 m.insert(v);
213 assert_eq!(m.get(&v), Some(0));
214 m.insert(v);
215 assert_eq!(m.get(&v), Some(0));
216
217 let u = Var::new();
218 assert!(m.get(&u).is_none());
219 m.insert(u);
220 assert_eq!(m.get(&u), Some(1));
221 }
222}