1use std::collections::HashMap;
4
5use crate::error::EvalError;
6use crate::graph::Graph;
7use crate::token::Source;
8
9pub fn evaluate(
27 graph: &Graph,
28 inputs: &HashMap<u16, bool>,
29) -> Result<HashMap<u16, bool>, EvalError> {
30 for &id in &graph.inputs {
32 if !inputs.contains_key(&id) {
33 return Err(EvalError::MissingInput(id));
34 }
35 }
36
37 let mut values: HashMap<u16, bool> = HashMap::new();
39
40 for &id in &graph.inputs {
42 if let Some(&val) = inputs.get(&id) {
43 values.insert(id, val);
44 }
45 }
46
47 for node in &graph.nodes {
49 let left = resolve_source(&node.left, &values)?;
50 let right = resolve_source(&node.right, &values)?;
51 let result = node.op.eval(left, right);
52 values.insert(node.id, result);
53 }
54
55 let mut outputs = HashMap::new();
57 for &id in &graph.outputs {
58 let val = values.get(&id).ok_or(EvalError::MissingOutput(id))?;
59 outputs.insert(id, *val);
60 }
61
62 Ok(outputs)
63}
64
65fn resolve_source(source: &Source, values: &HashMap<u16, bool>) -> Result<bool, EvalError> {
67 match source {
68 Source::True => Ok(true),
69 Source::False => Ok(false),
70 Source::Id(id) => values
71 .get(id)
72 .copied()
73 .ok_or(EvalError::UndefinedValue(*id)),
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80 use crate::graph::Node;
81 use crate::token::BoolOp;
82
83 #[test]
84 fn test_simple_or() {
85 let graph = Graph {
86 inputs: vec![0, 1],
87 nodes: vec![Node::new(2, BoolOp::Or, Source::Id(0), Source::Id(1))],
88 outputs: vec![2],
89 };
90
91 let result = evaluate(&graph, &[(0, false), (1, false)].into()).unwrap();
93 assert!(!result[&2]);
94
95 let result = evaluate(&graph, &[(0, false), (1, true)].into()).unwrap();
97 assert!(result[&2]);
98
99 let result = evaluate(&graph, &[(0, true), (1, false)].into()).unwrap();
101 assert!(result[&2]);
102
103 let result = evaluate(&graph, &[(0, true), (1, true)].into()).unwrap();
105 assert!(result[&2]);
106 }
107
108 #[test]
109 fn test_constants() {
110 let graph = Graph {
111 inputs: vec![],
112 nodes: vec![Node::new(0, BoolOp::Or, Source::True, Source::False)],
113 outputs: vec![0],
114 };
115
116 let result = evaluate(&graph, &HashMap::new()).unwrap();
117 assert!(result[&0]); }
119
120 #[test]
121 fn test_chain_evaluation() {
122 let graph = Graph {
125 inputs: vec![0, 1],
126 nodes: vec![
127 Node::new(2, BoolOp::Xor, Source::Id(0), Source::Id(1)),
128 Node::new(3, BoolOp::Or, Source::Id(2), Source::True),
129 ],
130 outputs: vec![3],
131 };
132
133 let result = evaluate(&graph, &[(0, false), (1, false)].into()).unwrap();
135 assert!(result[&3]);
136 }
137
138 #[test]
139 fn test_missing_input() {
140 let graph = Graph {
141 inputs: vec![0, 1],
142 nodes: vec![Node::new(2, BoolOp::Or, Source::Id(0), Source::Id(1))],
143 outputs: vec![2],
144 };
145
146 let result = evaluate(&graph, &[(0, false)].into());
148 assert_eq!(result, Err(EvalError::MissingInput(1)));
149 }
150
151 #[test]
152 fn test_nor_operation() {
153 let graph = Graph {
154 inputs: vec![0, 1],
155 nodes: vec![Node::new(2, BoolOp::Nor, Source::Id(0), Source::Id(1))],
156 outputs: vec![2],
157 };
158
159 let result = evaluate(&graph, &[(0, false), (1, false)].into()).unwrap();
161 assert!(result[&2]);
162
163 let result = evaluate(&graph, &[(0, true), (1, false)].into()).unwrap();
165 assert!(!result[&2]);
166 }
167}