1use crate::{Arity, Eval, Factory, NodeValue, TreeNode, ops::Param};
2use std::{
3 fmt::{Debug, Display},
4 hash::Hash,
5};
6
7pub enum Op<T> {
21 Fn(&'static str, Arity, fn(&[T]) -> T),
28 Var(&'static str, usize, Option<usize>),
35 Const(&'static str, T),
41 Value(&'static str, Arity, Param<T>, fn(&[T], &T) -> T),
52}
53
54impl<T> Op<T> {
55 pub fn name(&self) -> &str {
56 match self {
57 Op::Fn(name, _, _) => name,
58 Op::Var(name, _, _) => name,
59 Op::Const(name, _) => name,
60 Op::Value(name, _, _, _) => name,
61 }
62 }
63
64 pub fn arity(&self) -> Arity {
65 match self {
66 Op::Fn(_, arity, _) => *arity,
67 Op::Var(_, _, _) => Arity::Zero,
68 Op::Const(_, _) => Arity::Zero,
69 Op::Value(_, arity, _, _) => *arity,
70 }
71 }
72
73 pub fn value(&self) -> Option<&T> {
74 match self {
75 Op::Value(_, _, value, _) => Some(value.data()),
76 _ => None,
77 }
78 }
79
80 pub fn is_fn(&self) -> bool {
81 matches!(self, Op::Fn(_, _, _))
82 }
83
84 pub fn is_var(&self) -> bool {
85 matches!(self, Op::Var(_, _, _))
86 }
87
88 pub fn is_const(&self) -> bool {
89 matches!(self, Op::Const(_, _))
90 }
91
92 pub fn is_value(&self) -> bool {
93 matches!(self, Op::Value(_, _, _, _))
94 }
95}
96
97impl<T> Eval<[T], T> for Op<T>
98where
99 T: Clone,
100{
101 fn eval(&self, inputs: &[T]) -> T {
102 match self {
103 Op::Fn(_, _, op) => op(inputs),
104 Op::Var(_, index, _) => inputs[*index].clone(),
105 Op::Const(_, value) => value.clone(),
106 Op::Value(_, _, value, operation) => operation(inputs, value.data()),
107 }
108 }
109}
110
111impl<T> Factory<(), Op<T>> for Op<T>
112where
113 T: Clone,
114{
115 fn new_instance(&self, _: ()) -> Op<T> {
116 match self {
117 Op::Fn(name, arity, op) => Op::Fn(name, *arity, *op),
118 Op::Var(name, index, domain) => Op::Var(name, *index, domain.clone()),
119 Op::Const(name, value) => Op::Const(name, value.clone()),
120 Op::Value(name, arity, value, operation) => {
121 Op::Value(name, *arity, value.new_instance(()), *operation)
122 }
123 }
124 }
125}
126
127impl<T> Clone for Op<T>
128where
129 T: Clone,
130{
131 fn clone(&self) -> Self {
132 match self {
133 Op::Fn(name, arity, op) => Op::Fn(name, *arity, *op),
134 Op::Var(name, index, domain) => Op::Var(name, *index, domain.clone()),
135 Op::Const(name, value) => Op::Const(name, value.clone()),
136 Op::Value(name, arity, value, operation) => {
137 Op::Value(name, *arity, value.clone(), *operation)
138 }
139 }
140 }
141}
142
143impl<T> PartialEq for Op<T>
144where
145 T: PartialEq,
146{
147 fn eq(&self, other: &Self) -> bool {
148 self.name() == other.name()
149 && self.arity() == other.arity()
150 && match (self, other) {
151 (Op::Fn(_, _, _), Op::Fn(_, _, _)) => true,
152 (Op::Var(_, idx_a, card_a), Op::Var(_, idx_b, card_b)) => {
153 idx_a == idx_b && card_a == card_b
154 }
155 (Op::Const(_, val_a), Op::Const(_, val_b)) => val_a == val_b,
156 (Op::Value(_, _, val_a, _), Op::Value(_, _, val_b, _)) => val_a == val_b,
157 _ => false,
158 }
159 }
160}
161
162impl Hash for Op<f32> {
163 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
164 self.name().hash(state);
165 self.arity().hash(state);
166 match self {
167 Op::Fn(_, _, op) => {
168 let op_ptr = *op as usize;
169 op_ptr.hash(state);
170 }
171 Op::Var(_, index, domain) => {
172 index.hash(state);
173 domain.hash(state);
174 }
175 Op::Const(_, value) => {
176 value.to_bits().hash(state);
177 }
178 Op::Value(_, _, value, operation) => {
179 (*value).data().to_bits().hash(state);
180 let op_ptr = *operation as usize;
181 op_ptr.hash(state);
182 }
183 }
184 }
185}
186
187impl<T> Display for Op<T> {
188 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
189 write!(f, "{}", self.name())
190 }
191}
192
193impl<T> Default for Op<T>
194where
195 T: Default,
196{
197 fn default() -> Self {
198 Op::Fn("default", Arity::Zero, |_: &[T]| T::default())
199 }
200}
201
202impl<T> Debug for Op<T>
203where
204 T: Debug,
205{
206 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
207 match self {
208 Op::Fn(name, _, _) => write!(f, "Fn: {}", name),
209 Op::Var(name, index, card) => match card {
210 Some(k) => {
211 write!(f, "Var: {}({}, Cat:{})", name, index, k)
212 }
213 None => write!(f, "Var: {}({})", name, index),
214 },
215 Op::Const(name, value) => write!(f, "C: {}({:?})", name, value),
216 Op::Value(name, _, value, _) => {
217 write!(f, "Val: {}({:?})", name, value)
218 }
219 }
220 }
221}
222
223impl<T: Clone> From<Op<T>> for NodeValue<Op<T>> {
224 fn from(value: Op<T>) -> Self {
225 let arity = value.arity();
226 NodeValue::Bounded(value, arity)
227 }
228}
229
230impl<T> From<Op<T>> for TreeNode<Op<T>> {
231 fn from(value: Op<T>) -> Self {
232 let arity = value.arity();
233 TreeNode::with_arity(value, arity)
234 }
235}
236
237impl<T> From<Op<T>> for Vec<TreeNode<Op<T>>> {
238 fn from(value: Op<T>) -> Self {
239 vec![TreeNode::from(value)]
240 }
241}
242
243#[cfg(test)]
244mod test {
245 use super::*;
246
247 #[test]
248 fn test_ops() {
249 let op = Op::add();
250 assert_eq!(op.name(), "add");
251 assert_eq!(op.arity(), Arity::Exact(2));
252 assert_eq!(op.eval(&[1_f32, 2_f32]), 3_f32);
253 assert_eq!(op.new_instance(()), op);
254 }
255
256 #[test]
257 fn test_op_clone() {
258 let op = Op::add();
259 let op2 = op.clone();
260
261 let result = op.eval(&[1_f32, 2_f32]);
262 let result2 = op2.eval(&[1_f32, 2_f32]);
263
264 assert_eq!(op, op2);
265 assert_eq!(result, result2);
266 }
267}