1use crate::cell::{Cell, TernaryValue};
2use crate::grid::Grid;
3
4pub type FormulaResult = Result<f64, FormulaError>;
6
7#[derive(Debug, Clone, PartialEq)]
9pub enum FormulaError {
10 UnknownFunction(String),
11 InvalidArguments(String),
12 InvalidRange(String),
13 DivisionByZero,
14 NoData,
15}
16
17impl std::fmt::Display for FormulaError {
18 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19 match self {
20 FormulaError::UnknownFunction(name) => write!(f, "unknown function: {}", name),
21 FormulaError::InvalidArguments(msg) => write!(f, "invalid arguments: {}", msg),
22 FormulaError::InvalidRange(msg) => write!(f, "invalid range: {}", msg),
23 FormulaError::DivisionByZero => write!(f, "division by zero"),
24 FormulaError::NoData => write!(f, "no data"),
25 }
26 }
27}
28
29pub struct FormulaEngine {
31 grid: Grid,
32}
33
34impl FormulaEngine {
35 pub fn new(grid: Grid) -> Self {
37 FormulaEngine { grid }
38 }
39
40 pub fn grid(&self) -> &Grid {
42 &self.grid
43 }
44
45 pub fn grid_mut(&mut self) -> &mut Grid {
47 &mut self.grid
48 }
49
50 pub fn evaluate(&mut self, formula: &str) -> FormulaResult {
52 let formula = formula.trim();
53 let formula = formula.strip_prefix('=').unwrap_or(formula);
54
55 let paren_pos = formula.find('(').ok_or_else(|| {
57 FormulaError::InvalidArguments("expected function(args)".into())
58 })?;
59 let name = formula[..paren_pos].trim().to_uppercase();
60 let inner = formula[paren_pos + 1..].trim();
61 let inner = inner.strip_suffix(')').ok_or_else(|| {
62 FormulaError::InvalidArguments("missing closing parenthesis".into())
63 })?.trim();
64
65 match name.as_str() {
66 "EVOLVE" => self.evolve(inner),
67 "BEST" => self.best(inner),
68 "SPECIES" => self.species(inner),
69 "EXHAUSTIVE" => self.exhaustive(inner),
70 "ENTROPY" => self.entropy(inner),
71 "SUM" => self.sum(inner),
72 "AVG" => self.avg(inner),
73 "COUNT" => self.count(inner),
74 _ => Err(FormulaError::UnknownFunction(name)),
75 }
76 }
77
78 fn parse_range_cells(&self, range_str: &str) -> Result<Vec<Cell>, FormulaError> {
80 let range_str = range_str.trim();
81
82 if range_str.contains(':') {
83 let parts: Vec<&str> = range_str.splitn(2, ':').collect();
84 let start = parts[0].trim();
85 let end = parts[1].trim();
86
87 if start.chars().all(|c| c.is_ascii_alphabetic()) &&
89 end.chars().all(|c| c.is_ascii_alphabetic()) {
90 let col_start = Grid::parse_cell_ref(&format!("{}1", start));
91 let col_end = Grid::parse_cell_ref(&format!("{}{}", end, self.grid.rows()));
92 match (col_start, col_end) {
93 (Some((_, cs)), Some((_, ce))) if cs == ce => {
94 Ok(self.grid.col(cs).unwrap_or_default())
95 }
96 _ => Err(FormulaError::InvalidRange(range_str.into())),
97 }
98 } else {
99 let start_pos = Grid::parse_cell_ref(start)
100 .ok_or_else(|| FormulaError::InvalidRange(start.into()))?;
101 let end_pos = Grid::parse_cell_ref(end)
102 .ok_or_else(|| FormulaError::InvalidRange(end.into()))?;
103 Ok(self.grid.range(start_pos.0, start_pos.1, end_pos.0, end_pos.1)
104 .into_iter().cloned().collect())
105 }
106 } else {
107 let pos = Grid::parse_cell_ref(range_str)
109 .ok_or_else(|| FormulaError::InvalidRange(range_str.into()))?;
110 self.grid.get(pos.0, pos.1)
111 .cloned()
112 .map(|c| vec![c])
113 .ok_or_else(|| FormulaError::InvalidRange(range_str.into()))
114 }
115 }
116
117 fn evolve(&mut self, args: &str) -> FormulaResult {
121 let parts: Vec<&str> = args.splitn(2, ',').collect();
122 if parts.len() != 2 {
123 return Err(FormulaError::InvalidArguments("EVOLVE(range, generations)".into()));
124 }
125 let range_str = parts[0].trim();
126 let generations: u32 = parts[1].trim().parse()
127 .map_err(|_| FormulaError::InvalidArguments("generations must be a number".into()))?;
128
129 let positions = self.get_range_positions(range_str)?;
131 if positions.is_empty() {
132 return Err(FormulaError::NoData);
133 }
134
135 let mut best_fitness = f64::NEG_INFINITY;
136
137 for gen in 0..generations {
138 for &(r, c) in &positions {
140 if let Some(cell) = self.grid.get_mut(r, c) {
141 cell.set_fitness(cell.compute_default_fitness());
142 }
143 }
144
145 let mut with_fitness: Vec<(f64, usize, usize)> = positions.iter()
147 .filter_map(|&(r, c)| {
148 self.grid.get(r, c).map(|cell| (cell.fitness, r, c))
149 })
150 .collect();
151 with_fitness.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(std::cmp::Ordering::Equal));
152
153 let cutoff = (with_fitness.len() + 1) / 2;
154 for (_, r, c) in with_fitness.into_iter().skip(cutoff) {
155 if let Some(cell) = self.grid.get_mut(r, c) {
156 let new_val = TernaryValue::from_seed(gen * 17 + (r as u32) * 31 + (c as u32) * 7 + 13);
157 cell.set_value(new_val);
158 }
159 }
160
161 if let Some(cell) = self.grid.get(positions[0].0, positions[0].1) {
163 best_fitness = best_fitness.max(cell.fitness);
164 }
165 }
166
167 for &(r, c) in &positions {
169 if let Some(cell) = self.grid.get(r, c) {
170 best_fitness = best_fitness.max(cell.fitness);
171 }
172 }
173
174 Ok(best_fitness)
175 }
176
177 fn best(&self, args: &str) -> FormulaResult {
179 let cells = self.parse_range_cells(args.trim())?;
180 if cells.is_empty() {
181 return Err(FormulaError::NoData);
182 }
183 cells.iter()
184 .map(|c| c.fitness)
185 .fold(None, |acc: Option<f64>, f| {
186 Some(acc.map_or(f, |best| best.max(f)))
187 })
188 .ok_or(FormulaError::NoData)
189 }
190
191 fn species(&self, args: &str) -> FormulaResult {
193 let cells = self.parse_range_cells(args.trim())?;
194 if cells.is_empty() {
195 return Err(FormulaError::NoData);
196 }
197
198 let mut species_count = 0usize;
199 let mut prev_sign: Option<i8> = None;
200
201 for cell in &cells {
202 let sign = cell.value.as_i8().signum();
203 match prev_sign {
204 Some(p) if p == sign => {} _ => {
206 if sign != 0 {
207 species_count += 1;
208 }
209 }
210 }
211 if sign != 0 {
212 prev_sign = Some(sign);
213 }
214 }
215
216 Ok(species_count as f64)
217 }
218
219 fn exhaustive(&mut self, args: &str) -> FormulaResult {
222 let positions = self.get_range_positions(args.trim())?;
223 if positions.is_empty() {
224 return Err(FormulaError::NoData);
225 }
226 if positions.len() > 10 {
227 return Err(FormulaError::InvalidArguments(
228 "EXHAUSTIVE limited to 10 cells (3^10 = 59049 combinations)".into()
229 ));
230 }
231
232 let n = positions.len();
233 let combos = 3usize.pow(n as u32);
234 let mut best_total = f64::NEG_INFINITY;
235
236 let original: Vec<TernaryValue> = positions.iter()
238 .map(|&(r, c)| self.grid.get(r, c).map(|c| c.value).unwrap_or(TernaryValue::Neutral))
239 .collect();
240
241 for combo in 0..combos {
242 let mut val = combo;
243 for &(r, c) in &positions {
244 let tv = TernaryValue::from_seed(val as u32);
245 if let Some(cell) = self.grid.get_mut(r, c) {
246 cell.set_value(tv);
247 }
248 val /= 3;
249 }
250
251 let total: f64 = positions.iter()
253 .filter_map(|&(r, c)| {
254 let cell = self.grid.get_mut(r, c)?;
255 cell.set_fitness(cell.compute_default_fitness());
256 Some(cell.fitness)
257 })
258 .sum();
259
260 best_total = best_total.max(total);
261 }
262
263 for (i, &(r, c)) in positions.iter().enumerate() {
265 if let Some(cell) = self.grid.get_mut(r, c) {
266 cell.set_value(original[i]);
267 }
268 }
269
270 Ok(best_total)
271 }
272
273 fn entropy(&self, args: &str) -> FormulaResult {
275 let cells = self.parse_range_cells(args.trim())?;
276 if cells.is_empty() {
277 return Err(FormulaError::NoData);
278 }
279
280 let n = cells.len() as f64;
281 let mut counts = [0usize; 3]; for cell in &cells {
284 match cell.value {
285 TernaryValue::Negative => counts[0] += 1,
286 TernaryValue::Neutral => counts[1] += 1,
287 TernaryValue::Positive => counts[2] += 1,
288 }
289 }
290
291 let mut entropy = 0.0;
292 for &count in &counts {
293 if count > 0 {
294 let p = count as f64 / n;
295 entropy -= p * p.log2();
296 }
297 }
298
299 Ok(entropy)
300 }
301
302 fn sum(&self, args: &str) -> FormulaResult {
304 let cells = self.parse_range_cells(args.trim())?;
305 if cells.is_empty() {
306 return Err(FormulaError::NoData);
307 }
308 Ok(cells.iter().map(|c| c.value.as_i8() as f64).sum())
309 }
310
311 fn avg(&self, args: &str) -> FormulaResult {
313 let cells = self.parse_range_cells(args.trim())?;
314 if cells.is_empty() {
315 return Err(FormulaError::NoData);
316 }
317 let sum: f64 = cells.iter().map(|c| c.value.as_i8() as f64).sum();
318 Ok(sum / cells.len() as f64)
319 }
320
321 fn count(&self, args: &str) -> FormulaResult {
323 let cells = self.parse_range_cells(args.trim())?;
324 Ok(cells.len() as f64)
325 }
326
327 fn get_range_positions(&self, range_str: &str) -> Result<Vec<(usize, usize)>, FormulaError> {
329 let range_str = range_str.trim();
330
331 if range_str.contains(':') {
332 let parts: Vec<&str> = range_str.splitn(2, ':').collect();
333 let start = parts[0].trim();
334 let end = parts[1].trim();
335
336 if start.chars().all(|c| c.is_ascii_alphabetic()) &&
337 end.chars().all(|c| c.is_ascii_alphabetic()) {
338 let col_s = Grid::parse_cell_ref(&format!("{}1", start));
339 let col_e = Grid::parse_cell_ref(&format!("{}{}", end, self.grid.rows()));
340 match (col_s, col_e) {
341 (Some((_, cs)), Some((_, ce))) if cs == ce => {
342 Ok((0..self.grid.rows()).map(|r| (r, cs)).collect())
343 }
344 _ => Err(FormulaError::InvalidRange(range_str.into())),
345 }
346 } else {
347 let start_pos = Grid::parse_cell_ref(start)
348 .ok_or_else(|| FormulaError::InvalidRange(start.into()))?;
349 let end_pos = Grid::parse_cell_ref(end)
350 .ok_or_else(|| FormulaError::InvalidRange(end.into()))?;
351 let (r1, r2) = (start_pos.0.min(end_pos.0), start_pos.0.max(end_pos.0));
352 let (c1, c2) = (start_pos.1.min(end_pos.1), start_pos.1.max(end_pos.1));
353 let mut positions = Vec::new();
354 for r in r1..=r2 {
355 for c in c1..=c2 {
356 if r < self.grid.rows() && c < self.grid.cols() {
357 positions.push((r, c));
358 }
359 }
360 }
361 Ok(positions)
362 }
363 } else {
364 let pos = Grid::parse_cell_ref(range_str)
365 .ok_or_else(|| FormulaError::InvalidRange(range_str.into()))?;
366 if pos.0 < self.grid.rows() && pos.1 < self.grid.cols() {
367 Ok(vec![pos])
368 } else {
369 Err(FormulaError::InvalidRange(range_str.into()))
370 }
371 }
372 }
373}