1use std::sync::Arc;
4
5#[derive(Clone, Copy, Debug, PartialEq)]
7pub struct RangeValue {
8 pub start: f64,
9 pub step: f64,
10 pub end: f64,
11}
12
13#[derive(Clone, Debug, PartialEq)]
18pub enum Value {
19 Undef,
20 Bool(bool),
21 Number(f64),
22 String(String),
23 Vector(Arc<Vec<Value>>),
24 Range(RangeValue),
25}
26
27impl Value {
28 pub fn vector(items: Vec<Value>) -> Self {
29 Value::Vector(Arc::new(items))
30 }
31
32 pub fn truthy(&self) -> bool {
34 match self {
35 Value::Undef => false,
36 Value::Bool(b) => *b,
37 Value::Number(n) => *n != 0.0,
38 Value::String(s) => !s.is_empty(),
39 Value::Vector(v) => !v.is_empty(),
40 Value::Range(_) => true,
41 }
42 }
43
44 pub fn as_number(&self) -> Option<f64> {
45 match self {
46 Value::Number(n) => Some(*n),
47 _ => None,
48 }
49 }
50
51 pub fn as_vector(&self) -> Option<&[Value]> {
52 match self {
53 Value::Vector(v) => Some(v),
54 _ => None,
55 }
56 }
57
58 pub fn as_numbers(&self) -> Option<Vec<f64>> {
60 self.as_vector()?
61 .iter()
62 .map(Value::as_number)
63 .collect::<Option<Vec<_>>>()
64 }
65
66 pub fn as_vec3_lenient(&self) -> Option<[f64; 3]> {
68 let nums = self.as_numbers()?;
69 match nums.len() {
70 2 => Some([nums[0], nums[1], 0.0]),
71 3 => Some([nums[0], nums[1], nums[2]]),
72 _ => None,
73 }
74 }
75
76 pub fn type_name(&self) -> &'static str {
77 match self {
78 Value::Undef => "undef",
79 Value::Bool(_) => "bool",
80 Value::Number(_) => "number",
81 Value::String(_) => "string",
82 Value::Vector(_) => "vector",
83 Value::Range(_) => "range",
84 }
85 }
86
87 pub fn echo_format(&self) -> String {
89 self.format(true)
90 }
91
92 pub fn str_format(&self) -> String {
94 self.format(false)
95 }
96
97 fn format(&self, quote_strings: bool) -> String {
98 match self {
99 Value::Undef => "undef".to_string(),
100 Value::Bool(b) => b.to_string(),
101 Value::Number(n) => format_number(*n),
102 Value::String(s) if quote_strings => format!("\"{s}\""),
103 Value::String(s) => s.clone(),
104 Value::Vector(v) => {
105 let items: Vec<String> = v.iter().map(|x| x.format(true)).collect();
106 format!("[{}]", items.join(", "))
107 }
108 Value::Range(r) => format!(
109 "[{} : {} : {}]",
110 format_number(r.start),
111 format_number(r.step),
112 format_number(r.end)
113 ),
114 }
115 }
116}
117
118pub fn format_number(n: f64) -> String {
119 if n == f64::INFINITY {
120 "inf".to_string()
121 } else if n == f64::NEG_INFINITY {
122 "-inf".to_string()
123 } else if n.is_nan() {
124 "nan".to_string()
125 } else {
126 format!("{n}")
127 }
128}
129
130pub type OpResult = core::result::Result<Value, String>;
133
134fn both_numbers(a: &Value, b: &Value) -> Option<(f64, f64)> {
135 Some((a.as_number()?, b.as_number()?))
136}
137
138fn elementwise(a: &[Value], b: &[Value], op: fn(&Value, &Value) -> OpResult) -> OpResult {
139 if a.len() != b.len() {
140 return Err(format!(
141 "vector length mismatch: {} vs {}",
142 a.len(),
143 b.len()
144 ));
145 }
146 a.iter()
147 .zip(b)
148 .map(|(x, y)| op(x, y))
149 .collect::<Result<Vec<_>, _>>()
150 .map(Value::vector)
151}
152
153pub fn add(a: &Value, b: &Value) -> OpResult {
154 match (a, b) {
155 (Value::Number(x), Value::Number(y)) => Ok(Value::Number(x + y)),
156 (Value::Vector(x), Value::Vector(y)) => elementwise(x, y, add),
157 _ => Err(format!(
158 "cannot add {} and {}",
159 a.type_name(),
160 b.type_name()
161 )),
162 }
163}
164
165pub fn sub(a: &Value, b: &Value) -> OpResult {
166 match (a, b) {
167 (Value::Number(x), Value::Number(y)) => Ok(Value::Number(x - y)),
168 (Value::Vector(x), Value::Vector(y)) => elementwise(x, y, sub),
169 _ => Err(format!(
170 "cannot subtract {} from {}",
171 b.type_name(),
172 a.type_name()
173 )),
174 }
175}
176
177fn is_flat_numeric(v: &[Value]) -> bool {
179 v.iter().all(|x| matches!(x, Value::Number(_)))
180}
181
182fn matrix_dims(v: &[Value]) -> Option<(usize, usize)> {
184 let rows = v.len();
185 let first = v.first()?.as_vector()?;
186 let cols = first.len();
187 for row in v {
188 let row = row.as_vector()?;
189 if row.len() != cols || !is_flat_numeric(row) {
190 return None;
191 }
192 }
193 Some((rows, cols))
194}
195
196fn dot(a: &[Value], b: &[Value]) -> OpResult {
197 if a.len() != b.len() {
198 return Err(format!(
199 "vector length mismatch in dot product: {} vs {}",
200 a.len(),
201 b.len()
202 ));
203 }
204 let mut acc = 0.0;
205 for (x, y) in a.iter().zip(b) {
206 match both_numbers(x, y) {
207 Some((x, y)) => acc += x * y,
208 None => return Err("dot product requires numeric vectors".into()),
209 }
210 }
211 Ok(Value::Number(acc))
212}
213
214fn scale(v: &[Value], s: f64) -> OpResult {
215 v.iter()
216 .map(|x| mul(x, &Value::Number(s)))
217 .collect::<Result<Vec<_>, _>>()
218 .map(Value::vector)
219}
220
221pub fn mul(a: &Value, b: &Value) -> OpResult {
222 match (a, b) {
223 (Value::Number(x), Value::Number(y)) => Ok(Value::Number(x * y)),
224 (Value::Number(s), Value::Vector(v)) => scale(v, *s),
225 (Value::Vector(v), Value::Number(s)) => scale(v, *s),
226 (Value::Vector(x), Value::Vector(y)) => {
227 let (fx, fy) = (is_flat_numeric(x), is_flat_numeric(y));
228 match (fx, fy) {
229 (true, true) => dot(x, y),
230 (false, true) => {
232 matrix_dims(x).ok_or("left operand is not a matrix")?;
233 x.iter()
234 .map(|row| dot(row.as_vector().expect("BUG: matrix_dims checked"), y))
235 .collect::<Result<Vec<_>, _>>()
236 .map(Value::vector)
237 }
238 (true, false) => {
240 let (rows, cols) = matrix_dims(y).ok_or("right operand is not a matrix")?;
241 if x.len() != rows {
242 return Err(format!(
243 "vector*matrix dimension mismatch: {} vs {rows}",
244 x.len()
245 ));
246 }
247 let mut out = Vec::with_capacity(cols);
248 for c in 0..cols {
249 let mut acc = 0.0;
250 for (r, xv) in x.iter().enumerate() {
251 let cell = y[r].as_vector().expect("BUG: matrix_dims checked")[c]
252 .as_number()
253 .expect("BUG: matrix_dims checked");
254 acc += xv.as_number().expect("BUG: is_flat_numeric checked") * cell;
255 }
256 out.push(Value::Number(acc));
257 }
258 Ok(Value::vector(out))
259 }
260 (false, false) => {
262 let (_ar, ac) = matrix_dims(x).ok_or("left operand is not a matrix")?;
263 let (br, bc) = matrix_dims(y).ok_or("right operand is not a matrix")?;
264 if ac != br {
265 return Err(format!("matrix dimension mismatch: {ac} vs {br}"));
266 }
267 let mut out = Vec::with_capacity(x.len());
268 for row in x.iter() {
269 let row = row.as_vector().expect("BUG: matrix_dims checked");
270 let mut out_row = Vec::with_capacity(bc);
271 for c in 0..bc {
272 let mut acc = 0.0;
273 for (k, rv) in row.iter().enumerate() {
274 let cell = y[k].as_vector().expect("BUG: matrix_dims checked")[c]
275 .as_number()
276 .expect("BUG: matrix_dims checked");
277 acc += rv.as_number().expect("BUG: matrix_dims checked") * cell;
278 }
279 out_row.push(Value::Number(acc));
280 }
281 out.push(Value::vector(out_row));
282 }
283 Ok(Value::vector(out))
284 }
285 }
286 }
287 _ => Err(format!(
288 "cannot multiply {} and {}",
289 a.type_name(),
290 b.type_name()
291 )),
292 }
293}
294
295pub fn div(a: &Value, b: &Value) -> OpResult {
296 match (a, b) {
297 (Value::Number(x), Value::Number(y)) => Ok(Value::Number(x / y)),
298 (Value::Vector(v), Value::Number(s)) => v
299 .iter()
300 .map(|x| div(x, &Value::Number(*s)))
301 .collect::<Result<Vec<_>, _>>()
302 .map(Value::vector),
303 _ => Err(format!(
304 "cannot divide {} by {}",
305 a.type_name(),
306 b.type_name()
307 )),
308 }
309}
310
311pub fn rem(a: &Value, b: &Value) -> OpResult {
312 match both_numbers(a, b) {
313 Some((x, y)) => Ok(Value::Number(x % y)),
314 None => Err(format!(
315 "cannot take {} modulo {}",
316 a.type_name(),
317 b.type_name()
318 )),
319 }
320}
321
322pub fn neg(a: &Value) -> OpResult {
323 match a {
324 Value::Number(n) => Ok(Value::Number(-n)),
325 Value::Vector(v) => v
326 .iter()
327 .map(neg)
328 .collect::<Result<Vec<_>, _>>()
329 .map(Value::vector),
330 _ => Err(format!("cannot negate {}", a.type_name())),
331 }
332}
333
334pub fn compare(a: &Value, b: &Value) -> core::result::Result<core::cmp::Ordering, String> {
336 match (a, b) {
337 (Value::Number(x), Value::Number(y)) => x
338 .partial_cmp(y)
339 .ok_or_else(|| "cannot compare NaN".to_string()),
340 (Value::String(x), Value::String(y)) => Ok(x.cmp(y)),
341 _ => Err(format!(
342 "cannot compare {} with {}",
343 a.type_name(),
344 b.type_name()
345 )),
346 }
347}
348
349pub fn index(base: &Value, idx: &Value) -> OpResult {
351 let i = idx
352 .as_number()
353 .ok_or_else(|| format!("index must be a number, got {}", idx.type_name()))?;
354 if i.fract() != 0.0 || i < 0.0 {
355 return Err(format!("invalid index {i}"));
356 }
357 let i = i as usize;
358 match base {
359 Value::Vector(v) => v
360 .get(i)
361 .cloned()
362 .ok_or_else(|| format!("index {i} out of bounds (len {})", v.len())),
363 Value::String(s) => s
364 .chars()
365 .nth(i)
366 .map(|c| Value::String(c.to_string()))
367 .ok_or_else(|| format!("index {i} out of bounds (len {})", s.chars().count())),
368 _ => Err(format!("cannot index {}", base.type_name())),
369 }
370}
371
372pub fn iterate(v: &Value) -> Vec<Value> {
378 match v {
379 Value::Range(r) => {
380 let mut out = Vec::new();
381 if r.step == 0.0 || r.step.is_nan() || r.start.is_nan() || r.end.is_nan() {
382 return out;
383 }
384 let count = ((r.end - r.start) / r.step).floor();
385 if count < 0.0 || !count.is_finite() {
386 return out;
387 }
388 let count = count as u64;
389 for i in 0..=count {
390 out.push(Value::Number(r.start + r.step * i as f64));
391 }
392 out
393 }
394 Value::Vector(v) => v.iter().cloned().collect(),
395 Value::String(s) => s.chars().map(|c| Value::String(c.to_string())).collect(),
396 Value::Undef => Vec::new(),
397 other => vec![other.clone()],
398 }
399}
400
401pub fn iteration_count(v: &Value) -> u64 {
403 match v {
404 Value::Range(r) => {
405 if r.step == 0.0 || r.step.is_nan() || r.start.is_nan() || r.end.is_nan() {
406 return 0;
407 }
408 let count = ((r.end - r.start) / r.step).floor();
409 if count < 0.0 || !count.is_finite() {
410 0
411 } else {
412 count as u64 + 1
413 }
414 }
415 Value::Vector(v) => v.len() as u64,
416 Value::String(s) => s.chars().count() as u64,
417 Value::Undef => 0,
418 _ => 1,
419 }
420}
421
422#[cfg(test)]
423mod tests {
424 use super::*;
425
426 fn num(n: f64) -> Value {
427 Value::Number(n)
428 }
429 fn vec3(a: f64, b: f64, c: f64) -> Value {
430 Value::vector(vec![num(a), num(b), num(c)])
431 }
432
433 #[test]
434 fn truthiness() {
435 assert!(!Value::Undef.truthy());
436 assert!(!num(0.0).truthy());
437 assert!(!Value::String(String::new()).truthy());
438 assert!(!Value::vector(vec![]).truthy());
439 assert!(num(0.5).truthy());
440 assert!(Value::Bool(true).truthy());
441 }
442
443 #[test]
444 fn vector_arithmetic() {
445 assert_eq!(
446 add(&vec3(1.0, 2.0, 3.0), &vec3(4.0, 5.0, 6.0)),
447 Ok(vec3(5.0, 7.0, 9.0))
448 );
449 assert_eq!(
450 sub(&vec3(4.0, 5.0, 6.0), &vec3(1.0, 2.0, 3.0)),
451 Ok(vec3(3.0, 3.0, 3.0))
452 );
453 assert_eq!(
454 mul(&num(2.0), &vec3(1.0, 2.0, 3.0)),
455 Ok(vec3(2.0, 4.0, 6.0))
456 );
457 assert_eq!(
459 mul(&vec3(1.0, 2.0, 3.0), &vec3(4.0, 5.0, 6.0)),
460 Ok(num(32.0))
461 );
462 assert!(add(&vec3(1.0, 2.0, 3.0), &Value::vector(vec![num(1.0)])).is_err());
463 }
464
465 #[test]
466 fn matrix_vector_product() {
467 let m = Value::vector(vec![
468 Value::vector(vec![num(1.0), num(0.0)]),
469 Value::vector(vec![num(0.0), num(2.0)]),
470 ]);
471 let v = Value::vector(vec![num(3.0), num(4.0)]);
472 assert_eq!(mul(&m, &v), Ok(Value::vector(vec![num(3.0), num(8.0)])));
473 }
474
475 #[test]
476 fn range_iteration() {
477 let r = Value::Range(RangeValue {
478 start: 0.0,
479 step: 2.0,
480 end: 5.0,
481 });
482 let vals: Vec<f64> = iterate(&r).iter().filter_map(Value::as_number).collect();
483 assert_eq!(vals, vec![0.0, 2.0, 4.0]);
484 let r = Value::Range(RangeValue {
486 start: 5.0,
487 step: 1.0,
488 end: 0.0,
489 });
490 assert!(iterate(&r).is_empty());
491 let r = Value::Range(RangeValue {
493 start: 3.0,
494 step: -1.0,
495 end: 1.0,
496 });
497 assert_eq!(iteration_count(&r), 3);
498 }
499
500 #[test]
501 fn string_index_and_iterate() {
502 let s = Value::String("ab".into());
503 assert_eq!(index(&s, &num(1.0)), Ok(Value::String("b".into())));
504 assert_eq!(iterate(&s).len(), 2);
505 }
506
507 #[test]
508 fn number_formatting() {
509 assert_eq!(num(5.0).str_format(), "5");
510 assert_eq!(num(0.5).str_format(), "0.5");
511 assert_eq!(Value::String("x".into()).echo_format(), "\"x\"");
512 assert_eq!(vec3(1.0, 2.0, 3.0).echo_format(), "[1, 2, 3]");
513 }
514}