1use std::cell::RefCell;
12use std::rc::Rc;
13
14use crate::accumulator::BinnedAccumulatorF64;
15use crate::complex::ComplexF64;
16use crate::scratchpad::Scratchpad;
17use crate::paged_kv::PagedKvCache;
18use crate::tensor::Tensor;
19use crate::tensor_simd::UnaryOp;
20use crate::value::{Bf16, Value};
21
22pub fn value_to_shape(val: &Value) -> Result<Vec<usize>, String> {
28 match val {
29 Value::Array(arr) => {
30 let mut shape = Vec::with_capacity(arr.len());
31 for v in arr.iter() {
32 shape.push(value_to_usize(v)?);
33 }
34 Ok(shape)
35 }
36 _ => Err(format!("expected Array for shape, got {}", val.type_name())),
37 }
38}
39
40pub fn value_to_usize(val: &Value) -> Result<usize, String> {
42 match val {
43 Value::Int(i) => {
44 if *i < 0 {
45 Err(format!("expected non-negative integer, got {i}"))
46 } else {
47 Ok(*i as usize)
48 }
49 }
50 _ => Err(format!("expected Int, got {}", val.type_name())),
51 }
52}
53
54pub fn value_to_f64_vec(val: &Value) -> Result<Vec<f64>, String> {
56 match val {
57 Value::Array(arr) => {
58 let mut data = Vec::with_capacity(arr.len());
59 for v in arr.iter() {
60 match v {
61 Value::Float(f) => data.push(*f),
62 Value::Int(i) => data.push(*i as f64),
63 Value::Na => {}
65 _ => {
66 return Err(format!(
67 "expected numeric values in array, got {}",
68 v.type_name()
69 ));
70 }
71 }
72 }
73 Ok(data)
74 }
75 _ => Err(format!("expected Array, got {}", val.type_name())),
76 }
77}
78
79pub fn value_to_complex_vec(val: &Value) -> Result<Vec<(f64, f64)>, String> {
81 match val {
82 Value::Array(arr) => {
83 let mut data = Vec::with_capacity(arr.len());
84 for v in arr.iter() {
85 match v {
86 Value::Tuple(t) if t.len() == 2 => {
87 let re = match &t[0] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("complex tuple element must be numeric".into()) };
88 let im = match &t[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("complex tuple element must be numeric".into()) };
89 data.push((re, im));
90 }
91 _ => return Err("expected array of (re, im) tuples".into()),
92 }
93 }
94 Ok(data)
95 }
96 _ => Err(format!("expected Array of complex tuples, got {}", val.type_name())),
97 }
98}
99
100pub fn value_to_usize_vec(val: &Value) -> Result<Vec<usize>, String> {
102 match val {
103 Value::Array(arr) => {
104 let mut indices = Vec::with_capacity(arr.len());
105 for v in arr.iter() {
106 indices.push(value_to_usize(v)?);
107 }
108 Ok(indices)
109 }
110 _ => Err(format!("expected Array for indices, got {}", val.type_name())),
111 }
112}
113
114pub fn value_to_tensor(val: &Value) -> Result<&Tensor, String> {
116 match val {
117 Value::Tensor(t) => Ok(t),
118 _ => Err(format!("expected Tensor, got {}", val.type_name())),
119 }
120}
121
122pub fn value_to_f64(val: &Value) -> Result<f64, String> {
124 match val {
125 Value::Float(v) => Ok(*v),
126 Value::Int(v) => Ok(*v as f64),
127 _ => Err(format!("expected Float or Int, got {}", val.type_name())),
128 }
129}
130
131pub fn value_to_bytes(val: &Value) -> Result<Vec<u8>, String> {
133 match val {
134 Value::ByteSlice(b) => Ok(b.as_ref().clone()),
135 Value::Bytes(b) => Ok(b.borrow().clone()),
136 Value::String(s) => Ok(s.as_bytes().to_vec()),
137 _ => Err(format!("expected ByteSlice or Bytes, got {}", val.type_name())),
138 }
139}
140
141pub fn values_equal(a: &Value, b: &Value) -> bool {
143 match (a, b) {
144 (Value::Int(a), Value::Int(b)) => a == b,
145 (Value::Float(a), Value::Float(b)) => a == b,
146 (Value::Bool(a), Value::Bool(b)) => a == b,
147 (Value::String(a), Value::String(b)) => a == b,
148 (Value::Void, Value::Void) => true,
149 _ => false,
150 }
151}
152
153pub fn categorical_sample_with_u(probs: &Tensor, u: f64) -> Result<i64, String> {
161 if probs.ndim() == 0 {
162 return Err("categorical_sample requires at least a 1-D tensor".into());
163 }
164 let data = probs.to_vec();
165 if data.is_empty() {
166 return Err("categorical_sample: empty probability tensor".into());
167 }
168 let mut cumsum = 0.0;
169 for (i, &p) in data.iter().enumerate() {
170 cumsum += p;
171 if u < cumsum {
172 return Ok(i as i64);
173 }
174 }
175 Ok((data.len() - 1) as i64)
177}
178
179pub fn dispatch_builtin(name: &str, args: &[Value]) -> Result<Option<Value>, String> {
186 match name {
187 "Complex" => {
188 let re = match args.get(0) {
189 Some(Value::Float(v)) => *v,
190 Some(Value::Int(v)) => *v as f64,
191 _ => return Err("Complex() requires numeric re argument".into()),
192 };
193 let im = match args.get(1) {
194 Some(Value::Float(v)) => *v,
195 Some(Value::Int(v)) => *v as f64,
196 None => 0.0,
197 _ => return Err("Complex() requires numeric im argument".into()),
198 };
199 Ok(Some(Value::Complex(ComplexF64::new(re, im))))
200 }
201 "f16_to_f64" => match &args[0] {
203 Value::F16(v) => Ok(Some(Value::Float(v.to_f64()))),
204 _ => Err("f16_to_f64 expects f16".into()),
205 },
206 "f64_to_f16" => match &args[0] {
207 Value::Float(v) => Ok(Some(Value::F16(crate::f16::F16::from_f64(*v)))),
208 Value::Int(v) => Ok(Some(Value::F16(crate::f16::F16::from_f64(*v as f64)))),
209 _ => Err("f64_to_f16 expects f64".into()),
210 },
211 "f16_to_f32" => match &args[0] {
212 Value::F16(v) => Ok(Some(Value::Float(v.to_f32() as f64))),
213 _ => Err("f16_to_f32 expects f16".into()),
214 },
215 "f32_to_f16" => match &args[0] {
216 Value::Float(v) => Ok(Some(Value::F16(crate::f16::F16::from_f32(*v as f32)))),
217 _ => Err("f32_to_f16 expects f32".into()),
218 },
219 "bf16_to_f32" => match &args[0] {
221 Value::Bf16(v) => Ok(Some(Value::Float(v.to_f32() as f64))),
222 _ => Err("bf16_to_f32 expects bf16".into()),
223 },
224 "f32_to_bf16" => match &args[0] {
225 Value::Float(v) => Ok(Some(Value::Bf16(Bf16::from_f32(*v as f32)))),
226 _ => Err("f32_to_bf16 expects f32".into()),
227 },
228 "Tensor.zeros" => {
230 let shape = value_to_shape(&args[0])?;
231 Ok(Some(Value::Tensor(Tensor::zeros(&shape))))
232 }
233 "Tensor.ones" => {
234 let shape = value_to_shape(&args[0])?;
235 Ok(Some(Value::Tensor(Tensor::ones(&shape))))
236 }
237 "Tensor.from_vec" => {
238 if args.len() != 2 {
239 return Err("Tensor.from_vec requires 2 arguments: data and shape".into());
240 }
241 let data = value_to_f64_vec(&args[0])?;
242 let shape = value_to_shape(&args[1])?;
243 let t = Tensor::from_vec(data, &shape).map_err(|e| format!("{e}"))?;
244 Ok(Some(Value::Tensor(t)))
245 }
246 "matmul" => {
247 if args.len() != 2 {
248 return Err("matmul requires 2 Tensor arguments".into());
249 }
250 let a = value_to_tensor(&args[0])?;
251 let b = value_to_tensor(&args[1])?;
252 Ok(Some(Value::Tensor(a.matmul(b).map_err(|e| format!("{e}"))?)))
253 }
254 "attention" => {
255 if args.len() != 3 {
256 return Err("attention requires 3 Tensor arguments: queries, keys, values".into());
257 }
258 let q = value_to_tensor(&args[0])?;
259 let k = value_to_tensor(&args[1])?;
260 let v = value_to_tensor(&args[2])?;
261 Ok(Some(Value::Tensor(
262 Tensor::scaled_dot_product_attention(q, k, v).map_err(|e| format!("{e}"))?,
263 )))
264 }
265 "Buffer.alloc" => {
266 if args.is_empty() {
267 return Err("Buffer.alloc requires a length argument".into());
268 }
269 let len = value_to_usize(&args[0])?;
270 Ok(Some(Value::Tensor(Tensor::zeros(&[len]))))
271 }
272 "Tensor.from_bytes" => {
273 if args.len() < 2 || args.len() > 3 {
274 return Err(
275 "Tensor.from_bytes requires 2-3 arguments: bytes, shape, [dtype='f64']".into(),
276 );
277 }
278 let bytes = match &args[0] {
279 Value::ByteSlice(b) => b.clone(),
280 Value::Bytes(b) => Rc::new(b.borrow().clone()),
281 _ => {
282 return Err(
283 "Tensor.from_bytes: first argument must be ByteSlice or Bytes".into(),
284 )
285 }
286 };
287 let shape = value_to_shape(&args[1])?;
288 let dtype = if args.len() == 3 {
289 match &args[2] {
290 Value::String(s) => s.as_str().to_string(),
291 _ => return Err("Tensor.from_bytes: dtype must be a string".into()),
292 }
293 } else {
294 "f64".to_string()
295 };
296 Ok(Some(Value::Tensor(
297 Tensor::from_bytes(&bytes, &shape, &dtype).map_err(|e| format!("{e}"))?,
298 )))
299 }
300 "Scratchpad.new" => {
301 if args.len() != 2 {
302 return Err("Scratchpad.new requires 2 arguments: max_seq_len, dim".into());
303 }
304 let max_seq_len = value_to_usize(&args[0])?;
305 let dim = value_to_usize(&args[1])?;
306 Ok(Some(Value::Scratchpad(Rc::new(RefCell::new(
307 Scratchpad::new(max_seq_len, dim),
308 )))))
309 }
310 "PagedKvCache.new" => {
311 if args.len() != 2 {
312 return Err("PagedKvCache.new requires 2 arguments: max_tokens, dim".into());
313 }
314 let max_tokens = value_to_usize(&args[0])?;
315 let dim = value_to_usize(&args[1])?;
316 Ok(Some(Value::PagedKvCache(Rc::new(RefCell::new(
317 PagedKvCache::new(max_tokens, dim),
318 )))))
319 }
320 "AlignedByteSlice.from_bytes" => {
321 if args.len() != 1 {
322 return Err("AlignedByteSlice.from_bytes requires 1 argument: bytes".into());
323 }
324 let bytes = match &args[0] {
325 Value::ByteSlice(b) => b.clone(),
326 Value::Bytes(b) => Rc::new(b.borrow().clone()),
327 _ => {
328 return Err(
329 "AlignedByteSlice.from_bytes: argument must be ByteSlice or Bytes".into(),
330 )
331 }
332 };
333 Ok(Some(Value::AlignedBytes(
334 crate::aligned_pool::AlignedByteSlice::from_bytes(bytes),
335 )))
336 }
337 "to_string" => {
338 if args.len() != 1 {
339 return Err("to_string requires exactly 1 argument".into());
340 }
341 Ok(Some(Value::String(Rc::new(format!("{}", args[0])))))
342 }
343
344 "str_upper" => {
346 if args.len() != 1 { return Err("str_upper requires 1 argument".into()); }
347 match &args[0] {
348 Value::String(s) => Ok(Some(Value::String(Rc::new(s.to_uppercase())))),
349 _ => Err("str_upper: argument must be a string".into()),
350 }
351 }
352 "str_lower" => {
353 if args.len() != 1 { return Err("str_lower requires 1 argument".into()); }
354 match &args[0] {
355 Value::String(s) => Ok(Some(Value::String(Rc::new(s.to_lowercase())))),
356 _ => Err("str_lower: argument must be a string".into()),
357 }
358 }
359 "str_trim" => {
360 if args.len() != 1 { return Err("str_trim requires 1 argument".into()); }
361 match &args[0] {
362 Value::String(s) => Ok(Some(Value::String(Rc::new(s.trim().to_string())))),
363 _ => Err("str_trim: argument must be a string".into()),
364 }
365 }
366 "str_contains" => {
367 if args.len() != 2 { return Err("str_contains requires 2 arguments".into()); }
368 match (&args[0], &args[1]) {
369 (Value::String(haystack), Value::String(needle)) => {
370 Ok(Some(Value::Bool(haystack.contains(needle.as_str()))))
371 }
372 _ => Err("str_contains: both arguments must be strings".into()),
373 }
374 }
375 "str_replace" => {
376 if args.len() != 3 { return Err("str_replace requires 3 arguments (str, from, to)".into()); }
377 match (&args[0], &args[1], &args[2]) {
378 (Value::String(s), Value::String(from), Value::String(to)) => {
379 Ok(Some(Value::String(Rc::new(s.replacen(from.as_str(), to.as_str(), 1)))))
382 }
383 _ => Err("str_replace: all arguments must be strings".into()),
384 }
385 }
386 "str_split" => {
387 if args.len() != 2 { return Err("str_split requires 2 arguments (str, delimiter)".into()); }
388 match (&args[0], &args[1]) {
389 (Value::String(s), Value::String(delim)) => {
390 let parts: Vec<Value> = s.split(delim.as_str())
391 .map(|p| Value::String(Rc::new(p.to_string())))
392 .collect();
393 Ok(Some(Value::Array(Rc::new(parts))))
394 }
395 _ => Err("str_split: both arguments must be strings".into()),
396 }
397 }
398 "str_join" => {
399 if args.len() != 2 { return Err("str_join requires 2 arguments (array, delimiter)".into()); }
400 match (&args[0], &args[1]) {
401 (Value::Array(arr), Value::String(delim)) => {
402 let parts: Vec<String> = arr.iter()
403 .map(|v| format!("{}", v))
404 .collect();
405 Ok(Some(Value::String(Rc::new(parts.join(delim.as_str())))))
406 }
407 _ => Err("str_join: first arg must be array, second must be string".into()),
408 }
409 }
410 "str_starts_with" => {
411 if args.len() != 2 { return Err("str_starts_with requires 2 arguments".into()); }
412 match (&args[0], &args[1]) {
413 (Value::String(s), Value::String(prefix)) => {
414 Ok(Some(Value::Bool(s.starts_with(prefix.as_str()))))
415 }
416 _ => Err("str_starts_with: both arguments must be strings".into()),
417 }
418 }
419 "str_ends_with" => {
420 if args.len() != 2 { return Err("str_ends_with requires 2 arguments".into()); }
421 match (&args[0], &args[1]) {
422 (Value::String(s), Value::String(suffix)) => {
423 Ok(Some(Value::Bool(s.ends_with(suffix.as_str()))))
424 }
425 _ => Err("str_ends_with: both arguments must be strings".into()),
426 }
427 }
428 "str_repeat" => {
429 if args.len() != 2 { return Err("str_repeat requires 2 arguments (str, count)".into()); }
430 match (&args[0], &args[1]) {
431 (Value::String(s), Value::Int(n)) => {
432 if *n < 0 { return Err("str_repeat: count must be non-negative".into()); }
433 Ok(Some(Value::String(Rc::new(s.repeat(*n as usize)))))
434 }
435 _ => Err("str_repeat: first arg must be string, second must be integer".into()),
436 }
437 }
438 "str_chars" => {
439 if args.len() != 1 { return Err("str_chars requires 1 argument".into()); }
440 match &args[0] {
441 Value::String(s) => {
442 let chars: Vec<Value> = s.chars()
443 .map(|c| Value::String(Rc::new(c.to_string())))
444 .collect();
445 Ok(Some(Value::Array(Rc::new(chars))))
446 }
447 _ => Err("str_chars: argument must be a string".into()),
448 }
449 }
450 "str_substr" => {
451 if args.len() != 3 { return Err("str_substr requires 3 arguments (str, start, len)".into()); }
452 match (&args[0], &args[1], &args[2]) {
453 (Value::String(s), Value::Int(start), Value::Int(len)) => {
454 let start = (*start).max(0) as usize;
455 let len = (*len).max(0) as usize;
456 let result: String = s.chars().skip(start).take(len).collect();
457 Ok(Some(Value::String(Rc::new(result))))
458 }
459 _ => Err("str_substr: (str, int, int) expected".into()),
460 }
461 }
462
463 "len" => {
464 if args.len() != 1 {
465 return Err("len requires exactly 1 argument".into());
466 }
467 match &args[0] {
468 Value::Array(arr) => Ok(Some(Value::Int(arr.len() as i64))),
469 Value::String(s) => Ok(Some(Value::Int(s.len() as i64))),
470 Value::Tensor(t) => Ok(Some(Value::Int(t.len() as i64))),
471 Value::Tuple(t) => Ok(Some(Value::Int(t.len() as i64))),
472 other => Err(format!("len not supported for {}", other.type_name())),
473 }
474 }
475 "push" => {
476 if args.len() != 2 {
477 return Err("push requires 2 arguments: array and value".into());
478 }
479 match (&args[0], &args[1]) {
480 (Value::Array(a), val) => {
481 let mut new_arr = (**a).clone();
482 new_arr.push(val.clone());
483 Ok(Some(Value::Array(Rc::new(new_arr))))
484 }
485 _ => Err("push requires an Array as first argument".into()),
486 }
487 }
488 "sort" => {
489 if args.len() != 1 {
490 return Err("sort requires exactly 1 argument".into());
491 }
492 match &args[0] {
493 Value::Array(arr) => {
494 let mut sorted: Vec<Value> = (**arr).clone();
495 sorted.sort_by(|a, b| {
496 let fa = match a {
497 Value::Float(f) => *f,
498 Value::Int(i) => *i as f64,
499 _ => f64::NAN,
500 };
501 let fb = match b {
502 Value::Float(f) => *f,
503 Value::Int(i) => *i as f64,
504 _ => f64::NAN,
505 };
506 fa.partial_cmp(&fb).unwrap_or(std::cmp::Ordering::Equal)
507 });
508 Ok(Some(Value::Array(Rc::new(sorted))))
509 }
510 _ => Err(format!("sort requires an Array, got {}", args[0].type_name())),
511 }
512 }
513 "sqrt" => {
514 if args.len() != 1 {
515 return Err("sqrt requires exactly 1 argument".into());
516 }
517 match &args[0] {
518 Value::Float(f) => Ok(Some(Value::Float(f.sqrt()))),
519 Value::Int(i) => Ok(Some(Value::Float((*i as f64).sqrt()))),
520 _ => Err(format!("sqrt requires a number, got {}", args[0].type_name())),
521 }
522 }
523 "log" => {
524 if args.len() != 1 {
525 return Err("log requires exactly 1 argument".into());
526 }
527 match &args[0] {
528 Value::Float(f) => Ok(Some(Value::Float(f.ln()))),
529 Value::Int(i) => Ok(Some(Value::Float((*i as f64).ln()))),
530 _ => Err(format!("log requires a number, got {}", args[0].type_name())),
531 }
532 }
533 "exp" => {
534 if args.len() != 1 {
535 return Err("exp requires exactly 1 argument".into());
536 }
537 match &args[0] {
538 Value::Float(f) => Ok(Some(Value::Float(f.exp()))),
539 Value::Int(i) => Ok(Some(Value::Float((*i as f64).exp()))),
540 _ => Err(format!("exp requires a number, got {}", args[0].type_name())),
541 }
542 }
543 "sin" => {
545 if args.len() != 1 { return Err("sin requires exactly 1 argument".into()); }
546 match &args[0] {
547 Value::Float(f) => Ok(Some(Value::Float(f.sin()))),
548 Value::Int(i) => Ok(Some(Value::Float((*i as f64).sin()))),
549 _ => Err(format!("sin requires a number, got {}", args[0].type_name())),
550 }
551 }
552 "cos" => {
553 if args.len() != 1 { return Err("cos requires exactly 1 argument".into()); }
554 match &args[0] {
555 Value::Float(f) => Ok(Some(Value::Float(f.cos()))),
556 Value::Int(i) => Ok(Some(Value::Float((*i as f64).cos()))),
557 _ => Err(format!("cos requires a number, got {}", args[0].type_name())),
558 }
559 }
560 "tan" => {
561 if args.len() != 1 { return Err("tan requires exactly 1 argument".into()); }
562 match &args[0] {
563 Value::Float(f) => Ok(Some(Value::Float(f.tan()))),
564 Value::Int(i) => Ok(Some(Value::Float((*i as f64).tan()))),
565 _ => Err(format!("tan requires a number, got {}", args[0].type_name())),
566 }
567 }
568 "asin" => {
569 if args.len() != 1 { return Err("asin requires exactly 1 argument".into()); }
570 match &args[0] {
571 Value::Float(f) => Ok(Some(Value::Float(f.asin()))),
572 Value::Int(i) => Ok(Some(Value::Float((*i as f64).asin()))),
573 _ => Err(format!("asin requires a number, got {}", args[0].type_name())),
574 }
575 }
576 "acos" => {
577 if args.len() != 1 { return Err("acos requires exactly 1 argument".into()); }
578 match &args[0] {
579 Value::Float(f) => Ok(Some(Value::Float(f.acos()))),
580 Value::Int(i) => Ok(Some(Value::Float((*i as f64).acos()))),
581 _ => Err(format!("acos requires a number, got {}", args[0].type_name())),
582 }
583 }
584 "atan" => {
585 if args.len() != 1 { return Err("atan requires exactly 1 argument".into()); }
586 match &args[0] {
587 Value::Float(f) => Ok(Some(Value::Float(f.atan()))),
588 Value::Int(i) => Ok(Some(Value::Float((*i as f64).atan()))),
589 _ => Err(format!("atan requires a number, got {}", args[0].type_name())),
590 }
591 }
592 "atan2" => {
593 if args.len() != 2 { return Err("atan2 requires exactly 2 arguments".into()); }
594 let y = match &args[0] {
595 Value::Float(f) => *f,
596 Value::Int(i) => *i as f64,
597 _ => return Err(format!("atan2 requires numbers, got {}", args[0].type_name())),
598 };
599 let x = match &args[1] {
600 Value::Float(f) => *f,
601 Value::Int(i) => *i as f64,
602 _ => return Err(format!("atan2 requires numbers, got {}", args[1].type_name())),
603 };
604 Ok(Some(Value::Float(y.atan2(x))))
605 }
606 "sinh" => {
608 if args.len() != 1 { return Err("sinh requires exactly 1 argument".into()); }
609 match &args[0] {
610 Value::Float(f) => Ok(Some(Value::Float(f.sinh()))),
611 Value::Int(i) => Ok(Some(Value::Float((*i as f64).sinh()))),
612 _ => Err(format!("sinh requires a number, got {}", args[0].type_name())),
613 }
614 }
615 "cosh" => {
616 if args.len() != 1 { return Err("cosh requires exactly 1 argument".into()); }
617 match &args[0] {
618 Value::Float(f) => Ok(Some(Value::Float(f.cosh()))),
619 Value::Int(i) => Ok(Some(Value::Float((*i as f64).cosh()))),
620 _ => Err(format!("cosh requires a number, got {}", args[0].type_name())),
621 }
622 }
623 "tanh" | "tanh_scalar" => {
624 if args.len() != 1 { return Err("tanh requires exactly 1 argument".into()); }
625 match &args[0] {
626 Value::Float(f) => Ok(Some(Value::Float(f.tanh()))),
627 Value::Int(i) => Ok(Some(Value::Float((*i as f64).tanh()))),
628 Value::Tensor(t) => Ok(Some(Value::Tensor(t.tanh_activation()))),
629 _ => Err(format!("tanh requires a number or Tensor, got {}", args[0].type_name())),
630 }
631 }
632 "pow" => {
634 if args.len() != 2 { return Err("pow requires exactly 2 arguments".into()); }
635 let base = match &args[0] {
636 Value::Float(f) => *f,
637 Value::Int(i) => *i as f64,
638 _ => return Err(format!("pow requires numbers, got {}", args[0].type_name())),
639 };
640 let exp = match &args[1] {
641 Value::Float(f) => *f,
642 Value::Int(i) => *i as f64,
643 _ => return Err(format!("pow requires numbers, got {}", args[1].type_name())),
644 };
645 Ok(Some(Value::Float(base.powf(exp))))
646 }
647 "log2" => {
648 if args.len() != 1 { return Err("log2 requires exactly 1 argument".into()); }
649 match &args[0] {
650 Value::Float(f) => Ok(Some(Value::Float(f.log2()))),
651 Value::Int(i) => Ok(Some(Value::Float((*i as f64).log2()))),
652 _ => Err(format!("log2 requires a number, got {}", args[0].type_name())),
653 }
654 }
655 "log10" => {
656 if args.len() != 1 { return Err("log10 requires exactly 1 argument".into()); }
657 match &args[0] {
658 Value::Float(f) => Ok(Some(Value::Float(f.log10()))),
659 Value::Int(i) => Ok(Some(Value::Float((*i as f64).log10()))),
660 _ => Err(format!("log10 requires a number, got {}", args[0].type_name())),
661 }
662 }
663 "log1p" => {
664 if args.len() != 1 { return Err("log1p requires exactly 1 argument".into()); }
665 match &args[0] {
666 Value::Float(f) => Ok(Some(Value::Float(f.ln_1p()))),
667 Value::Int(i) => Ok(Some(Value::Float((*i as f64).ln_1p()))),
668 _ => Err(format!("log1p requires a number, got {}", args[0].type_name())),
669 }
670 }
671 "expm1" => {
672 if args.len() != 1 { return Err("expm1 requires exactly 1 argument".into()); }
673 match &args[0] {
674 Value::Float(f) => Ok(Some(Value::Float(f.exp_m1()))),
675 Value::Int(i) => Ok(Some(Value::Float((*i as f64).exp_m1()))),
676 _ => Err(format!("expm1 requires a number, got {}", args[0].type_name())),
677 }
678 }
679 "ceil" => {
681 if args.len() != 1 { return Err("ceil requires exactly 1 argument".into()); }
682 match &args[0] {
683 Value::Float(f) => Ok(Some(Value::Float(f.ceil()))),
684 Value::Int(i) => Ok(Some(Value::Int(*i))),
685 _ => Err(format!("ceil requires a number, got {}", args[0].type_name())),
686 }
687 }
688 "round" => {
689 if args.len() != 1 { return Err("round requires exactly 1 argument".into()); }
690 match &args[0] {
691 Value::Float(f) => Ok(Some(Value::Float(f.round()))),
692 Value::Int(i) => Ok(Some(Value::Int(*i))),
693 _ => Err(format!("round requires a number, got {}", args[0].type_name())),
694 }
695 }
696 "floor" => {
697 if args.len() != 1 {
698 return Err("floor requires exactly 1 argument".into());
699 }
700 match &args[0] {
701 Value::Float(f) => Ok(Some(Value::Float(f.floor()))),
702 Value::Int(i) => Ok(Some(Value::Int(*i))),
703 _ => Err(format!("floor requires a number, got {}", args[0].type_name())),
704 }
705 }
706 "int" => {
707 if args.len() != 1 {
708 return Err("int requires exactly 1 argument".into());
709 }
710 match &args[0] {
711 Value::Float(f) => Ok(Some(Value::Int(*f as i64))),
712 Value::Int(i) => Ok(Some(Value::Int(*i))),
713 Value::Bool(b) => Ok(Some(Value::Int(if *b { 1 } else { 0 }))),
714 _ => Err(format!("int requires a number, got {}", args[0].type_name())),
715 }
716 }
717 "float" => {
718 if args.len() != 1 {
719 return Err("float requires exactly 1 argument".into());
720 }
721 match &args[0] {
722 Value::Int(i) => Ok(Some(Value::Float(*i as f64))),
723 Value::Float(f) => Ok(Some(Value::Float(*f))),
724 Value::Bool(b) => Ok(Some(Value::Float(if *b { 1.0 } else { 0.0 }))),
725 _ => Err(format!("float requires a number, got {}", args[0].type_name())),
726 }
727 }
728 "isnan" => {
729 if args.len() != 1 {
730 return Err("isnan requires exactly 1 argument".into());
731 }
732 match &args[0] {
733 Value::Float(f) => Ok(Some(Value::Bool(f.is_nan()))),
734 Value::Int(_) => Ok(Some(Value::Bool(false))),
735 _ => Err(format!("isnan requires a number, got {}", args[0].type_name())),
736 }
737 }
738 "isinf" => {
739 if args.len() != 1 {
740 return Err("isinf requires exactly 1 argument".into());
741 }
742 match &args[0] {
743 Value::Float(f) => Ok(Some(Value::Bool(f.is_infinite()))),
744 Value::Int(_) => Ok(Some(Value::Bool(false))),
745 _ => Err(format!("isinf requires a number, got {}", args[0].type_name())),
746 }
747 }
748 "abs" => {
749 if args.len() != 1 {
750 return Err("abs requires exactly 1 argument".into());
751 }
752 match &args[0] {
753 Value::Float(f) => Ok(Some(Value::Float(f.abs()))),
754 Value::Int(i) => Ok(Some(Value::Int(i.abs()))),
755 _ => Err(format!("abs requires a number, got {}", args[0].type_name())),
756 }
757 }
758 "min" => {
760 if args.len() != 2 { return Err("min requires exactly 2 arguments".into()); }
761 let a = match &args[0] {
762 Value::Float(f) => *f,
763 Value::Int(i) => *i as f64,
764 _ => return Err(format!("min requires numbers, got {}", args[0].type_name())),
765 };
766 let b = match &args[1] {
767 Value::Float(f) => *f,
768 Value::Int(i) => *i as f64,
769 _ => return Err(format!("min requires numbers, got {}", args[1].type_name())),
770 };
771 Ok(Some(Value::Float(a.min(b))))
772 }
773 "max" => {
774 if args.len() != 2 { return Err("max requires exactly 2 arguments".into()); }
775 let a = match &args[0] {
776 Value::Float(f) => *f,
777 Value::Int(i) => *i as f64,
778 _ => return Err(format!("max requires numbers, got {}", args[0].type_name())),
779 };
780 let b = match &args[1] {
781 Value::Float(f) => *f,
782 Value::Int(i) => *i as f64,
783 _ => return Err(format!("max requires numbers, got {}", args[1].type_name())),
784 };
785 Ok(Some(Value::Float(a.max(b))))
786 }
787 "sign" => {
788 if args.len() != 1 { return Err("sign requires exactly 1 argument".into()); }
789 match &args[0] {
790 Value::Float(f) => Ok(Some(Value::Float(f.signum()))),
791 Value::Int(i) => Ok(Some(Value::Float((*i as f64).signum()))),
792 _ => Err(format!("sign requires a number, got {}", args[0].type_name())),
793 }
794 }
795 "hypot" => {
797 if args.len() != 2 { return Err("hypot requires exactly 2 arguments".into()); }
798 let x = match &args[0] {
799 Value::Float(f) => *f,
800 Value::Int(i) => *i as f64,
801 _ => return Err(format!("hypot requires numbers, got {}", args[0].type_name())),
802 };
803 let y = match &args[1] {
804 Value::Float(f) => *f,
805 Value::Int(i) => *i as f64,
806 _ => return Err(format!("hypot requires numbers, got {}", args[1].type_name())),
807 };
808 Ok(Some(Value::Float(x.hypot(y))))
809 }
810 "PI" => {
812 if !args.is_empty() { return Err("PI takes no arguments".into()); }
813 Ok(Some(Value::Float(std::f64::consts::PI)))
814 }
815 "E" => {
816 if !args.is_empty() { return Err("E takes no arguments".into()); }
817 Ok(Some(Value::Float(std::f64::consts::E)))
818 }
819 "TAU" => {
820 if !args.is_empty() { return Err("TAU takes no arguments".into()); }
821 Ok(Some(Value::Float(std::f64::consts::TAU)))
822 }
823 "INF" => {
824 if !args.is_empty() { return Err("INF takes no arguments".into()); }
825 Ok(Some(Value::Float(f64::INFINITY)))
826 }
827 "NAN_VAL" => {
828 if !args.is_empty() { return Err("NAN_VAL takes no arguments".into()); }
829 Ok(Some(Value::Float(f64::NAN)))
830 }
831 "dot" => {
833 if args.len() != 2 { return Err("dot requires exactly 2 arguments".into()); }
834 let a = match &args[0] {
835 Value::Tensor(t) => t,
836 _ => return Err(format!("dot requires tensors, got {}", args[0].type_name())),
837 };
838 let b = match &args[1] {
839 Value::Tensor(t) => t,
840 _ => return Err(format!("dot requires tensors, got {}", args[1].type_name())),
841 };
842 if a.ndim() != 1 || b.ndim() != 1 {
843 return Err("dot requires 1D tensors".into());
844 }
845 if a.len() != b.len() {
846 return Err(format!("dot: length mismatch ({} vs {})", a.len(), b.len()));
847 }
848 let av = a.to_vec();
849 let bv = b.to_vec();
850 let products: Vec<f64> = av.iter().zip(bv.iter()).map(|(x, y)| x * y).collect();
851 let sum = crate::accumulator::binned_sum_f64(&products);
852 Ok(Some(Value::Float(sum)))
853 }
854 "outer" => {
855 if args.len() != 2 { return Err("outer requires exactly 2 arguments".into()); }
856 let a = match &args[0] {
857 Value::Tensor(t) => t,
858 _ => return Err(format!("outer requires tensors, got {}", args[0].type_name())),
859 };
860 let b = match &args[1] {
861 Value::Tensor(t) => t,
862 _ => return Err(format!("outer requires tensors, got {}", args[1].type_name())),
863 };
864 if a.ndim() != 1 || b.ndim() != 1 {
865 return Err("outer requires 1D tensors".into());
866 }
867 let av = a.to_vec();
868 let bv = b.to_vec();
869 let m = av.len();
870 let n = bv.len();
871 let mut data = Vec::with_capacity(m * n);
872 for ai in &av {
873 for bj in &bv {
874 data.push(ai * bj);
875 }
876 }
877 Ok(Some(Value::Tensor(Tensor::from_vec(data, &[m, n]).map_err(|e| format!("{e}"))?)))
878 }
879 "cross" => {
880 if args.len() != 2 { return Err("cross requires exactly 2 arguments".into()); }
881 let a = match &args[0] {
882 Value::Tensor(t) => t,
883 _ => return Err(format!("cross requires tensors, got {}", args[0].type_name())),
884 };
885 let b = match &args[1] {
886 Value::Tensor(t) => t,
887 _ => return Err(format!("cross requires tensors, got {}", args[1].type_name())),
888 };
889 if a.ndim() != 1 || b.ndim() != 1 || a.len() != 3 || b.len() != 3 {
890 return Err("cross requires two 3-element 1D tensors".into());
891 }
892 let av = a.to_vec();
893 let bv = b.to_vec();
894 let result = vec![
895 av[1] * bv[2] - av[2] * bv[1],
896 av[2] * bv[0] - av[0] * bv[2],
897 av[0] * bv[1] - av[1] * bv[0],
898 ];
899 Ok(Some(Value::Tensor(Tensor::from_vec(result, &[3]).map_err(|e| format!("{e}"))?)))
900 }
901 "norm" => {
902 if args.len() < 1 || args.len() > 2 { return Err("norm requires 1-2 arguments".into()); }
903 let t = match &args[0] {
904 Value::Tensor(t) => t,
905 _ => return Err(format!("norm requires a tensor, got {}", args[0].type_name())),
906 };
907 let ord = if args.len() == 2 {
908 match &args[1] {
909 Value::Int(i) => *i,
910 Value::Float(f) => *f as i64,
911 _ => return Err("norm: ord must be an integer".into()),
912 }
913 } else {
914 2 };
916 let data = t.to_vec();
917 let result = match ord {
918 1 => {
919 let abs_vals: Vec<f64> = data.iter().map(|x| x.abs()).collect();
920 crate::accumulator::binned_sum_f64(&abs_vals)
921 }
922 2 => {
923 let sq_vals: Vec<f64> = data.iter().map(|x| x * x).collect();
924 crate::accumulator::binned_sum_f64(&sq_vals).sqrt()
925 }
926 _ => {
927 let p = ord as f64;
928 let pow_vals: Vec<f64> = data.iter().map(|x| x.abs().powf(p)).collect();
929 crate::accumulator::binned_sum_f64(&pow_vals).powf(1.0 / p)
930 }
931 };
932 Ok(Some(Value::Float(result)))
933 }
934 "Tensor.linspace" => {
936 if args.len() != 3 { return Err("Tensor.linspace requires 3 arguments (start, end, n)".into()); }
937 let start = match &args[0] {
938 Value::Float(f) => *f,
939 Value::Int(i) => *i as f64,
940 _ => return Err("Tensor.linspace: start must be a number".into()),
941 };
942 let end = match &args[1] {
943 Value::Float(f) => *f,
944 Value::Int(i) => *i as f64,
945 _ => return Err("Tensor.linspace: end must be a number".into()),
946 };
947 let n = match &args[2] {
948 Value::Int(i) => *i as usize,
949 _ => return Err("Tensor.linspace: n must be an integer".into()),
950 };
951 if n == 0 {
952 return Ok(Some(Value::Tensor(Tensor::from_vec(vec![], &[0]).map_err(|e| format!("{e}"))?)));
953 }
954 if n == 1 {
955 return Ok(Some(Value::Tensor(Tensor::from_vec(vec![start], &[1]).map_err(|e| format!("{e}"))?)));
956 }
957 let step = (end - start) / (n as f64 - 1.0);
958 let data: Vec<f64> = (0..n).map(|i| start + step * i as f64).collect();
959 Ok(Some(Value::Tensor(Tensor::from_vec(data, &[n]).map_err(|e| format!("{e}"))?)))
960 }
961 "Tensor.arange" => {
962 if args.len() < 2 || args.len() > 3 { return Err("Tensor.arange requires 2-3 arguments (start, end, step?)".into()); }
963 let start = match &args[0] {
964 Value::Float(f) => *f,
965 Value::Int(i) => *i as f64,
966 _ => return Err("Tensor.arange: start must be a number".into()),
967 };
968 let end = match &args[1] {
969 Value::Float(f) => *f,
970 Value::Int(i) => *i as f64,
971 _ => return Err("Tensor.arange: end must be a number".into()),
972 };
973 let step = if args.len() == 3 {
974 match &args[2] {
975 Value::Float(f) => *f,
976 Value::Int(i) => *i as f64,
977 _ => return Err("Tensor.arange: step must be a number".into()),
978 }
979 } else {
980 1.0
981 };
982 if step == 0.0 { return Err("Tensor.arange: step cannot be zero".into()); }
983 let mut data = Vec::new();
984 let mut val = start;
985 if step > 0.0 {
986 while val < end { data.push(val); val += step; }
987 } else {
988 while val > end { data.push(val); val += step; }
989 }
990 let n = data.len();
991 Ok(Some(Value::Tensor(Tensor::from_vec(data, &[n]).map_err(|e| format!("{e}"))?)))
992 }
993 "Tensor.eye" => {
994 if args.len() != 1 { return Err("Tensor.eye requires 1 argument (n)".into()); }
995 let n = match &args[0] {
996 Value::Int(i) => *i as usize,
997 _ => return Err("Tensor.eye: n must be an integer".into()),
998 };
999 let mut data = vec![0.0; n * n];
1000 for i in 0..n {
1001 data[i * n + i] = 1.0;
1002 }
1003 Ok(Some(Value::Tensor(Tensor::from_vec(data, &[n, n]).map_err(|e| format!("{e}"))?)))
1004 }
1005 "Tensor.full" => {
1006 if args.len() != 2 { return Err("Tensor.full requires 2 arguments (shape, value)".into()); }
1007 let shape = match &args[0] {
1008 Value::Array(arr) => {
1009 let mut s = Vec::new();
1010 for v in arr.iter() {
1011 match v {
1012 Value::Int(i) => s.push(*i as usize),
1013 _ => return Err("Tensor.full: shape must be an array of ints".into()),
1014 }
1015 }
1016 s
1017 }
1018 _ => return Err("Tensor.full: shape must be an array".into()),
1019 };
1020 let fill_val = match &args[1] {
1021 Value::Float(f) => *f,
1022 Value::Int(i) => *i as f64,
1023 _ => return Err("Tensor.full: value must be a number".into()),
1024 };
1025 let total: usize = shape.iter().product();
1026 let data = vec![fill_val; total];
1027 Ok(Some(Value::Tensor(Tensor::from_vec(data, &shape).map_err(|e| format!("{e}"))?)))
1028 }
1029 "Tensor.diag" => {
1030 if args.len() != 1 { return Err("Tensor.diag requires 1 argument".into()); }
1031 let t = match &args[0] {
1032 Value::Tensor(t) => t,
1033 _ => return Err("Tensor.diag requires a tensor".into()),
1034 };
1035 match t.ndim() {
1036 1 => {
1037 let data = t.to_vec();
1039 let n = data.len();
1040 let mut out = vec![0.0; n * n];
1041 for i in 0..n {
1042 out[i * n + i] = data[i];
1043 }
1044 Ok(Some(Value::Tensor(Tensor::from_vec(out, &[n, n]).map_err(|e| format!("{e}"))?)))
1045 }
1046 2 => {
1047 let rows = t.shape()[0];
1049 let cols = t.shape()[1];
1050 let n = rows.min(cols);
1051 let mut data = Vec::with_capacity(n);
1052 for i in 0..n {
1053 data.push(t.get(&[i, i]).map_err(|e| format!("{e}"))?);
1054 }
1055 Ok(Some(Value::Tensor(Tensor::from_vec(data, &[n]).map_err(|e| format!("{e}"))?)))
1056 }
1057 _ => Err("Tensor.diag requires a 1D or 2D tensor".into()),
1058 }
1059 }
1060 "assert" => {
1061 if args.len() != 1 {
1062 return Err("assert requires exactly 1 argument".into());
1063 }
1064 match &args[0] {
1065 Value::Bool(true) => Ok(Some(Value::Void)),
1066 Value::Bool(false) => Err("assertion failed".into()),
1067 other => Err(format!("assert requires Bool, got {}", other.type_name())),
1068 }
1069 }
1070 "assert_eq" => {
1071 if args.len() != 2 {
1072 return Err("assert_eq requires exactly 2 arguments".into());
1073 }
1074 if values_equal(&args[0], &args[1]) {
1075 Ok(Some(Value::Void))
1076 } else {
1077 Err(format!("assertion failed: `{}` != `{}`", args[0], args[1]))
1078 }
1079 }
1080 "json_parse" => {
1082 if args.len() != 1 {
1083 return Err("json_parse requires exactly 1 argument".into());
1084 }
1085 let s = match &args[0] {
1086 Value::String(s) => s.as_str(),
1087 _ => return Err(format!("json_parse requires String, got {}", args[0].type_name())),
1088 };
1089 crate::json::json_parse(s).map(Some)
1090 }
1091 "json_stringify" => {
1092 if args.len() != 1 {
1093 return Err("json_stringify requires exactly 1 argument".into());
1094 }
1095 crate::json::json_stringify(&args[0]).map(|s| Some(Value::String(Rc::new(s))))
1096 }
1097
1098 "datetime_from_epoch" => {
1100 if args.len() != 1 {
1101 return Err("datetime_from_epoch requires exactly 1 argument".into());
1102 }
1103 match &args[0] {
1104 Value::Int(n) => Ok(Some(Value::Int(crate::datetime::datetime_from_epoch(*n)))),
1105 _ => Err(format!("datetime_from_epoch requires Int, got {}", args[0].type_name())),
1106 }
1107 }
1108 "datetime_from_parts" => {
1109 if args.len() != 6 {
1110 return Err("datetime_from_parts requires 6 arguments (year, month, day, hour, min, sec)".into());
1111 }
1112 let mut vals = [0i64; 6];
1113 for (i, arg) in args.iter().enumerate() {
1114 match arg {
1115 Value::Int(n) => vals[i] = *n,
1116 _ => return Err(format!("datetime_from_parts arg {} must be Int", i)),
1117 }
1118 }
1119 Ok(Some(Value::Int(crate::datetime::datetime_from_parts(
1120 vals[0], vals[1], vals[2], vals[3], vals[4], vals[5],
1121 ))))
1122 }
1123 "datetime_year" => {
1124 if args.len() != 1 { return Err("datetime_year requires 1 argument".into()); }
1125 match &args[0] {
1126 Value::Int(n) => Ok(Some(Value::Int(crate::datetime::datetime_year(*n)))),
1127 _ => Err(format!("datetime_year requires Int, got {}", args[0].type_name())),
1128 }
1129 }
1130 "datetime_month" => {
1131 if args.len() != 1 { return Err("datetime_month requires 1 argument".into()); }
1132 match &args[0] {
1133 Value::Int(n) => Ok(Some(Value::Int(crate::datetime::datetime_month(*n)))),
1134 _ => Err(format!("datetime_month requires Int, got {}", args[0].type_name())),
1135 }
1136 }
1137 "datetime_day" => {
1138 if args.len() != 1 { return Err("datetime_day requires 1 argument".into()); }
1139 match &args[0] {
1140 Value::Int(n) => Ok(Some(Value::Int(crate::datetime::datetime_day(*n)))),
1141 _ => Err(format!("datetime_day requires Int, got {}", args[0].type_name())),
1142 }
1143 }
1144 "datetime_hour" => {
1145 if args.len() != 1 { return Err("datetime_hour requires 1 argument".into()); }
1146 match &args[0] {
1147 Value::Int(n) => Ok(Some(Value::Int(crate::datetime::datetime_hour(*n)))),
1148 _ => Err(format!("datetime_hour requires Int, got {}", args[0].type_name())),
1149 }
1150 }
1151 "datetime_minute" => {
1152 if args.len() != 1 { return Err("datetime_minute requires 1 argument".into()); }
1153 match &args[0] {
1154 Value::Int(n) => Ok(Some(Value::Int(crate::datetime::datetime_minute(*n)))),
1155 _ => Err(format!("datetime_minute requires Int, got {}", args[0].type_name())),
1156 }
1157 }
1158 "datetime_second" => {
1159 if args.len() != 1 { return Err("datetime_second requires 1 argument".into()); }
1160 match &args[0] {
1161 Value::Int(n) => Ok(Some(Value::Int(crate::datetime::datetime_second(*n)))),
1162 _ => Err(format!("datetime_second requires Int, got {}", args[0].type_name())),
1163 }
1164 }
1165 "datetime_diff" => {
1166 if args.len() != 2 { return Err("datetime_diff requires 2 arguments".into()); }
1167 match (&args[0], &args[1]) {
1168 (Value::Int(a), Value::Int(b)) => Ok(Some(Value::Int(crate::datetime::datetime_diff(*a, *b)))),
1169 _ => Err("datetime_diff requires two Int arguments".into()),
1170 }
1171 }
1172 "datetime_add_millis" => {
1173 if args.len() != 2 { return Err("datetime_add_millis requires 2 arguments".into()); }
1174 match (&args[0], &args[1]) {
1175 (Value::Int(a), Value::Int(b)) => Ok(Some(Value::Int(crate::datetime::datetime_add_millis(*a, *b)))),
1176 _ => Err("datetime_add_millis requires two Int arguments".into()),
1177 }
1178 }
1179 "datetime_format" => {
1180 if args.len() != 1 { return Err("datetime_format requires 1 argument".into()); }
1181 match &args[0] {
1182 Value::Int(n) => Ok(Some(Value::String(Rc::new(crate::datetime::datetime_format(*n))))),
1183 _ => Err(format!("datetime_format requires Int, got {}", args[0].type_name())),
1184 }
1185 }
1186
1187 "file_read" => {
1189 if args.len() != 1 { return Err("file_read requires 1 argument".into()); }
1190 match &args[0] {
1191 Value::String(path) => {
1192 let content = std::fs::read_to_string(path.as_str())
1193 .map_err(|e| format!("file_read error: {}", e))?;
1194 Ok(Some(Value::String(Rc::new(content))))
1195 }
1196 _ => Err(format!("file_read requires String path, got {}", args[0].type_name())),
1197 }
1198 }
1199 "file_write" => {
1200 if args.len() != 2 { return Err("file_write requires 2 arguments (path, content)".into()); }
1201 match (&args[0], &args[1]) {
1202 (Value::String(path), Value::String(content)) => {
1203 std::fs::write(path.as_str(), content.as_str())
1204 .map_err(|e| format!("file_write error: {}", e))?;
1205 Ok(Some(Value::Void))
1206 }
1207 _ => Err("file_write requires (String, String) arguments".into()),
1208 }
1209 }
1210 "file_exists" => {
1211 if args.len() != 1 { return Err("file_exists requires 1 argument".into()); }
1212 match &args[0] {
1213 Value::String(path) => Ok(Some(Value::Bool(std::path::Path::new(path.as_str()).exists()))),
1214 _ => Err(format!("file_exists requires String path, got {}", args[0].type_name())),
1215 }
1216 }
1217 "file_lines" => {
1218 if args.len() != 1 { return Err("file_lines requires 1 argument".into()); }
1219 match &args[0] {
1220 Value::String(path) => {
1221 let content = std::fs::read_to_string(path.as_str())
1222 .map_err(|e| format!("file_lines error: {}", e))?;
1223 let lines: Vec<Value> = content
1224 .lines()
1225 .map(|l| Value::String(Rc::new(l.to_string())))
1226 .collect();
1227 Ok(Some(Value::Array(Rc::new(lines))))
1228 }
1229 _ => Err(format!("file_lines requires String path, got {}", args[0].type_name())),
1230 }
1231 }
1232 "dir_list" => {
1234 if args.len() != 1 { return Err("dir_list requires 1 argument (path)".into()); }
1235 match &args[0] {
1236 Value::String(path) => {
1237 let entries = std::fs::read_dir(path.as_str())
1238 .map_err(|e| format!("dir_list error: {}", e))?;
1239 let mut sorted = std::collections::BTreeSet::new();
1241 for entry in entries {
1242 let entry = entry.map_err(|e| format!("dir_list error: {}", e))?;
1243 let name = entry.file_name().to_string_lossy().to_string();
1244 sorted.insert(name);
1245 }
1246 let values: Vec<Value> = sorted
1247 .into_iter()
1248 .map(|s| Value::String(Rc::new(s)))
1249 .collect();
1250 Ok(Some(Value::Array(Rc::new(values))))
1251 }
1252 _ => Err(format!("dir_list requires String path, got {}", args[0].type_name())),
1253 }
1254 }
1255 "path_join" => {
1256 if args.len() != 2 { return Err("path_join requires 2 arguments (base, segment)".into()); }
1257 match (&args[0], &args[1]) {
1258 (Value::String(a), Value::String(b)) => {
1259 let joined = std::path::Path::new(a.as_str())
1260 .join(b.as_str())
1261 .to_string_lossy()
1262 .to_string();
1263 Ok(Some(Value::String(Rc::new(joined))))
1264 }
1265 _ => Err(format!(
1266 "path_join requires (String, String) arguments, got ({}, {})",
1267 args[0].type_name(), args[1].type_name()
1268 )),
1269 }
1270 }
1271
1272 "window_sum" | "window_mean" | "window_min" | "window_max" => {
1274 if args.len() != 2 {
1275 return Err(format!("{name} requires 2 arguments (array, window_size)"));
1276 }
1277 let data = value_to_f64_vec(&args[0])?;
1278 let ws = match &args[1] {
1279 Value::Int(i) => {
1280 if *i < 0 {
1281 return Err(format!("{name}: window_size must be non-negative, got {i}"));
1282 }
1283 *i as usize
1284 }
1285 _ => return Err(format!("{name} requires Int window_size, got {}", args[1].type_name())),
1286 };
1287 let result = match name {
1288 "window_sum" => crate::window::window_sum(&data, ws),
1289 "window_mean" => crate::window::window_mean(&data, ws),
1290 "window_min" => crate::window::window_min(&data, ws),
1291 "window_max" => crate::window::window_max(&data, ws),
1292 _ => unreachable!(),
1293 };
1294 let values: Vec<Value> = result.into_iter().map(Value::Float).collect();
1295 Ok(Some(Value::Array(Rc::new(values))))
1296 }
1297
1298 "mean" => {
1300 if args.len() != 1 { return Err("mean requires 1 argument".into()); }
1301 let data = value_to_f64_vec(&args[0])?;
1302 if data.is_empty() { return Err("mean: empty data".into()); }
1303 Ok(Some(Value::Float(cjc_repro::kahan_sum_f64(&data) / data.len() as f64)))
1304 }
1305 "variance" => {
1306 if args.len() != 1 { return Err("variance requires 1 argument".into()); }
1307 let data = value_to_f64_vec(&args[0])?;
1308 Ok(Some(Value::Float(crate::stats::variance(&data)?)))
1309 }
1310 "sd" => {
1311 if args.len() != 1 { return Err("sd requires 1 argument".into()); }
1312 let data = value_to_f64_vec(&args[0])?;
1313 Ok(Some(Value::Float(crate::stats::sd(&data)?)))
1314 }
1315 "se" => {
1316 if args.len() != 1 { return Err("se requires 1 argument".into()); }
1317 let data = value_to_f64_vec(&args[0])?;
1318 Ok(Some(Value::Float(crate::stats::se(&data)?)))
1319 }
1320 "median" => {
1321 if args.len() != 1 { return Err("median requires 1 argument".into()); }
1322 let data = value_to_f64_vec(&args[0])?;
1323 Ok(Some(Value::Float(crate::stats::median(&data)?)))
1324 }
1325 "quantile" => {
1326 if args.len() != 2 { return Err("quantile requires 2 arguments".into()); }
1327 let data = value_to_f64_vec(&args[0])?;
1328 let p = match &args[1] {
1329 Value::Float(f) => *f,
1330 Value::Int(i) => *i as f64,
1331 _ => return Err("quantile: p must be a number".into()),
1332 };
1333 Ok(Some(Value::Float(crate::stats::quantile(&data, p)?)))
1334 }
1335 "nth_element" => {
1337 if args.len() != 2 { return Err("nth_element requires 2 arguments: data, k".into()); }
1338 let data = value_to_f64_vec(&args[0])?;
1339 let k = value_to_usize(&args[1])?;
1340 Ok(Some(Value::Float(crate::stats::nth_element_copy(&data, k)?)))
1341 }
1342 "median_fast" => {
1343 if args.len() != 1 { return Err("median_fast requires 1 argument".into()); }
1344 let data = value_to_f64_vec(&args[0])?;
1345 Ok(Some(Value::Float(crate::stats::median_fast(&data)?)))
1346 }
1347 "quantile_fast" => {
1348 if args.len() != 2 { return Err("quantile_fast requires 2 arguments: data, p".into()); }
1349 let data = value_to_f64_vec(&args[0])?;
1350 let p = match &args[1] {
1351 Value::Float(f) => *f,
1352 Value::Int(i) => *i as f64,
1353 _ => return Err("quantile_fast: p must be a number".into()),
1354 };
1355 Ok(Some(Value::Float(crate::stats::quantile_fast(&data, p)?)))
1356 }
1357 "filter_mask" => {
1358 if args.len() != 2 { return Err("filter_mask requires 2 arguments: data, mask".into()); }
1359 let data = value_to_f64_vec(&args[0])?;
1360 let mask: Vec<bool> = match &args[1] {
1361 Value::Array(arr) => arr.iter().map(|v| match v {
1362 Value::Bool(b) => Ok(*b),
1363 Value::Int(i) => Ok(*i != 0),
1364 _ => Err("filter_mask: mask must be array of bools".to_string()),
1365 }).collect::<Result<Vec<_>, _>>()?,
1366 _ => return Err("filter_mask: mask must be an array".into()),
1367 };
1368 let result = crate::stats::filter_mask(&data, &mask)?;
1369 let values: Vec<Value> = result.into_iter().map(Value::Float).collect();
1370 Ok(Some(Value::Array(Rc::new(values))))
1371 }
1372 "erf" => {
1373 if args.len() != 1 { return Err("erf requires 1 argument".into()); }
1374 let x = match &args[0] {
1375 Value::Float(f) => *f,
1376 Value::Int(i) => *i as f64,
1377 _ => return Err("erf requires a number".into()),
1378 };
1379 Ok(Some(Value::Float(crate::distributions::erf(x))))
1380 }
1381 "erfc" => {
1382 if args.len() != 1 { return Err("erfc requires 1 argument".into()); }
1383 let x = match &args[0] {
1384 Value::Float(f) => *f,
1385 Value::Int(i) => *i as f64,
1386 _ => return Err("erfc requires a number".into()),
1387 };
1388 Ok(Some(Value::Float(crate::distributions::erfc(x))))
1389 }
1390 "iqr" => {
1391 if args.len() != 1 { return Err("iqr requires 1 argument".into()); }
1392 let data = value_to_f64_vec(&args[0])?;
1393 Ok(Some(Value::Float(crate::stats::iqr(&data)?)))
1394 }
1395 "skewness" => {
1396 if args.len() != 1 { return Err("skewness requires 1 argument".into()); }
1397 let data = value_to_f64_vec(&args[0])?;
1398 Ok(Some(Value::Float(crate::stats::skewness(&data)?)))
1399 }
1400 "kurtosis" => {
1401 if args.len() != 1 { return Err("kurtosis requires 1 argument".into()); }
1402 let data = value_to_f64_vec(&args[0])?;
1403 Ok(Some(Value::Float(crate::stats::kurtosis(&data)?)))
1404 }
1405 "z_score" => {
1406 if args.len() != 1 { return Err("z_score requires 1 argument".into()); }
1407 let data = value_to_f64_vec(&args[0])?;
1408 let result = crate::stats::z_score(&data)?;
1409 let values: Vec<Value> = result.into_iter().map(Value::Float).collect();
1410 Ok(Some(Value::Array(Rc::new(values))))
1411 }
1412 "standardize" => {
1413 if args.len() != 1 { return Err("standardize requires 1 argument".into()); }
1414 let data = value_to_f64_vec(&args[0])?;
1415 let result = crate::stats::standardize(&data)?;
1416 let values: Vec<Value> = result.into_iter().map(Value::Float).collect();
1417 Ok(Some(Value::Array(Rc::new(values))))
1418 }
1419 "n_distinct" => {
1420 if args.len() != 1 { return Err("n_distinct requires 1 argument".into()); }
1421 let data = value_to_f64_vec(&args[0])?;
1422 Ok(Some(Value::Int(crate::stats::n_distinct(&data) as i64)))
1423 }
1424 "cor" => {
1426 if args.len() != 2 { return Err("cor requires 2 arguments".into()); }
1427 let x = value_to_f64_vec(&args[0])?;
1428 let y = value_to_f64_vec(&args[1])?;
1429 Ok(Some(Value::Float(crate::stats::cor(&x, &y)?)))
1430 }
1431 "cov" => {
1432 if args.len() != 2 { return Err("cov requires 2 arguments".into()); }
1433 let x = value_to_f64_vec(&args[0])?;
1434 let y = value_to_f64_vec(&args[1])?;
1435 Ok(Some(Value::Float(crate::stats::cov(&x, &y)?)))
1436 }
1437 "normal_cdf" => {
1439 if args.len() != 1 { return Err("normal_cdf requires 1 argument".into()); }
1440 let x = match &args[0] {
1441 Value::Float(f) => *f,
1442 Value::Int(i) => *i as f64,
1443 _ => return Err("normal_cdf requires a number".into()),
1444 };
1445 Ok(Some(Value::Float(crate::distributions::normal_cdf(x))))
1446 }
1447 "normal_pdf" => {
1448 if args.len() != 1 { return Err("normal_pdf requires 1 argument".into()); }
1449 let x = match &args[0] {
1450 Value::Float(f) => *f,
1451 Value::Int(i) => *i as f64,
1452 _ => return Err("normal_pdf requires a number".into()),
1453 };
1454 Ok(Some(Value::Float(crate::distributions::normal_pdf(x))))
1455 }
1456 "normal_ppf" => {
1457 if args.len() != 1 { return Err("normal_ppf requires 1 argument".into()); }
1458 let p = match &args[0] {
1459 Value::Float(f) => *f,
1460 Value::Int(i) => *i as f64,
1461 _ => return Err("normal_ppf requires a number".into()),
1462 };
1463 Ok(Some(Value::Float(crate::distributions::normal_ppf(p)?)))
1464 }
1465 "t_cdf" => {
1466 if args.len() != 2 { return Err("t_cdf requires 2 arguments (x, df)".into()); }
1467 let x = match &args[0] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("t_cdf: x must be a number".into()) };
1468 let df = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("t_cdf: df must be a number".into()) };
1469 Ok(Some(Value::Float(crate::distributions::t_cdf(x, df))))
1470 }
1471 "chi2_cdf" => {
1472 if args.len() != 2 { return Err("chi2_cdf requires 2 arguments (x, df)".into()); }
1473 let x = match &args[0] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("chi2_cdf: x must be a number".into()) };
1474 let df = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("chi2_cdf: df must be a number".into()) };
1475 Ok(Some(Value::Float(crate::distributions::chi2_cdf(x, df))))
1476 }
1477 "f_cdf" => {
1478 if args.len() != 3 { return Err("f_cdf requires 3 arguments (x, df1, df2)".into()); }
1479 let x = match &args[0] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("f_cdf: x must be a number".into()) };
1480 let df1 = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("f_cdf: df1 must be a number".into()) };
1481 let df2 = match &args[2] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("f_cdf: df2 must be a number".into()) };
1482 Ok(Some(Value::Float(crate::distributions::f_cdf(x, df1, df2))))
1483 }
1484 "t_test" => {
1486 if args.len() != 2 { return Err("t_test requires 2 arguments (data, mu)".into()); }
1487 let data = value_to_f64_vec(&args[0])?;
1488 let mu = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("t_test: mu must be a number".into()) };
1489 let r = crate::hypothesis::t_test(&data, mu)?;
1490 let mut fields = std::collections::BTreeMap::new();
1491 fields.insert("t_statistic".into(), Value::Float(r.t_statistic));
1492 fields.insert("p_value".into(), Value::Float(r.p_value));
1493 fields.insert("df".into(), Value::Float(r.df));
1494 fields.insert("mean".into(), Value::Float(r.mean));
1495 fields.insert("se".into(), Value::Float(r.se));
1496 Ok(Some(Value::Struct { name: "TTestResult".into(), fields }))
1497 }
1498 "t_test_two_sample" => {
1499 if args.len() != 2 { return Err("t_test_two_sample requires 2 arguments".into()); }
1500 let x = value_to_f64_vec(&args[0])?;
1501 let y = value_to_f64_vec(&args[1])?;
1502 let r = crate::hypothesis::t_test_two_sample(&x, &y)?;
1503 let mut fields = std::collections::BTreeMap::new();
1504 fields.insert("t_statistic".into(), Value::Float(r.t_statistic));
1505 fields.insert("p_value".into(), Value::Float(r.p_value));
1506 fields.insert("df".into(), Value::Float(r.df));
1507 Ok(Some(Value::Struct { name: "TTestResult".into(), fields }))
1508 }
1509 "chi_squared_test" => {
1510 if args.len() != 2 { return Err("chi_squared_test requires 2 arguments".into()); }
1511 let obs = value_to_f64_vec(&args[0])?;
1512 let exp = value_to_f64_vec(&args[1])?;
1513 let r = crate::hypothesis::chi_squared_test(&obs, &exp)?;
1514 let mut fields = std::collections::BTreeMap::new();
1515 fields.insert("chi2".into(), Value::Float(r.chi2));
1516 fields.insert("p_value".into(), Value::Float(r.p_value));
1517 fields.insert("df".into(), Value::Float(r.df));
1518 Ok(Some(Value::Struct { name: "ChiSquaredResult".into(), fields }))
1519 }
1520 "det" => {
1522 if args.len() != 1 { return Err("det requires 1 Tensor argument".into()); }
1523 let t = value_to_tensor(&args[0])?;
1524 Ok(Some(Value::Float(t.det().map_err(|e| format!("{e}"))?)))
1525 }
1526 "solve" => {
1527 if args.len() != 2 { return Err("solve requires 2 Tensor arguments".into()); }
1528 let a = value_to_tensor(&args[0])?;
1529 let b = value_to_tensor(&args[1])?;
1530 Ok(Some(Value::Tensor(a.solve(b).map_err(|e| format!("{e}"))?)))
1531 }
1532 "lstsq" => {
1533 if args.len() != 2 { return Err("lstsq requires 2 Tensor arguments".into()); }
1534 let a = value_to_tensor(&args[0])?;
1535 let b = value_to_tensor(&args[1])?;
1536 Ok(Some(Value::Tensor(a.lstsq(b).map_err(|e| format!("{e}"))?)))
1537 }
1538 "trace" => {
1539 if args.len() != 1 { return Err("trace requires 1 Tensor argument".into()); }
1540 let t = value_to_tensor(&args[0])?;
1541 Ok(Some(Value::Float(t.trace().map_err(|e| format!("{e}"))?)))
1542 }
1543 "norm_frobenius" => {
1544 if args.len() != 1 { return Err("norm_frobenius requires 1 Tensor argument".into()); }
1545 let t = value_to_tensor(&args[0])?;
1546 Ok(Some(Value::Float(t.norm_frobenius().map_err(|e| format!("{e}"))?)))
1547 }
1548 "eigh" => {
1549 if args.len() != 1 { return Err("eigh requires 1 Tensor argument".into()); }
1550 let t = value_to_tensor(&args[0])?;
1551 let (vals, vecs) = t.eigh().map_err(|e| format!("{e}"))?;
1552 let val_values: Vec<Value> = vals.into_iter().map(Value::Float).collect();
1553 Ok(Some(Value::Tuple(Rc::new(vec![
1554 Value::Array(Rc::new(val_values)),
1555 Value::Tensor(vecs),
1556 ]))))
1557 }
1558 "matrix_rank" => {
1559 if args.len() != 1 { return Err("matrix_rank requires 1 Tensor argument".into()); }
1560 let t = value_to_tensor(&args[0])?;
1561 Ok(Some(Value::Int(t.matrix_rank().map_err(|e| format!("{e}"))? as i64)))
1562 }
1563 "kron" => {
1564 if args.len() != 2 { return Err("kron requires 2 Tensor arguments".into()); }
1565 let a = value_to_tensor(&args[0])?;
1566 let b = value_to_tensor(&args[1])?;
1567 Ok(Some(Value::Tensor(a.kron(b).map_err(|e| format!("{e}"))?)))
1568 }
1569 "mse_loss" => {
1571 if args.len() != 2 { return Err("mse_loss requires 2 arguments".into()); }
1572 let pred = value_to_f64_vec(&args[0])?;
1573 let target = value_to_f64_vec(&args[1])?;
1574 Ok(Some(Value::Float(crate::ml::mse_loss(&pred, &target)?)))
1575 }
1576 "cross_entropy_loss" => {
1577 if args.len() != 2 { return Err("cross_entropy_loss requires 2 arguments".into()); }
1578 let pred = value_to_f64_vec(&args[0])?;
1579 let target = value_to_f64_vec(&args[1])?;
1580 Ok(Some(Value::Float(crate::ml::cross_entropy_loss(&pred, &target)?)))
1581 }
1582 "huber_loss" => {
1583 if args.len() != 3 { return Err("huber_loss requires 3 arguments".into()); }
1584 let pred = value_to_f64_vec(&args[0])?;
1585 let target = value_to_f64_vec(&args[1])?;
1586 let delta = match &args[2] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("huber_loss: delta must be a number".into()) };
1587 Ok(Some(Value::Float(crate::ml::huber_loss(&pred, &target, delta)?)))
1588 }
1589 "cumsum" => {
1591 if args.len() != 1 { return Err("cumsum requires 1 argument".into()); }
1592 let data = value_to_f64_vec(&args[0])?;
1593 let result = crate::stats::cumsum(&data);
1594 let values: Vec<Value> = result.into_iter().map(Value::Float).collect();
1595 Ok(Some(Value::Array(Rc::new(values))))
1596 }
1597 "cumprod" => {
1598 if args.len() != 1 { return Err("cumprod requires 1 argument".into()); }
1599 let data = value_to_f64_vec(&args[0])?;
1600 let result = crate::stats::cumprod(&data);
1601 let values: Vec<Value> = result.into_iter().map(Value::Float).collect();
1602 Ok(Some(Value::Array(Rc::new(values))))
1603 }
1604 "cummax" => {
1605 if args.len() != 1 { return Err("cummax requires 1 argument".into()); }
1606 let data = value_to_f64_vec(&args[0])?;
1607 let result = crate::stats::cummax(&data);
1608 let values: Vec<Value> = result.into_iter().map(Value::Float).collect();
1609 Ok(Some(Value::Array(Rc::new(values))))
1610 }
1611 "cummin" => {
1612 if args.len() != 1 { return Err("cummin requires 1 argument".into()); }
1613 let data = value_to_f64_vec(&args[0])?;
1614 let result = crate::stats::cummin(&data);
1615 let values: Vec<Value> = result.into_iter().map(Value::Float).collect();
1616 Ok(Some(Value::Array(Rc::new(values))))
1617 }
1618 "lag" => {
1619 if args.len() != 2 { return Err("lag requires 2 arguments".into()); }
1620 let data = value_to_f64_vec(&args[0])?;
1621 let n = value_to_usize(&args[1])?;
1622 let result = crate::stats::lag(&data, n);
1623 let values: Vec<Value> = result.into_iter().map(Value::Float).collect();
1624 Ok(Some(Value::Array(Rc::new(values))))
1625 }
1626 "lead" => {
1627 if args.len() != 2 { return Err("lead requires 2 arguments".into()); }
1628 let data = value_to_f64_vec(&args[0])?;
1629 let n = value_to_usize(&args[1])?;
1630 let result = crate::stats::lead(&data, n);
1631 let values: Vec<Value> = result.into_iter().map(Value::Float).collect();
1632 Ok(Some(Value::Array(Rc::new(values))))
1633 }
1634 "rank" => {
1635 if args.len() != 1 { return Err("rank requires 1 argument".into()); }
1636 let data = value_to_f64_vec(&args[0])?;
1637 let result = crate::stats::rank(&data);
1638 let values: Vec<Value> = result.into_iter().map(Value::Float).collect();
1639 Ok(Some(Value::Array(Rc::new(values))))
1640 }
1641 "dense_rank" => {
1642 if args.len() != 1 { return Err("dense_rank requires 1 argument".into()); }
1643 let data = value_to_f64_vec(&args[0])?;
1644 let result = crate::stats::dense_rank(&data);
1645 let values: Vec<Value> = result.into_iter().map(Value::Float).collect();
1646 Ok(Some(Value::Array(Rc::new(values))))
1647 }
1648 "histogram" => {
1649 if args.len() != 2 { return Err("histogram requires 2 arguments".into()); }
1650 let data = value_to_f64_vec(&args[0])?;
1651 let n_bins = value_to_usize(&args[1])?;
1652 let (edges, counts) = crate::stats::histogram(&data, n_bins)?;
1653 let edge_values: Vec<Value> = edges.into_iter().map(Value::Float).collect();
1654 let count_values: Vec<Value> = counts.into_iter().map(|c| Value::Int(c as i64)).collect();
1655 Ok(Some(Value::Tuple(Rc::new(vec![
1656 Value::Array(Rc::new(edge_values)),
1657 Value::Array(Rc::new(count_values)),
1658 ]))))
1659 }
1660 "sample_variance" => {
1662 if args.len() != 1 { return Err("sample_variance requires 1 argument".into()); }
1663 let data = value_to_f64_vec(&args[0])?;
1664 Ok(Some(Value::Float(crate::stats::sample_variance(&data)?)))
1665 }
1666 "sample_sd" => {
1667 if args.len() != 1 { return Err("sample_sd requires 1 argument".into()); }
1668 let data = value_to_f64_vec(&args[0])?;
1669 Ok(Some(Value::Float(crate::stats::sample_sd(&data)?)))
1670 }
1671 "sample_cov" => {
1672 if args.len() != 2 { return Err("sample_cov requires 2 arguments".into()); }
1673 let x = value_to_f64_vec(&args[0])?;
1674 let y = value_to_f64_vec(&args[1])?;
1675 Ok(Some(Value::Float(crate::stats::sample_cov(&x, &y)?)))
1676 }
1677 "row_number" => {
1678 if args.len() != 1 { return Err("row_number requires 1 argument".into()); }
1679 let data = value_to_f64_vec(&args[0])?;
1680 let result = crate::stats::row_number(&data);
1681 let values: Vec<Value> = result.into_iter().map(Value::Float).collect();
1682 Ok(Some(Value::Array(Rc::new(values))))
1683 }
1684 "t_ppf" => {
1686 if args.len() != 2 { return Err("t_ppf requires 2 arguments (p, df)".into()); }
1687 let p = match &args[0] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("t_ppf: p must be a number".into()) };
1688 let df = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("t_ppf: df must be a number".into()) };
1689 Ok(Some(Value::Float(crate::distributions::t_ppf(p, df)?)))
1690 }
1691 "chi2_ppf" => {
1692 if args.len() != 2 { return Err("chi2_ppf requires 2 arguments (p, df)".into()); }
1693 let p = match &args[0] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("chi2_ppf: p must be a number".into()) };
1694 let df = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("chi2_ppf: df must be a number".into()) };
1695 Ok(Some(Value::Float(crate::distributions::chi2_ppf(p, df)?)))
1696 }
1697 "f_ppf" => {
1698 if args.len() != 3 { return Err("f_ppf requires 3 arguments (p, df1, df2)".into()); }
1699 let p = match &args[0] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("f_ppf: p must be a number".into()) };
1700 let df1 = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("f_ppf: df1 must be a number".into()) };
1701 let df2 = match &args[2] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("f_ppf: df2 must be a number".into()) };
1702 Ok(Some(Value::Float(crate::distributions::f_ppf(p, df1, df2)?)))
1703 }
1704 "binomial_pmf" => {
1706 if args.len() != 3 { return Err("binomial_pmf requires 3 arguments (k, n, p)".into()); }
1707 let k = value_to_usize(&args[0])? as u64;
1708 let n = value_to_usize(&args[1])? as u64;
1709 let p = match &args[2] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("binomial_pmf: p must be a number".into()) };
1710 Ok(Some(Value::Float(crate::distributions::binomial_pmf(k, n, p))))
1711 }
1712 "binomial_cdf" => {
1713 if args.len() != 3 { return Err("binomial_cdf requires 3 arguments (k, n, p)".into()); }
1714 let k = value_to_usize(&args[0])? as u64;
1715 let n = value_to_usize(&args[1])? as u64;
1716 let p = match &args[2] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("binomial_cdf: p must be a number".into()) };
1717 Ok(Some(Value::Float(crate::distributions::binomial_cdf(k, n, p))))
1718 }
1719 "poisson_pmf" => {
1720 if args.len() != 2 { return Err("poisson_pmf requires 2 arguments (k, lambda)".into()); }
1721 let k = value_to_usize(&args[0])? as u64;
1722 let lambda = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("poisson_pmf: lambda must be a number".into()) };
1723 Ok(Some(Value::Float(crate::distributions::poisson_pmf(k, lambda))))
1724 }
1725 "poisson_cdf" => {
1726 if args.len() != 2 { return Err("poisson_cdf requires 2 arguments (k, lambda)".into()); }
1727 let k = value_to_usize(&args[0])? as u64;
1728 let lambda = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("poisson_cdf: lambda must be a number".into()) };
1729 Ok(Some(Value::Float(crate::distributions::poisson_cdf(k, lambda))))
1730 }
1731 "t_test_paired" => {
1733 if args.len() != 2 { return Err("t_test_paired requires 2 arguments".into()); }
1734 let x = value_to_f64_vec(&args[0])?;
1735 let y = value_to_f64_vec(&args[1])?;
1736 let r = crate::hypothesis::t_test_paired(&x, &y)?;
1737 let mut fields = std::collections::BTreeMap::new();
1738 fields.insert("t_statistic".into(), Value::Float(r.t_statistic));
1739 fields.insert("p_value".into(), Value::Float(r.p_value));
1740 fields.insert("df".into(), Value::Float(r.df));
1741 Ok(Some(Value::Struct { name: "TTestResult".into(), fields }))
1742 }
1743 "anova_oneway" => {
1744 if args.len() < 2 { return Err("anova_oneway requires at least 2 group arguments".into()); }
1745 let mut groups = Vec::new();
1746 let mut group_vecs = Vec::new();
1747 for a in args.iter() {
1748 group_vecs.push(value_to_f64_vec(a)?);
1749 }
1750 for gv in &group_vecs {
1751 groups.push(gv.as_slice());
1752 }
1753 let r = crate::hypothesis::anova_oneway(&groups)?;
1754 let mut fields = std::collections::BTreeMap::new();
1755 fields.insert("f_statistic".into(), Value::Float(r.f_statistic));
1756 fields.insert("p_value".into(), Value::Float(r.p_value));
1757 fields.insert("df_between".into(), Value::Float(r.df_between));
1758 fields.insert("df_within".into(), Value::Float(r.df_within));
1759 fields.insert("ss_between".into(), Value::Float(r.ss_between));
1760 fields.insert("ss_within".into(), Value::Float(r.ss_within));
1761 Ok(Some(Value::Struct { name: "AnovaResult".into(), fields }))
1762 }
1763 "f_test" => {
1764 if args.len() != 2 { return Err("f_test requires 2 arguments".into()); }
1765 let x = value_to_f64_vec(&args[0])?;
1766 let y = value_to_f64_vec(&args[1])?;
1767 let (f_stat, p_val) = crate::hypothesis::f_test(&x, &y)?;
1768 let mut fields = std::collections::BTreeMap::new();
1769 fields.insert("f_statistic".into(), Value::Float(f_stat));
1770 fields.insert("p_value".into(), Value::Float(p_val));
1771 Ok(Some(Value::Struct { name: "FTestResult".into(), fields }))
1772 }
1773 "lm" => {
1774 if args.len() != 4 { return Err("lm requires 4 arguments (X_flat, y, n, p)".into()); }
1776 let x_flat = value_to_f64_vec(&args[0])?;
1777 let y = value_to_f64_vec(&args[1])?;
1778 let n = value_to_usize(&args[2])?;
1779 let p = value_to_usize(&args[3])?;
1780 let r = crate::hypothesis::lm(&x_flat, &y, n, p)?;
1781 let coef_values: Vec<Value> = r.coefficients.into_iter().map(Value::Float).collect();
1782 let se_values: Vec<Value> = r.std_errors.into_iter().map(Value::Float).collect();
1783 let t_values: Vec<Value> = r.t_values.into_iter().map(Value::Float).collect();
1784 let p_values: Vec<Value> = r.p_values.into_iter().map(Value::Float).collect();
1785 let resid_values: Vec<Value> = r.residuals.into_iter().map(Value::Float).collect();
1786 let mut fields = std::collections::BTreeMap::new();
1787 fields.insert("coefficients".into(), Value::Array(Rc::new(coef_values)));
1788 fields.insert("std_errors".into(), Value::Array(Rc::new(se_values)));
1789 fields.insert("t_values".into(), Value::Array(Rc::new(t_values)));
1790 fields.insert("p_values".into(), Value::Array(Rc::new(p_values)));
1791 fields.insert("r_squared".into(), Value::Float(r.r_squared));
1792 fields.insert("adj_r_squared".into(), Value::Float(r.adj_r_squared));
1793 fields.insert("residuals".into(), Value::Array(Rc::new(resid_values)));
1794 fields.insert("f_statistic".into(), Value::Float(r.f_statistic));
1795 Ok(Some(Value::Struct { name: "LmResult".into(), fields }))
1796 }
1797 "binary_cross_entropy" => {
1799 if args.len() != 2 { return Err("binary_cross_entropy requires 2 arguments".into()); }
1800 let pred = value_to_f64_vec(&args[0])?;
1801 let target = value_to_f64_vec(&args[1])?;
1802 Ok(Some(Value::Float(crate::ml::binary_cross_entropy(&pred, &target)?)))
1803 }
1804 "hinge_loss" => {
1805 if args.len() != 2 { return Err("hinge_loss requires 2 arguments".into()); }
1806 let pred = value_to_f64_vec(&args[0])?;
1807 let target = value_to_f64_vec(&args[1])?;
1808 Ok(Some(Value::Float(crate::ml::hinge_loss(&pred, &target)?)))
1809 }
1810 "confusion_matrix" => {
1811 if args.len() != 2 { return Err("confusion_matrix requires 2 arguments".into()); }
1812 let pred = value_to_f64_vec(&args[0])?;
1813 let actual = value_to_f64_vec(&args[1])?;
1814 let pred_bool: Vec<bool> = pred.iter().map(|&x| x > 0.5).collect();
1815 let actual_bool: Vec<bool> = actual.iter().map(|&x| x > 0.5).collect();
1816 let cm = crate::ml::confusion_matrix(&pred_bool, &actual_bool);
1817 let mut fields = std::collections::BTreeMap::new();
1818 fields.insert("tp".into(), Value::Int(cm.tp as i64));
1819 fields.insert("fp".into(), Value::Int(cm.fp as i64));
1820 fields.insert("tn".into(), Value::Int(cm.tn as i64));
1821 fields.insert("fn_count".into(), Value::Int(cm.fn_count as i64));
1822 fields.insert("precision".into(), Value::Float(crate::ml::precision(&cm)));
1823 fields.insert("recall".into(), Value::Float(crate::ml::recall(&cm)));
1824 fields.insert("f1_score".into(), Value::Float(crate::ml::f1_score(&cm)));
1825 fields.insert("accuracy".into(), Value::Float(crate::ml::accuracy(&cm)));
1826 Ok(Some(Value::Struct { name: "ConfusionMatrix".into(), fields }))
1827 }
1828 "auc_roc" => {
1829 if args.len() != 2 { return Err("auc_roc requires 2 arguments (scores, labels)".into()); }
1830 let scores = value_to_f64_vec(&args[0])?;
1831 let labels_f = value_to_f64_vec(&args[1])?;
1832 let labels: Vec<bool> = labels_f.iter().map(|&x| x > 0.5).collect();
1833 Ok(Some(Value::Float(crate::ml::auc_roc(&scores, &labels)?)))
1834 }
1835 "embedding" => {
1837 if args.len() != 2 { return Err("embedding requires 2 arguments (weight, indices)".into()); }
1838 let weight = value_to_tensor(&args[0])?;
1839 let indices: Vec<i64> = match &args[1] {
1840 Value::Array(arr) => arr.iter().map(|v| match v {
1841 Value::Int(i) => Ok(*i),
1842 _ => Err("embedding: indices must be integers".to_string()),
1843 }).collect::<Result<Vec<_>, _>>()?,
1844 Value::Tensor(t) => t.to_vec().iter().map(|f| Ok::<i64, String>(*f as i64)).collect::<Result<Vec<_>, _>>()?,
1845 _ => return Err("embedding: indices must be an array or tensor".into()),
1846 };
1847 let result = crate::ml::embedding(weight, &indices)?;
1848 Ok(Some(Value::Tensor(result)))
1849 }
1850 "batch_indices" => {
1852 if args.len() != 3 { return Err("batch_indices requires 3 arguments (dataset_size, batch_size, seed)".into()); }
1853 let ds = match &args[0] { Value::Int(i) => *i as usize, _ => return Err("batch_indices: dataset_size must be int".into()) };
1854 let bs = match &args[1] { Value::Int(i) => *i as usize, _ => return Err("batch_indices: batch_size must be int".into()) };
1855 let seed = match &args[2] { Value::Int(i) => *i as u64, _ => return Err("batch_indices: seed must be int".into()) };
1856 let result = crate::ml::batch_indices(ds, bs, seed);
1857 let arr: Vec<Value> = result.into_iter().map(|(s, e)| {
1858 Value::Array(Rc::new(vec![Value::Int(s as i64), Value::Int(e as i64)]))
1859 }).collect();
1860 Ok(Some(Value::Array(Rc::new(arr))))
1861 }
1862 "sigmoid" => {
1864 if args.len() != 1 { return Err("sigmoid requires 1 argument".into()); }
1865 let t = value_to_tensor(&args[0])?;
1866 Ok(Some(Value::Tensor(t.sigmoid())))
1867 }
1868 "tanh_activation" => {
1869 if args.len() != 1 { return Err("tanh_activation requires 1 argument".into()); }
1870 let t = value_to_tensor(&args[0])?;
1871 Ok(Some(Value::Tensor(t.tanh_activation())))
1872 }
1873 "leaky_relu" => {
1874 if args.len() != 2 { return Err("leaky_relu requires 2 arguments (tensor, alpha)".into()); }
1875 let t = value_to_tensor(&args[0])?;
1876 let alpha = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("leaky_relu: alpha must be a number".into()) };
1877 Ok(Some(Value::Tensor(t.leaky_relu(alpha))))
1878 }
1879 "silu" => {
1880 if args.len() != 1 { return Err("silu requires 1 argument".into()); }
1881 let t = value_to_tensor(&args[0])?;
1882 Ok(Some(Value::Tensor(t.silu())))
1883 }
1884 "mish" => {
1885 if args.len() != 1 { return Err("mish requires 1 argument".into()); }
1886 let t = value_to_tensor(&args[0])?;
1887 Ok(Some(Value::Tensor(t.mish())))
1888 }
1889 "relu" => {
1891 if args.len() != 1 { return Err("relu requires 1 argument".into()); }
1892 match &args[0] {
1893 Value::Float(f) => Ok(Some(Value::Float(f.max(0.0)))),
1894 Value::Int(i) => Ok(Some(Value::Int((*i).max(0)))),
1895 Value::Tensor(t) => Ok(Some(Value::Tensor(t.relu()))),
1896 _ => Err(format!("relu requires a number or Tensor, got {}", args[0].type_name())),
1897 }
1898 }
1899 "reshape" => {
1901 if args.len() != 2 { return Err("reshape requires 2 arguments (tensor, shape)".into()); }
1902 let t = value_to_tensor(&args[0])?;
1903 let shape = value_to_shape(&args[1])?;
1904 let result = t.reshape(&shape).map_err(|e| format!("{e}"))?;
1905 Ok(Some(Value::Tensor(result)))
1906 }
1907 "tensor_slice" => {
1909 if args.len() != 3 { return Err("tensor_slice requires 3 arguments (tensor, starts, ends)".into()); }
1910 let t = value_to_tensor(&args[0])?;
1911 let starts = value_to_usize_vec(&args[1])?;
1912 let ends = value_to_usize_vec(&args[2])?;
1913 if starts.len() != ends.len() {
1914 return Err("tensor_slice: starts and ends must have same length".into());
1915 }
1916 let ranges: Vec<(usize, usize)> = starts.into_iter().zip(ends).collect();
1917 let result = t.slice(&ranges).map_err(|e| format!("{e}"))?;
1918 Ok(Some(Value::Tensor(result)))
1919 }
1920 "slice" => {
1922 if args.len() != 4 { return Err("slice requires 4 arguments (tensor, dim, start, end)".into()); }
1923 let t = value_to_tensor(&args[0])?;
1924 let dim = match &args[1] { Value::Int(i) => *i as usize, _ => return Err("slice: dim must be an integer".into()) };
1925 let start = match &args[2] { Value::Int(i) => *i as usize, _ => return Err("slice: start must be an integer".into()) };
1926 let end = match &args[3] { Value::Int(i) => *i as usize, _ => return Err("slice: end must be an integer".into()) };
1927 if dim >= t.ndim() {
1928 return Err(format!("slice: dim {} out of bounds for tensor with {} dimensions", dim, t.ndim()));
1929 }
1930 let mut ranges: Vec<(usize, usize)> = t.shape().iter().map(|&s| (0, s)).collect();
1931 ranges[dim] = (start, end);
1932 let result = t.slice(&ranges).map_err(|e| format!("{e}"))?;
1933 Ok(Some(Value::Tensor(result)))
1934 }
1935 "argmax" => {
1936 if args.len() != 1 { return Err("argmax requires 1 argument".into()); }
1937 let t = value_to_tensor(&args[0])?;
1938 Ok(Some(Value::Int(t.argmax() as i64)))
1939 }
1940 "argmin" => {
1941 if args.len() != 1 { return Err("argmin requires 1 argument".into()); }
1942 let t = value_to_tensor(&args[0])?;
1943 Ok(Some(Value::Int(t.argmin() as i64)))
1944 }
1945 "clamp" => {
1946 if args.len() != 3 { return Err("clamp requires 3 arguments (tensor, min, max)".into()); }
1947 let t = value_to_tensor(&args[0])?;
1948 let min_v = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("clamp: min must be a number".into()) };
1949 let max_v = match &args[2] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("clamp: max must be a number".into()) };
1950 Ok(Some(Value::Tensor(t.clamp(min_v, max_v))))
1951 }
1952 "one_hot" => {
1953 if args.len() != 2 { return Err("one_hot requires 2 arguments (indices, depth)".into()); }
1954 let indices = value_to_usize_vec(&args[0])?;
1955 let depth = value_to_usize(&args[1])?;
1956 Ok(Some(Value::Tensor(Tensor::one_hot(&indices, depth).map_err(|e| format!("{e}"))?)))
1957 }
1958 "rfft" => {
1960 if args.len() != 1 { return Err("rfft requires 1 argument".into()); }
1961 let data = value_to_f64_vec(&args[0])?;
1962 let result = crate::fft::rfft(&data);
1963 let pairs: Vec<Value> = result.iter().map(|&(re, im)| {
1964 Value::Tuple(Rc::new(vec![Value::Float(re), Value::Float(im)]))
1965 }).collect();
1966 Ok(Some(Value::Array(Rc::new(pairs))))
1967 }
1968 "psd" => {
1969 if args.len() != 1 { return Err("psd requires 1 argument".into()); }
1970 let data = value_to_f64_vec(&args[0])?;
1971 let result = crate::fft::psd(&data);
1972 let values: Vec<Value> = result.into_iter().map(Value::Float).collect();
1973 Ok(Some(Value::Array(Rc::new(values))))
1974 }
1975
1976 "weighted_mean" => {
1978 if args.len() != 2 { return Err("weighted_mean requires 2 arguments".into()); }
1979 let data = value_to_f64_vec(&args[0])?;
1980 let weights = value_to_f64_vec(&args[1])?;
1981 Ok(Some(Value::Float(crate::stats::weighted_mean(&data, &weights)?)))
1982 }
1983 "weighted_var" => {
1984 if args.len() != 2 { return Err("weighted_var requires 2 arguments".into()); }
1985 let data = value_to_f64_vec(&args[0])?;
1986 let weights = value_to_f64_vec(&args[1])?;
1987 Ok(Some(Value::Float(crate::stats::weighted_var(&data, &weights)?)))
1988 }
1989 "trimmed_mean" => {
1990 if args.len() != 2 { return Err("trimmed_mean requires 2 arguments".into()); }
1991 let data = value_to_f64_vec(&args[0])?;
1992 let prop = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("trimmed_mean: proportion must be a number".into()) };
1993 Ok(Some(Value::Float(crate::stats::trimmed_mean(&data, prop)?)))
1994 }
1995 "winsorize" => {
1996 if args.len() != 2 { return Err("winsorize requires 2 arguments".into()); }
1997 let data = value_to_f64_vec(&args[0])?;
1998 let prop = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("winsorize: proportion must be a number".into()) };
1999 let result = crate::stats::winsorize(&data, prop)?;
2000 let values: Vec<Value> = result.into_iter().map(Value::Float).collect();
2001 Ok(Some(Value::Array(Rc::new(values))))
2002 }
2003 "mad" => {
2004 if args.len() != 1 { return Err("mad requires 1 argument".into()); }
2005 let data = value_to_f64_vec(&args[0])?;
2006 Ok(Some(Value::Float(crate::stats::mad(&data)?)))
2007 }
2008 "mode" => {
2009 if args.len() != 1 { return Err("mode requires 1 argument".into()); }
2010 let data = value_to_f64_vec(&args[0])?;
2011 Ok(Some(Value::Float(crate::stats::mode(&data)?)))
2012 }
2013 "percentile_rank" => {
2014 if args.len() != 2 { return Err("percentile_rank requires 2 arguments".into()); }
2015 let data = value_to_f64_vec(&args[0])?;
2016 let value = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("percentile_rank: value must be a number".into()) };
2017 Ok(Some(Value::Float(crate::stats::percentile_rank(&data, value)?)))
2018 }
2019
2020 "cat" => {
2022 if args.len() != 2 { return Err("cat requires 2 arguments (array of tensors, axis)".into()); }
2023 let tensors_arr = match &args[0] {
2024 Value::Array(arr) => arr.iter().map(|v| match v {
2025 Value::Tensor(t) => Ok(t),
2026 _ => Err("cat: first argument must be array of tensors".to_string()),
2027 }).collect::<Result<Vec<&Tensor>, String>>()?,
2028 _ => return Err("cat: first argument must be array of tensors".into()),
2029 };
2030 let axis = value_to_usize(&args[1])?;
2031 let refs: Vec<&Tensor> = tensors_arr;
2032 Ok(Some(Value::Tensor(Tensor::cat(&refs, axis).map_err(|e| format!("{e}"))?)))
2033 }
2034 "stack" => {
2035 if args.len() != 2 { return Err("stack requires 2 arguments (array of tensors, axis)".into()); }
2036 let tensors_arr = match &args[0] {
2037 Value::Array(arr) => arr.iter().map(|v| match v {
2038 Value::Tensor(t) => Ok(t),
2039 _ => Err("stack: first argument must be array of tensors".to_string()),
2040 }).collect::<Result<Vec<&Tensor>, String>>()?,
2041 _ => return Err("stack: first argument must be array of tensors".into()),
2042 };
2043 let axis = value_to_usize(&args[1])?;
2044 Ok(Some(Value::Tensor(Tensor::stack(&tensors_arr, axis).map_err(|e| format!("{e}"))?)))
2045 }
2046 "topk" => {
2047 if args.len() != 2 { return Err("topk requires 2 arguments (tensor, k)".into()); }
2048 let t = value_to_tensor(&args[0])?;
2049 let k = value_to_usize(&args[1])?;
2050 let (vals, idxs) = t.topk(k).map_err(|e| format!("{e}"))?;
2051 let idx_values: Vec<Value> = idxs.into_iter().map(|i| Value::Int(i as i64)).collect();
2052 Ok(Some(Value::Tuple(Rc::new(vec![Value::Tensor(vals), Value::Array(Rc::new(idx_values))]))))
2053 }
2054 "batch_norm" => {
2055 if args.len() != 6 { return Err("batch_norm requires 6 arguments".into()); }
2056 let x = value_to_f64_vec(&args[0])?;
2057 let mean = value_to_f64_vec(&args[1])?;
2058 let var = value_to_f64_vec(&args[2])?;
2059 let gamma = value_to_f64_vec(&args[3])?;
2060 let beta = value_to_f64_vec(&args[4])?;
2061 let eps = match &args[5] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("batch_norm: eps must be a number".into()) };
2062 let result = crate::ml::batch_norm(&x, &mean, &var, &gamma, &beta, eps)?;
2063 let values: Vec<Value> = result.into_iter().map(Value::Float).collect();
2064 Ok(Some(Value::Array(Rc::new(values))))
2065 }
2066 "dropout_mask" => {
2067 if args.len() != 3 { return Err("dropout_mask requires 3 arguments (n, prob, seed)".into()); }
2068 let n = value_to_usize(&args[0])?;
2069 let prob = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("dropout_mask: prob must be a number".into()) };
2070 let seed = match &args[2] { Value::Int(i) => *i as u64, _ => return Err("dropout_mask: seed must be an integer".into()) };
2071 let mask = crate::ml::dropout_mask(n, prob, seed);
2072 let values: Vec<Value> = mask.into_iter().map(Value::Float).collect();
2073 Ok(Some(Value::Array(Rc::new(values))))
2074 }
2075 "lr_step_decay" => {
2076 if args.len() != 4 { return Err("lr_step_decay requires 4 arguments".into()); }
2077 let lr = match &args[0] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("lr_step_decay: lr must be a number".into()) };
2078 let rate = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("lr_step_decay: rate must be a number".into()) };
2079 let epoch = value_to_usize(&args[2])?;
2080 let step = value_to_usize(&args[3])?;
2081 Ok(Some(Value::Float(crate::ml::lr_step_decay(lr, rate, epoch, step))))
2082 }
2083 "lr_cosine" => {
2084 if args.len() != 4 { return Err("lr_cosine requires 4 arguments".into()); }
2085 let max_lr = match &args[0] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("lr_cosine: max_lr must be a number".into()) };
2086 let min_lr = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("lr_cosine: min_lr must be a number".into()) };
2087 let epoch = value_to_usize(&args[2])?;
2088 let total = value_to_usize(&args[3])?;
2089 Ok(Some(Value::Float(crate::ml::lr_cosine(max_lr, min_lr, epoch, total))))
2090 }
2091 "lr_linear_warmup" => {
2092 if args.len() != 3 { return Err("lr_linear_warmup requires 3 arguments".into()); }
2093 let lr = match &args[0] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("lr_linear_warmup: lr must be a number".into()) };
2094 let epoch = value_to_usize(&args[1])?;
2095 let warmup = value_to_usize(&args[2])?;
2096 Ok(Some(Value::Float(crate::ml::lr_linear_warmup(lr, epoch, warmup))))
2097 }
2098 "l1_penalty" => {
2099 if args.len() != 2 { return Err("l1_penalty requires 2 arguments".into()); }
2100 let params = value_to_f64_vec(&args[0])?;
2101 let lambda = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("l1_penalty: lambda must be a number".into()) };
2102 Ok(Some(Value::Float(crate::ml::l1_penalty(¶ms, lambda))))
2103 }
2104 "l2_penalty" => {
2105 if args.len() != 2 { return Err("l2_penalty requires 2 arguments".into()); }
2106 let params = value_to_f64_vec(&args[0])?;
2107 let lambda = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("l2_penalty: lambda must be a number".into()) };
2108 Ok(Some(Value::Float(crate::ml::l2_penalty(¶ms, lambda))))
2109 }
2110
2111 "cond" => {
2113 if args.len() != 1 { return Err("cond requires 1 Tensor argument".into()); }
2114 let t = value_to_tensor(&args[0])?;
2115 Ok(Some(Value::Float(t.cond().map_err(|e| format!("{e}"))?)))
2116 }
2117 "norm_1" => {
2118 if args.len() != 1 { return Err("norm_1 requires 1 Tensor argument".into()); }
2119 let t = value_to_tensor(&args[0])?;
2120 Ok(Some(Value::Float(t.norm_1().map_err(|e| format!("{e}"))?)))
2121 }
2122 "norm_inf" => {
2123 if args.len() != 1 { return Err("norm_inf requires 1 Tensor argument".into()); }
2124 let t = value_to_tensor(&args[0])?;
2125 Ok(Some(Value::Float(t.norm_inf().map_err(|e| format!("{e}"))?)))
2126 }
2127 "schur" => {
2128 if args.len() != 1 { return Err("schur requires 1 Tensor argument".into()); }
2129 let t = value_to_tensor(&args[0])?;
2130 let (q, t_mat) = t.schur().map_err(|e| format!("{e}"))?;
2131 Ok(Some(Value::Tuple(Rc::new(vec![Value::Tensor(q), Value::Tensor(t_mat)]))))
2132 }
2133 "matrix_exp" => {
2134 if args.len() != 1 { return Err("matrix_exp requires 1 Tensor argument".into()); }
2135 let t = value_to_tensor(&args[0])?;
2136 Ok(Some(Value::Tensor(t.matrix_exp().map_err(|e| format!("{e}"))?)))
2137 }
2138
2139 "spearman_cor" => {
2141 if args.len() != 2 { return Err("spearman_cor requires 2 arguments".into()); }
2142 let x = value_to_f64_vec(&args[0])?;
2143 let y = value_to_f64_vec(&args[1])?;
2144 Ok(Some(Value::Float(crate::stats::spearman_cor(&x, &y)?)))
2145 }
2146 "kendall_cor" => {
2147 if args.len() != 2 { return Err("kendall_cor requires 2 arguments".into()); }
2148 let x = value_to_f64_vec(&args[0])?;
2149 let y = value_to_f64_vec(&args[1])?;
2150 Ok(Some(Value::Float(crate::stats::kendall_cor(&x, &y)?)))
2151 }
2152 "partial_cor" => {
2153 if args.len() != 3 { return Err("partial_cor requires 3 arguments".into()); }
2154 let x = value_to_f64_vec(&args[0])?;
2155 let y = value_to_f64_vec(&args[1])?;
2156 let z = value_to_f64_vec(&args[2])?;
2157 Ok(Some(Value::Float(crate::stats::partial_cor(&x, &y, &z)?)))
2158 }
2159 "cor_ci" => {
2160 if args.len() != 3 { return Err("cor_ci requires 3 arguments".into()); }
2161 let x = value_to_f64_vec(&args[0])?;
2162 let y = value_to_f64_vec(&args[1])?;
2163 let alpha = match &args[2] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("cor_ci: alpha must be a number".into()) };
2164 let (lo, hi) = crate::stats::cor_ci(&x, &y, alpha)?;
2165 Ok(Some(Value::Tuple(Rc::new(vec![Value::Float(lo), Value::Float(hi)]))))
2166 }
2167
2168 "hann" => {
2170 if args.len() != 1 { return Err("hann requires 1 argument (n)".into()); }
2171 let n = match &args[0] { Value::Int(i) => *i as usize, _ => return Err("hann: n must be an integer".into()) };
2172 let w = crate::fft::hann_window(n);
2173 Ok(Some(Value::Array(Rc::new(w.into_iter().map(Value::Float).collect()))))
2174 }
2175 "hamming" => {
2176 if args.len() != 1 { return Err("hamming requires 1 argument (n)".into()); }
2177 let n = match &args[0] { Value::Int(i) => *i as usize, _ => return Err("hamming: n must be an integer".into()) };
2178 let w = crate::fft::hamming_window(n);
2179 Ok(Some(Value::Array(Rc::new(w.into_iter().map(Value::Float).collect()))))
2180 }
2181 "blackman" => {
2182 if args.len() != 1 { return Err("blackman requires 1 argument (n)".into()); }
2183 let n = match &args[0] { Value::Int(i) => *i as usize, _ => return Err("blackman: n must be an integer".into()) };
2184 let w = crate::fft::blackman_window(n);
2185 Ok(Some(Value::Array(Rc::new(w.into_iter().map(Value::Float).collect()))))
2186 }
2187 "fft_arbitrary" => {
2188 if args.len() != 1 { return Err("fft_arbitrary requires 1 argument (complex array)".into()); }
2189 let data = value_to_complex_vec(&args[0])?;
2190 let result = crate::fft::fft_arbitrary(&data);
2191 let pairs: Vec<Value> = result.iter().map(|&(re, im)| {
2192 Value::Tuple(Rc::new(vec![Value::Float(re), Value::Float(im)]))
2193 }).collect();
2194 Ok(Some(Value::Array(Rc::new(pairs))))
2195 }
2196 "fft_2d" => {
2197 if args.len() != 3 { return Err("fft_2d requires 3 arguments (data, rows, cols)".into()); }
2198 let data = value_to_complex_vec(&args[0])?;
2199 let rows = match &args[1] { Value::Int(i) => *i as usize, _ => return Err("fft_2d: rows must be an integer".into()) };
2200 let cols = match &args[2] { Value::Int(i) => *i as usize, _ => return Err("fft_2d: cols must be an integer".into()) };
2201 let result = crate::fft::fft_2d(&data, rows, cols)?;
2202 let pairs: Vec<Value> = result.iter().map(|&(re, im)| {
2203 Value::Tuple(Rc::new(vec![Value::Float(re), Value::Float(im)]))
2204 }).collect();
2205 Ok(Some(Value::Array(Rc::new(pairs))))
2206 }
2207 "ifft_2d" => {
2208 if args.len() != 3 { return Err("ifft_2d requires 3 arguments (data, rows, cols)".into()); }
2209 let data = value_to_complex_vec(&args[0])?;
2210 let rows = match &args[1] { Value::Int(i) => *i as usize, _ => return Err("ifft_2d: rows must be an integer".into()) };
2211 let cols = match &args[2] { Value::Int(i) => *i as usize, _ => return Err("ifft_2d: cols must be an integer".into()) };
2212 let result = crate::fft::ifft_2d(&data, rows, cols)?;
2213 let pairs: Vec<Value> = result.iter().map(|&(re, im)| {
2214 Value::Tuple(Rc::new(vec![Value::Float(re), Value::Float(im)]))
2215 }).collect();
2216 Ok(Some(Value::Array(Rc::new(pairs))))
2217 }
2218 "beta_pdf" => {
2219 if args.len() != 3 { return Err("beta_pdf requires 3 arguments (x, a, b)".into()); }
2220 let x = match &args[0] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("beta_pdf: x must be a number".into()) };
2221 let a = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("beta_pdf: a must be a number".into()) };
2222 let b = match &args[2] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("beta_pdf: b must be a number".into()) };
2223 Ok(Some(Value::Float(crate::distributions::beta_pdf(x, a, b))))
2224 }
2225 "beta_cdf" => {
2226 if args.len() != 3 { return Err("beta_cdf requires 3 arguments (x, a, b)".into()); }
2227 let x = match &args[0] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("beta_cdf: x must be a number".into()) };
2228 let a = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("beta_cdf: a must be a number".into()) };
2229 let b = match &args[2] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("beta_cdf: b must be a number".into()) };
2230 Ok(Some(Value::Float(crate::distributions::beta_cdf(x, a, b))))
2231 }
2232 "gamma_pdf" => {
2233 if args.len() != 3 { return Err("gamma_pdf requires 3 arguments (x, k, theta)".into()); }
2234 let x = match &args[0] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("gamma_pdf: x must be a number".into()) };
2235 let k = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("gamma_pdf: k must be a number".into()) };
2236 let theta = match &args[2] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("gamma_pdf: theta must be a number".into()) };
2237 Ok(Some(Value::Float(crate::distributions::gamma_pdf(x, k, theta))))
2238 }
2239 "gamma_cdf" => {
2240 if args.len() != 3 { return Err("gamma_cdf requires 3 arguments (x, k, theta)".into()); }
2241 let x = match &args[0] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("gamma_cdf: x must be a number".into()) };
2242 let k = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("gamma_cdf: k must be a number".into()) };
2243 let theta = match &args[2] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("gamma_cdf: theta must be a number".into()) };
2244 Ok(Some(Value::Float(crate::distributions::gamma_cdf(x, k, theta))))
2245 }
2246 "exp_pdf" => {
2247 if args.len() != 2 { return Err("exp_pdf requires 2 arguments (x, lambda)".into()); }
2248 let x = match &args[0] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("exp_pdf: x must be a number".into()) };
2249 let lambda = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("exp_pdf: lambda must be a number".into()) };
2250 Ok(Some(Value::Float(crate::distributions::exp_pdf(x, lambda))))
2251 }
2252 "exp_cdf" => {
2253 if args.len() != 2 { return Err("exp_cdf requires 2 arguments (x, lambda)".into()); }
2254 let x = match &args[0] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("exp_cdf: x must be a number".into()) };
2255 let lambda = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("exp_cdf: lambda must be a number".into()) };
2256 Ok(Some(Value::Float(crate::distributions::exp_cdf(x, lambda))))
2257 }
2258 "weibull_pdf" => {
2259 if args.len() != 3 { return Err("weibull_pdf requires 3 arguments (x, k, lambda)".into()); }
2260 let x = match &args[0] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("weibull_pdf: x must be a number".into()) };
2261 let k = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("weibull_pdf: k must be a number".into()) };
2262 let lambda = match &args[2] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("weibull_pdf: lambda must be a number".into()) };
2263 Ok(Some(Value::Float(crate::distributions::weibull_pdf(x, k, lambda))))
2264 }
2265 "weibull_cdf" => {
2266 if args.len() != 3 { return Err("weibull_cdf requires 3 arguments (x, k, lambda)".into()); }
2267 let x = match &args[0] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("weibull_cdf: x must be a number".into()) };
2268 let k = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("weibull_cdf: k must be a number".into()) };
2269 let lambda = match &args[2] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("weibull_cdf: lambda must be a number".into()) };
2270 Ok(Some(Value::Float(crate::distributions::weibull_cdf(x, k, lambda))))
2271 }
2272
2273 "case_when" => {
2275 if args.len() != 3 { return Err("case_when requires 3 arguments (conditions, values, default)".into()); }
2276 let conditions = match &args[0] {
2277 Value::Array(arr) => arr.iter().map(|v| match v {
2278 Value::Bool(b) => Ok(*b),
2279 _ => Err("case_when conditions must be booleans".into()),
2280 }).collect::<Result<Vec<bool>, String>>()?,
2281 _ => return Err("case_when conditions must be an array".into()),
2282 };
2283 let values = match &args[1] {
2284 Value::Array(arr) => arr.as_ref().clone(),
2285 _ => return Err("case_when values must be an array".into()),
2286 };
2287 if conditions.len() != values.len() {
2288 return Err("case_when conditions and values must have same length".into());
2289 }
2290 for (i, &cond) in conditions.iter().enumerate() {
2291 if cond { return Ok(Some(values[i].clone())); }
2292 }
2293 Ok(Some(args[2].clone())) }
2295 "ntile" => {
2296 if args.len() != 2 { return Err("ntile requires 2 arguments (data, n)".into()); }
2297 let data = value_to_f64_vec(&args[0])?;
2298 let n = match &args[1] { Value::Int(i) => *i as usize, _ => return Err("ntile: n must be an integer".into()) };
2299 let result = crate::stats::ntile(&data, n)?;
2300 Ok(Some(Value::Array(Rc::new(result.into_iter().map(Value::Float).collect()))))
2301 }
2302 "percent_rank" => {
2303 if args.len() != 1 { return Err("percent_rank requires 1 argument".into()); }
2304 let data = value_to_f64_vec(&args[0])?;
2305 let result = crate::stats::percent_rank_fn(&data)?;
2306 Ok(Some(Value::Array(Rc::new(result.into_iter().map(Value::Float).collect()))))
2307 }
2308 "cume_dist" => {
2309 if args.len() != 1 { return Err("cume_dist requires 1 argument".into()); }
2310 let data = value_to_f64_vec(&args[0])?;
2311 let result = crate::stats::cume_dist(&data)?;
2312 Ok(Some(Value::Array(Rc::new(result.into_iter().map(Value::Float).collect()))))
2313 }
2314 "wls" => {
2315 if args.len() != 5 { return Err("wls requires 5 arguments (X, y, weights, n, p)".into()); }
2316 let x = value_to_f64_vec(&args[0])?;
2317 let y = value_to_f64_vec(&args[1])?;
2318 let w = value_to_f64_vec(&args[2])?;
2319 let n = match &args[3] { Value::Int(i) => *i as usize, _ => return Err("wls: n must be an integer".into()) };
2320 let p = match &args[4] { Value::Int(i) => *i as usize, _ => return Err("wls: p must be an integer".into()) };
2321 let r = crate::hypothesis::wls(&x, &y, &w, n, p)?;
2322 let fields = std::collections::BTreeMap::from([
2323 ("coefficients".to_string(), Value::Array(Rc::new(r.coefficients.into_iter().map(Value::Float).collect()))),
2324 ("r_squared".to_string(), Value::Float(r.r_squared)),
2325 ("residuals".to_string(), Value::Array(Rc::new(r.residuals.into_iter().map(Value::Float).collect()))),
2326 ]);
2327 Ok(Some(Value::Struct { name: "LmResult".to_string(), fields }))
2328 }
2329
2330 "tukey_hsd" => {
2332 let groups: Vec<Vec<f64>> = args.iter()
2333 .map(|a| value_to_f64_vec(a))
2334 .collect::<Result<Vec<_>, _>>()?;
2335 let group_refs: Vec<&[f64]> = groups.iter().map(|g| g.as_slice()).collect();
2336 let results = crate::hypothesis::tukey_hsd(&group_refs)?;
2337 let result_values: Vec<Value> = results.iter().map(|pair| {
2338 let mut fields = std::collections::BTreeMap::new();
2339 fields.insert("group_i".into(), Value::Int(pair.group_i as i64));
2340 fields.insert("group_j".into(), Value::Int(pair.group_j as i64));
2341 fields.insert("mean_diff".into(), Value::Float(pair.mean_diff));
2342 fields.insert("q_statistic".into(), Value::Float(pair.q_statistic));
2343 fields.insert("p_value".into(), Value::Float(pair.p_value));
2344 Value::Struct { name: "TukeyHsdPair".into(), fields }
2345 }).collect();
2346 Ok(Some(Value::Array(Rc::new(result_values))))
2347 }
2348 "mann_whitney" => {
2349 if args.len() != 2 { return Err("mann_whitney requires 2 arguments".into()); }
2350 let x = value_to_f64_vec(&args[0])?;
2351 let y = value_to_f64_vec(&args[1])?;
2352 let r = crate::hypothesis::mann_whitney(&x, &y)?;
2353 let mut fields = std::collections::BTreeMap::new();
2354 fields.insert("u_statistic".into(), Value::Float(r.u_statistic));
2355 fields.insert("z_score".into(), Value::Float(r.z_score));
2356 fields.insert("p_value".into(), Value::Float(r.p_value));
2357 Ok(Some(Value::Struct { name: "MannWhitneyResult".into(), fields }))
2358 }
2359 "kruskal_wallis" => {
2360 let groups: Vec<Vec<f64>> = args.iter()
2361 .map(|a| value_to_f64_vec(a))
2362 .collect::<Result<Vec<_>, _>>()?;
2363 let group_refs: Vec<&[f64]> = groups.iter().map(|g| g.as_slice()).collect();
2364 let r = crate::hypothesis::kruskal_wallis(&group_refs)?;
2365 let mut fields = std::collections::BTreeMap::new();
2366 fields.insert("h_statistic".into(), Value::Float(r.h_statistic));
2367 fields.insert("p_value".into(), Value::Float(r.p_value));
2368 fields.insert("df".into(), Value::Float(r.df));
2369 Ok(Some(Value::Struct { name: "KruskalWallisResult".into(), fields }))
2370 }
2371 "wilcoxon_signed_rank" => {
2372 if args.len() != 2 { return Err("wilcoxon_signed_rank requires 2 arguments".into()); }
2373 let x = value_to_f64_vec(&args[0])?;
2374 let y = value_to_f64_vec(&args[1])?;
2375 let r = crate::hypothesis::wilcoxon_signed_rank(&x, &y)?;
2376 let mut fields = std::collections::BTreeMap::new();
2377 fields.insert("w_statistic".into(), Value::Float(r.w_statistic));
2378 fields.insert("z_score".into(), Value::Float(r.z_score));
2379 fields.insert("p_value".into(), Value::Float(r.p_value));
2380 Ok(Some(Value::Struct { name: "WilcoxonResult".into(), fields }))
2381 }
2382 "bonferroni" => {
2383 if args.len() != 1 { return Err("bonferroni requires 1 argument (p_values array)".into()); }
2384 let pvals = value_to_f64_vec(&args[0])?;
2385 let adj = crate::hypothesis::bonferroni(&pvals);
2386 Ok(Some(Value::Array(Rc::new(adj.into_iter().map(Value::Float).collect()))))
2387 }
2388 "fdr_bh" => {
2389 if args.len() != 1 { return Err("fdr_bh requires 1 argument (p_values array)".into()); }
2390 let pvals = value_to_f64_vec(&args[0])?;
2391 let adj = crate::hypothesis::fdr_bh(&pvals);
2392 Ok(Some(Value::Array(Rc::new(adj.into_iter().map(Value::Float).collect()))))
2393 }
2394 "logistic_regression" => {
2395 if args.len() != 4 { return Err("logistic_regression requires 4 arguments (X, y, n, p)".into()); }
2396 let x = value_to_f64_vec(&args[0])?;
2397 let y = value_to_f64_vec(&args[1])?;
2398 let n = match &args[2] { Value::Int(i) => *i as usize, _ => return Err("logistic_regression: n must be an integer".into()) };
2399 let p = match &args[3] { Value::Int(i) => *i as usize, _ => return Err("logistic_regression: p must be an integer".into()) };
2400 let r = crate::hypothesis::logistic_regression(&x, &y, n, p)?;
2401 let mut fields = std::collections::BTreeMap::new();
2402 fields.insert("coefficients".into(), Value::Array(Rc::new(r.coefficients.into_iter().map(Value::Float).collect())));
2403 fields.insert("std_errors".into(), Value::Array(Rc::new(r.std_errors.into_iter().map(Value::Float).collect())));
2404 fields.insert("z_values".into(), Value::Array(Rc::new(r.z_values.into_iter().map(Value::Float).collect())));
2405 fields.insert("p_values".into(), Value::Array(Rc::new(r.p_values.into_iter().map(Value::Float).collect())));
2406 fields.insert("log_likelihood".into(), Value::Float(r.log_likelihood));
2407 fields.insert("aic".into(), Value::Float(r.aic));
2408 fields.insert("iterations".into(), Value::Int(r.iterations as i64));
2409 Ok(Some(Value::Struct { name: "LogisticResult".into(), fields }))
2410 }
2411
2412 "adf_test" => {
2414 if args.len() != 1 { return Err("adf_test requires 1 argument: data".into()); }
2415 let data = value_to_f64_vec(&args[0])?;
2416 let (t_stat, p_val) = crate::stationarity::adf_test(&data)?;
2417 let mut fields = std::collections::BTreeMap::new();
2418 fields.insert("statistic".into(), Value::Float(t_stat));
2419 fields.insert("p_value".into(), Value::Float(p_val));
2420 Ok(Some(Value::Struct { name: "AdfResult".into(), fields }))
2421 }
2422 "kpss_test" => {
2423 if args.len() != 1 { return Err("kpss_test requires 1 argument: data".into()); }
2424 let data = value_to_f64_vec(&args[0])?;
2425 let (stat, p_val) = crate::stationarity::kpss_test(&data)?;
2426 let mut fields = std::collections::BTreeMap::new();
2427 fields.insert("statistic".into(), Value::Float(stat));
2428 fields.insert("p_value".into(), Value::Float(p_val));
2429 Ok(Some(Value::Struct { name: "KpssResult".into(), fields }))
2430 }
2431 "pp_test" => {
2432 if args.len() != 1 { return Err("pp_test requires 1 argument: data".into()); }
2433 let data = value_to_f64_vec(&args[0])?;
2434 let (z_t, p_val) = crate::stationarity::pp_test(&data)?;
2435 let mut fields = std::collections::BTreeMap::new();
2436 fields.insert("statistic".into(), Value::Float(z_t));
2437 fields.insert("p_value".into(), Value::Float(p_val));
2438 Ok(Some(Value::Struct { name: "PpResult".into(), fields }))
2439 }
2440
2441 "argsort" => {
2443 if args.len() != 1 { return Err("argsort requires 1 arg: Tensor".into()); }
2444 let t = value_to_tensor(&args[0])?;
2445 Ok(Some(Value::Tensor(t.argsort())))
2446 }
2447 "gather" => {
2448 if args.len() != 3 { return Err("gather requires 3 args: tensor, dim, indices".into()); }
2449 let t = value_to_tensor(&args[0])?;
2450 let dim = value_to_usize(&args[1])?;
2451 let indices = value_to_tensor(&args[2])?;
2452 Ok(Some(Value::Tensor(t.gather(dim, &indices).map_err(|e| format!("{e}"))?)))
2453 }
2454 "scatter" => {
2455 if args.len() != 4 { return Err("scatter requires 4 args: tensor, dim, indices, src".into()); }
2456 let t = value_to_tensor(&args[0])?;
2457 let dim = value_to_usize(&args[1])?;
2458 let indices = value_to_tensor(&args[2])?;
2459 let src = value_to_tensor(&args[3])?;
2460 Ok(Some(Value::Tensor(t.scatter(dim, &indices, &src).map_err(|e| format!("{e}"))?)))
2461 }
2462 "index_select" => {
2463 if args.len() != 3 { return Err("index_select requires 3 args: tensor, dim, indices".into()); }
2464 let t = value_to_tensor(&args[0])?;
2465 let dim = value_to_usize(&args[1])?;
2466 let indices = value_to_tensor(&args[2])?;
2467 Ok(Some(Value::Tensor(t.index_select(dim, &indices).map_err(|e| format!("{e}"))?)))
2468 }
2469
2470 "array_push" => {
2472 if args.len() != 2 { return Err("array_push requires 2 args: array, value".into()); }
2473 let mut arr_rc = match &args[0] { Value::Array(a) => Rc::clone(a), _ => return Err("array_push: first arg must be Array".into()) };
2474 Rc::make_mut(&mut arr_rc).push(args[1].clone());
2478 Ok(Some(Value::Array(arr_rc)))
2479 }
2480 "array_pop" => {
2481 if args.len() != 1 { return Err("array_pop requires 1 arg: array".into()); }
2482 let arr = match &args[0] { Value::Array(a) => (**a).clone(), _ => return Err("array_pop: expected Array".into()) };
2483 if arr.is_empty() { return Err("array_pop: empty array".into()); }
2484 let mut new_arr = arr;
2485 let last = new_arr.pop().unwrap();
2486 Ok(Some(Value::Tuple(Rc::new(vec![last, Value::Array(Rc::new(new_arr))]))))
2487 }
2488 "array_contains" => {
2489 if args.len() != 2 { return Err("array_contains requires 2 args: array, value".into()); }
2490 let arr = match &args[0] { Value::Array(a) => a, _ => return Err("array_contains: first arg must be Array".into()) };
2491 let needle = &args[1];
2492 let found = arr.iter().any(|v| format!("{v}") == format!("{needle}"));
2493 Ok(Some(Value::Bool(found)))
2494 }
2495 "array_reverse" => {
2496 if args.len() != 1 { return Err("array_reverse requires 1 arg: array".into()); }
2497 let arr = match &args[0] { Value::Array(a) => (**a).clone(), _ => return Err("array_reverse: expected Array".into()) };
2498 let mut new_arr = arr;
2499 new_arr.reverse();
2500 Ok(Some(Value::Array(Rc::new(new_arr))))
2501 }
2502 "array_flatten" => {
2503 if args.len() != 1 { return Err("array_flatten requires 1 arg: array".into()); }
2504 let arr = match &args[0] { Value::Array(a) => a.clone(), _ => return Err("array_flatten: expected Array".into()) };
2505 let mut result = Vec::new();
2506 fn flatten_recursive(arr: &[Value], result: &mut Vec<Value>) {
2507 for v in arr {
2508 match v {
2509 Value::Array(inner) => flatten_recursive(inner, result),
2510 _ => result.push(v.clone()),
2511 }
2512 }
2513 }
2514 flatten_recursive(&arr, &mut result);
2515 Ok(Some(Value::Array(Rc::new(result))))
2516 }
2517 "array_len" => {
2518 if args.len() != 1 { return Err("array_len requires 1 arg: array".into()); }
2519 match &args[0] {
2520 Value::Array(a) => Ok(Some(Value::Int(a.len() as i64))),
2521 _ => Err("array_len: expected Array".into()),
2522 }
2523 }
2524 "array_slice" => {
2525 if args.len() != 3 { return Err("array_slice requires 3 args: array, start, end".into()); }
2526 let arr = match &args[0] { Value::Array(a) => a, _ => return Err("array_slice: expected Array".into()) };
2527 let start = match &args[1] { Value::Int(i) => *i as usize, _ => return Err("array_slice: start must be Int".into()) };
2528 let end = match &args[2] { Value::Int(i) => *i as usize, _ => return Err("array_slice: end must be Int".into()) };
2529 if start > end || end > arr.len() {
2530 return Err(format!("array_slice: bounds [{start}, {end}) out of range for len {}", arr.len()));
2531 }
2532 Ok(Some(Value::Array(Rc::new(arr[start..end].to_vec()))))
2533 }
2534
2535 "Map.new" => {
2537 if !args.is_empty() { return Err("Map.new takes 0 arguments".into()); }
2538 Ok(Some(Value::Map(Rc::new(RefCell::new(crate::det_map::DetMap::new())))))
2539 }
2540 "Set.new" => {
2541 if !args.is_empty() { return Err("Set.new takes 0 arguments".into()); }
2542 Ok(Some(Value::Map(Rc::new(RefCell::new(crate::det_map::DetMap::new())))))
2543 }
2544
2545 "bit_and" => {
2547 if args.len() != 2 { return Err("bit_and requires 2 Int args".into()); }
2548 let a = match &args[0] { Value::Int(i) => *i, _ => return Err("bit_and: expected Int".into()) };
2549 let b = match &args[1] { Value::Int(i) => *i, _ => return Err("bit_and: expected Int".into()) };
2550 Ok(Some(Value::Int(a & b)))
2551 }
2552 "bit_or" => {
2553 if args.len() != 2 { return Err("bit_or requires 2 Int args".into()); }
2554 let a = match &args[0] { Value::Int(i) => *i, _ => return Err("bit_or: expected Int".into()) };
2555 let b = match &args[1] { Value::Int(i) => *i, _ => return Err("bit_or: expected Int".into()) };
2556 Ok(Some(Value::Int(a | b)))
2557 }
2558 "bit_xor" => {
2559 if args.len() != 2 { return Err("bit_xor requires 2 Int args".into()); }
2560 let a = match &args[0] { Value::Int(i) => *i, _ => return Err("bit_xor: expected Int".into()) };
2561 let b = match &args[1] { Value::Int(i) => *i, _ => return Err("bit_xor: expected Int".into()) };
2562 Ok(Some(Value::Int(a ^ b)))
2563 }
2564 "bit_not" => {
2565 if args.len() != 1 { return Err("bit_not requires 1 Int arg".into()); }
2566 let a = match &args[0] { Value::Int(i) => *i, _ => return Err("bit_not: expected Int".into()) };
2567 Ok(Some(Value::Int(!a)))
2568 }
2569 "bit_shl" => {
2570 if args.len() != 2 { return Err("bit_shl requires 2 Int args".into()); }
2571 let a = match &args[0] { Value::Int(i) => *i, _ => return Err("bit_shl: expected Int".into()) };
2572 let n = match &args[1] { Value::Int(i) => *i, _ => return Err("bit_shl: expected Int".into()) };
2573 if n < 0 || n > 63 { return Err("bit_shl: shift amount must be 0-63".into()); }
2574 Ok(Some(Value::Int(((a as u64) << n) as i64)))
2575 }
2576 "bit_shr" => {
2577 if args.len() != 2 { return Err("bit_shr requires 2 Int args".into()); }
2578 let a = match &args[0] { Value::Int(i) => *i, _ => return Err("bit_shr: expected Int".into()) };
2579 let n = match &args[1] { Value::Int(i) => *i, _ => return Err("bit_shr: expected Int".into()) };
2580 if n < 0 || n > 63 { return Err("bit_shr: shift amount must be 0-63".into()); }
2581 Ok(Some(Value::Int(((a as u64) >> n) as i64)))
2582 }
2583 "popcount" => {
2584 if args.len() != 1 { return Err("popcount requires 1 Int arg".into()); }
2585 let a = match &args[0] { Value::Int(i) => *i, _ => return Err("popcount: expected Int".into()) };
2586 Ok(Some(Value::Int((a as u64).count_ones() as i64)))
2587 }
2588
2589 "Adam.new" => {
2591 if args.len() < 2 || args.len() > 4 {
2592 return Err("Adam.new requires 2-4 args: n_params, lr, [beta1], [beta2]".into());
2593 }
2594 let n = match &args[0] { Value::Int(i) => *i as usize, _ => return Err("Adam.new: n_params must be Int".into()) };
2595 let lr = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("Adam.new: lr must be Float".into()) };
2596 let beta1 = if args.len() > 2 {
2597 match &args[2] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("Adam.new: beta1 must be Float".into()) }
2598 } else { 0.9 };
2599 let beta2 = if args.len() > 3 {
2600 match &args[3] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("Adam.new: beta2 must be Float".into()) }
2601 } else { 0.999 };
2602 let mut state = crate::ml::AdamState::new(n, lr);
2603 state.beta1 = beta1;
2604 state.beta2 = beta2;
2605 let erased: Rc<RefCell<dyn std::any::Any>> = Rc::new(RefCell::new(state));
2606 Ok(Some(Value::OptimizerState(erased)))
2607 }
2608 "Sgd.new" => {
2609 if args.len() < 2 || args.len() > 3 {
2610 return Err("Sgd.new requires 2-3 args: n_params, lr, [momentum]".into());
2611 }
2612 let n = match &args[0] { Value::Int(i) => *i as usize, _ => return Err("Sgd.new: n_params must be Int".into()) };
2613 let lr = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("Sgd.new: lr must be Float".into()) };
2614 let momentum = if args.len() > 2 {
2615 match &args[2] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("Sgd.new: momentum must be Float".into()) }
2616 } else { 0.0 };
2617 let state = crate::ml::SgdState::new(n, lr, momentum);
2618 let erased: Rc<RefCell<dyn std::any::Any>> = Rc::new(RefCell::new(state));
2619 Ok(Some(Value::OptimizerState(erased)))
2620 }
2621
2622 "stop_gradient" => {
2625 if args.len() != 1 { return Err("stop_gradient requires exactly 1 argument".into()); }
2626 Ok(Some(args[0].clone()))
2627 }
2628 "grad_checkpoint" => {
2630 if args.len() != 1 { return Err("grad_checkpoint requires exactly 1 argument".into()); }
2631 Ok(Some(args[0].clone()))
2632 }
2633 "clip_grad" => {
2635 if args.len() != 3 { return Err("clip_grad requires 3 arguments (value, min, max)".into()); }
2636 let val = match &args[0] {
2637 Value::Float(f) => *f,
2638 Value::Int(i) => *i as f64,
2639 _ => return Err("clip_grad requires numeric arguments".into()),
2640 };
2641 let min_val = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("clip_grad min must be numeric".into()) };
2642 let max_val = match &args[2] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("clip_grad max must be numeric".into()) };
2643 Ok(Some(Value::Float(val.max(min_val).min(max_val))))
2644 }
2645 "grad_scale" => {
2647 if args.len() != 2 { return Err("grad_scale requires 2 arguments (value, scale)".into()); }
2648 let val = match &args[0] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("grad_scale requires numeric first arg".into()) };
2649 let scale = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("grad_scale requires numeric scale".into()) };
2650 Ok(Some(Value::Float(val * scale)))
2651 }
2652
2653 "broadcast" => {
2656 if args.len() != 2 {
2657 return Err("broadcast requires 2 arguments (fn_name, tensor)".into());
2658 }
2659 let fn_name = match &args[0] {
2660 Value::String(s) => s.clone(),
2661 _ => return Err("broadcast: first argument must be a function name string".into()),
2662 };
2663 let t = value_to_tensor(&args[1])?;
2664
2665 match fn_name.as_str() {
2668 "sqrt" => return Ok(Some(Value::Tensor(t.map_simd(UnaryOp::Sqrt)))),
2669 "abs" => return Ok(Some(Value::Tensor(t.map_simd(UnaryOp::Abs)))),
2670 "neg" => return Ok(Some(Value::Tensor(t.map_simd(UnaryOp::Neg)))),
2671 "relu" => return Ok(Some(Value::Tensor(t.map_simd(UnaryOp::Relu)))),
2672 _ => {} }
2674
2675 let f: Box<dyn Fn(f64) -> f64> = match fn_name.as_str() {
2679 "sin" => Box::new(|x: f64| x.sin()),
2680 "cos" => Box::new(|x: f64| x.cos()),
2681 "tan" => Box::new(|x: f64| x.tan()),
2682 "asin" => Box::new(|x: f64| x.asin()),
2683 "acos" => Box::new(|x: f64| x.acos()),
2684 "atan" => Box::new(|x: f64| x.atan()),
2685 "exp" => Box::new(|x: f64| x.exp()),
2686 "ln" => Box::new(|x: f64| x.ln()),
2687 "log" => Box::new(|x: f64| x.ln()),
2688 "log2" => Box::new(|x: f64| x.log2()),
2689 "log10" => Box::new(|x: f64| x.log10()),
2690 "log1p" => Box::new(|x: f64| x.ln_1p()),
2691 "expm1" => Box::new(|x: f64| x.exp_m1()),
2692 "floor" => Box::new(|x: f64| x.floor()),
2693 "ceil" => Box::new(|x: f64| x.ceil()),
2694 "round" => Box::new(|x: f64| x.round()),
2695 "sigmoid" => Box::new(|x: f64| 1.0 / (1.0 + (-x).exp())),
2696 "tanh" => Box::new(|x: f64| x.tanh()),
2697 "sign" => Box::new(|x: f64| {
2698 if x > 0.0 { 1.0 } else if x < 0.0 { -1.0 } else { 0.0 }
2699 }),
2700 _ => return Err(format!("broadcast: unknown unary function '{fn_name}'")),
2701 };
2702 Ok(Some(Value::Tensor(t.map(f))))
2703 }
2704
2705 "broadcast2" => {
2706 if args.len() != 3 {
2707 return Err("broadcast2 requires 3 arguments (fn_name, tensor1, tensor2)".into());
2708 }
2709 let fn_name = match &args[0] {
2710 Value::String(s) => s.clone(),
2711 _ => return Err("broadcast2: first argument must be a function name string".into()),
2712 };
2713 let t1 = value_to_tensor(&args[1])?;
2714 let t2 = value_to_tensor(&args[2])?;
2715 let result = match fn_name.as_str() {
2716 "add" => t1.add(&t2),
2717 "sub" => t1.sub(&t2),
2718 "mul" => t1.mul_elem(&t2),
2719 "div" => t1.div_elem(&t2),
2720 "pow" => t1.elem_pow(&t2),
2721 "min" => t1.elem_min(&t2),
2722 "max" => t1.elem_max(&t2),
2723 "atan2" => t1.elem_atan2(&t2),
2724 "hypot" => t1.elem_hypot(&t2),
2725 _ => return Err(format!("broadcast2: unknown binary function '{fn_name}'")),
2726 };
2727 match result {
2728 Ok(t) => Ok(Some(Value::Tensor(t))),
2729 Err(e) => Err(format!("broadcast2: {e}")),
2730 }
2731 }
2732
2733 "peak_rss" => {
2735 Ok(Some(Value::Int(peak_rss_kb() as i64)))
2736 }
2737
2738 "broadcast_fma" => {
2740 if args.len() != 3 {
2743 return Err("broadcast_fma requires 3 arguments (a, b, c)".into());
2744 }
2745 let a = value_to_tensor(&args[0])?;
2746 let b = value_to_tensor(&args[1])?;
2747 let c = value_to_tensor(&args[2])?;
2748 let result = a.fused_mul_add(&b, &c)
2749 .map_err(|e| format!("broadcast_fma: {e}"))?;
2750 Ok(Some(Value::Tensor(result)))
2751 }
2752
2753 "tensor_where" => {
2755 let a = value_to_tensor(&args[0])?;
2756 let cond = value_to_tensor(&args[1])?;
2757 let other = value_to_tensor(&args[2])?;
2758 Ok(Some(Value::Tensor(a.tensor_where(cond, other).map_err(|e| format!("{e}"))?)))
2759 }
2760 "tensor_any" => {
2761 let t = value_to_tensor(&args[0])?;
2762 Ok(Some(Value::Bool(t.any())))
2763 }
2764 "tensor_all" => {
2765 let t = value_to_tensor(&args[0])?;
2766 Ok(Some(Value::Bool(t.all())))
2767 }
2768 "tensor_nonzero" => {
2769 let t = value_to_tensor(&args[0])?;
2770 Ok(Some(Value::Tensor(t.nonzero())))
2771 }
2772 "tensor_masked_fill" => {
2773 let t = value_to_tensor(&args[0])?;
2774 let mask = value_to_tensor(&args[1])?;
2775 let val = value_to_f64(&args[2])?;
2776 Ok(Some(Value::Tensor(t.masked_fill(mask, val).map_err(|e| format!("{e}"))?)))
2777 }
2778 "tensor_mean_axis" => {
2780 let t = value_to_tensor(&args[0])?;
2781 let axis = value_to_usize(&args[1])?;
2782 let keepdim = matches!(args.get(2), Some(Value::Bool(true)));
2783 Ok(Some(Value::Tensor(t.mean_axis(axis, keepdim).map_err(|e| format!("{e}"))?)))
2784 }
2785 "tensor_var_axis" => {
2786 let t = value_to_tensor(&args[0])?;
2787 let axis = value_to_usize(&args[1])?;
2788 let keepdim = matches!(args.get(2), Some(Value::Bool(true)));
2789 Ok(Some(Value::Tensor(t.var_axis(axis, keepdim).map_err(|e| format!("{e}"))?)))
2790 }
2791 "tensor_std_axis" => {
2792 let t = value_to_tensor(&args[0])?;
2793 let axis = value_to_usize(&args[1])?;
2794 let keepdim = matches!(args.get(2), Some(Value::Bool(true)));
2795 Ok(Some(Value::Tensor(t.std_axis(axis, keepdim).map_err(|e| format!("{e}"))?)))
2796 }
2797 "tensor_prod_axis" => {
2798 let t = value_to_tensor(&args[0])?;
2799 let axis = value_to_usize(&args[1])?;
2800 let keepdim = matches!(args.get(2), Some(Value::Bool(true)));
2801 Ok(Some(Value::Tensor(t.prod_axis(axis, keepdim).map_err(|e| format!("{e}"))?)))
2802 }
2803 "tensor_sort" => {
2805 let t = value_to_tensor(&args[0])?;
2806 let axis = value_to_usize(&args[1])?;
2807 let desc = matches!(args.get(2), Some(Value::Bool(true)));
2808 Ok(Some(Value::Tensor(t.sort_axis(axis, desc).map_err(|e| format!("{e}"))?)))
2809 }
2810 "tensor_argsort_axis" => {
2811 let t = value_to_tensor(&args[0])?;
2812 let axis = value_to_usize(&args[1])?;
2813 let desc = matches!(args.get(2), Some(Value::Bool(true)));
2814 Ok(Some(Value::Tensor(t.argsort_axis(axis, desc).map_err(|e| format!("{e}"))?)))
2815 }
2816 "einsum" => {
2818 let notation = match &args[0] {
2819 Value::String(s) => s.as_str().to_string(),
2820 _ => return Err("einsum: first arg must be notation string".into()),
2821 };
2822 let tensors: Vec<&Tensor> = args[1..].iter()
2823 .map(value_to_tensor)
2824 .collect::<Result<_, _>>()?;
2825 let result = Tensor::einsum(¬ation, &tensors).map_err(|e| format!("{e}"))?;
2826 Ok(Some(Value::Tensor(result)))
2827 }
2828 "tensor_unsqueeze" => {
2830 let t = value_to_tensor(&args[0])?;
2831 let dim = value_to_usize(&args[1])?;
2832 Ok(Some(Value::Tensor(t.unsqueeze(dim).map_err(|e| format!("{e}"))?)))
2833 }
2834 "tensor_squeeze" => {
2835 let t = value_to_tensor(&args[0])?;
2836 let dim = args.get(1).map(|v| value_to_usize(v)).transpose()?;
2837 Ok(Some(Value::Tensor(t.squeeze(dim).map_err(|e| format!("{e}"))?)))
2838 }
2839 "tensor_flatten" => {
2840 let t = value_to_tensor(&args[0])?;
2841 let start = value_to_usize(&args[1])?;
2842 let end = value_to_usize(&args[2])?;
2843 Ok(Some(Value::Tensor(t.flatten(start, end).map_err(|e| format!("{e}"))?)))
2844 }
2845 "tensor_chunk" => {
2846 let t = value_to_tensor(&args[0])?;
2847 let n = value_to_usize(&args[1])?;
2848 let dim = value_to_usize(&args[2])?;
2849 let chunks = t.chunk(n, dim).map_err(|e| format!("{e}"))?;
2850 Ok(Some(Value::Array(Rc::new(chunks.into_iter().map(Value::Tensor).collect()))))
2851 }
2852 "svd" => {
2854 let t = value_to_tensor(&args[0])?;
2855 let (u, s, vt) = t.svd().map_err(|e| format!("{e}"))?;
2856 let s_tensor = Tensor::from_vec(s, &[u.shape()[1]]).map_err(|e| format!("{e}"))?;
2857 Ok(Some(Value::Tuple(Rc::new(vec![
2858 Value::Tensor(u),
2859 Value::Tensor(s_tensor),
2860 Value::Tensor(vt),
2861 ]))))
2862 }
2863 "pinv" => {
2864 let t = value_to_tensor(&args[0])?;
2865 Ok(Some(Value::Tensor(t.pinv().map_err(|e| format!("{e}"))?)))
2866 }
2867 "pca" => {
2868 let t = value_to_tensor(&args[0])?;
2869 let n_components = value_to_usize(&args[1])?;
2870 let (transformed, components, variance) = crate::ml::pca(&t, n_components).map_err(|e| format!("{e}"))?;
2871 let vlen = variance.len();
2872 let var_tensor = Tensor::from_vec(variance, &[vlen]).map_err(|e| format!("{e}"))?;
2873 Ok(Some(Value::Tuple(Rc::new(vec![
2874 Value::Tensor(transformed),
2875 Value::Tensor(components),
2876 Value::Tensor(var_tensor),
2877 ]))))
2878 }
2879 "sparse_add" => {
2881 let a = value_to_sparse(&args[0])?;
2882 let b = value_to_sparse(&args[1])?;
2883 Ok(Some(Value::SparseTensor(crate::sparse::sparse_add(a, b).map_err(|e| e)?)))
2884 }
2885 "sparse_sub" => {
2886 let a = value_to_sparse(&args[0])?;
2887 let b = value_to_sparse(&args[1])?;
2888 Ok(Some(Value::SparseTensor(crate::sparse::sparse_sub(a, b).map_err(|e| e)?)))
2889 }
2890 "sparse_matmul" => {
2891 let a = value_to_sparse(&args[0])?;
2892 let b = value_to_sparse(&args[1])?;
2893 Ok(Some(Value::SparseTensor(crate::sparse::sparse_matmul(a, b).map_err(|e| e)?)))
2894 }
2895 "sparse_transpose" => {
2896 let a = value_to_sparse(&args[0])?;
2897 Ok(Some(Value::SparseTensor(crate::sparse::sparse_transpose(a))))
2898 }
2899 "kmeans" => {
2901 let data = value_to_f64_vec(&args[0])?;
2902 let n_samples = value_to_usize(&args[1])?;
2903 let n_features = value_to_usize(&args[2])?;
2904 let k = value_to_usize(&args[3])?;
2905 let max_iter = value_to_usize(&args[4])?;
2906 let seed = match &args[5] { Value::Int(v) => *v as u64, _ => 42 };
2907 let (centroids, labels, inertia) = crate::clustering::kmeans(&data, n_samples, n_features, k, max_iter, seed);
2908 let label_vals: Vec<Value> = labels.iter().map(|&l| Value::Int(l as i64)).collect();
2909 let centroid_t = Tensor::from_vec(centroids, &[k, n_features]).map_err(|e| format!("{e}"))?;
2910 Ok(Some(Value::Tuple(Rc::new(vec![
2911 Value::Tensor(centroid_t),
2912 Value::Array(Rc::new(label_vals)),
2913 Value::Float(inertia),
2914 ]))))
2915 }
2916 "dbscan" => {
2917 let data = value_to_f64_vec(&args[0])?;
2918 let n_samples = value_to_usize(&args[1])?;
2919 let n_features = value_to_usize(&args[2])?;
2920 let eps = value_to_f64(&args[3])?;
2921 let min_samples = value_to_usize(&args[4])?;
2922 let labels = crate::clustering::dbscan(&data, n_samples, n_features, eps, min_samples);
2923 let label_vals: Vec<Value> = labels.iter().map(|&l| Value::Int(l)).collect();
2924 Ok(Some(Value::Array(Rc::new(label_vals))))
2925 }
2926 "label_encode" => {
2928 let strs: Vec<String> = match &args[0] {
2931 Value::Array(arr) => arr.iter().map(|v| match v {
2932 Value::String(s) => Ok(s.as_str().to_string()),
2933 _ => Err("label_encode: expected array of strings".to_string()),
2934 }).collect::<Result<_, _>>()?,
2935 _ => return Err("label_encode: expected array".into()),
2936 };
2937 let mut level_set = std::collections::BTreeSet::new();
2938 for s in &strs { level_set.insert(s.clone()); }
2939 let levels: Vec<String> = level_set.into_iter().collect();
2940 let level_map: std::collections::BTreeMap<&str, u32> = levels.iter().enumerate()
2941 .map(|(i, s)| (s.as_str(), i as u32)).collect();
2942 let codes: Vec<u32> = strs.iter().map(|s| level_map[s.as_str()]).collect();
2943 let level_vals: Vec<Value> = levels.iter().map(|s| Value::String(Rc::new(s.clone()))).collect();
2944 let code_vals: Vec<Value> = codes.iter().map(|&c| Value::Int(c as i64)).collect();
2945 Ok(Some(Value::Tuple(Rc::new(vec![
2946 Value::Array(Rc::new(level_vals)),
2947 Value::Array(Rc::new(code_vals)),
2948 ]))))
2949 }
2950 "acf" => {
2952 let data = value_to_f64_vec(&args[0])?;
2953 let max_lag = value_to_usize(&args[1])?;
2954 let result = crate::timeseries::acf(&data, max_lag);
2955 Ok(Some(Value::Array(Rc::new(result.into_iter().map(Value::Float).collect()))))
2956 }
2957 "ewma" => {
2958 let data = value_to_f64_vec(&args[0])?;
2959 let alpha = value_to_f64(&args[1])?;
2960 let result = crate::timeseries::ewma(&data, alpha);
2961 Ok(Some(Value::Array(Rc::new(result.into_iter().map(Value::Float).collect()))))
2962 }
2963 "diff" => {
2964 let data = value_to_f64_vec(&args[0])?;
2965 let periods = value_to_usize(&args[1])?;
2966 let result = crate::timeseries::diff(&data, periods);
2967 Ok(Some(Value::Array(Rc::new(result.into_iter().map(Value::Float).collect()))))
2968 }
2969 "bisect" => {
2971 Ok(None)
2973 }
2974 "polyfit" => {
2976 let x = value_to_f64_vec(&args[0])?;
2977 let y = value_to_f64_vec(&args[1])?;
2978 let degree = value_to_usize(&args[2])?;
2979 let coeffs = crate::interpolate::polyfit(&x, &y, degree).map_err(|e| e)?;
2980 Ok(Some(Value::Array(Rc::new(coeffs.into_iter().map(Value::Float).collect()))))
2981 }
2982 "polyval" => {
2983 let coeffs = value_to_f64_vec(&args[0])?;
2984 let x = value_to_f64_vec(&args[1])?;
2985 let result = crate::interpolate::polyval(&coeffs, &x);
2986 Ok(Some(Value::Array(Rc::new(result.into_iter().map(Value::Float).collect()))))
2987 }
2988
2989 "getenv" => {
2991 if args.len() != 1 { return Err("getenv requires 1 argument (name)".into()); }
2992 let name = match &args[0] {
2993 Value::String(s) => s.as_str().to_string(),
2994 _ => return Err("getenv: argument must be String".into()),
2995 };
2996 let val = std::env::var(&name).unwrap_or_default();
2997 Ok(Some(Value::String(Rc::new(val))))
2998 }
2999
3000 "map_new" => {
3002 if !args.is_empty() { return Err("map_new takes 0 arguments".into()); }
3003 Ok(Some(Value::Map(Rc::new(RefCell::new(crate::det_map::DetMap::new())))))
3004 }
3005 "map_set" => {
3006 if args.len() != 3 { return Err("map_set requires 3 args: map, key, value".into()); }
3007 let m = match &args[0] {
3008 Value::Map(m) => m,
3009 _ => return Err("map_set: first argument must be Map".into()),
3010 };
3011 let mut new_map = m.borrow().clone();
3012 new_map.insert(args[1].clone(), args[2].clone());
3013 Ok(Some(Value::Map(Rc::new(RefCell::new(new_map)))))
3014 }
3015 "map_get" => {
3016 if args.len() != 2 { return Err("map_get requires 2 args: map, key".into()); }
3017 let m = match &args[0] {
3018 Value::Map(m) => m,
3019 _ => return Err("map_get: first argument must be Map".into()),
3020 };
3021 match m.borrow().get(&args[1]) {
3022 Some(v) => Ok(Some(v.clone())),
3023 None => Ok(Some(Value::Void)),
3024 }
3025 }
3026 "map_keys" => {
3027 if args.len() != 1 { return Err("map_keys requires 1 arg: map".into()); }
3028 let m = match &args[0] {
3029 Value::Map(m) => m,
3030 _ => return Err("map_keys: argument must be Map".into()),
3031 };
3032 Ok(Some(Value::Array(Rc::new(m.borrow().keys()))))
3033 }
3034 "map_values" => {
3035 if args.len() != 1 { return Err("map_values requires 1 arg: map".into()); }
3036 let m = match &args[0] {
3037 Value::Map(m) => m,
3038 _ => return Err("map_values: argument must be Map".into()),
3039 };
3040 Ok(Some(Value::Array(Rc::new(m.borrow().values_vec()))))
3041 }
3042 "map_contains" => {
3043 if args.len() != 2 { return Err("map_contains requires 2 args: map, key".into()); }
3044 let m = match &args[0] {
3045 Value::Map(m) => m,
3046 _ => return Err("map_contains: first argument must be Map".into()),
3047 };
3048 Ok(Some(Value::Bool(m.borrow().contains_key(&args[1]))))
3049 }
3050
3051 "trapezoid" | "trapz" => {
3053 if args.len() != 2 {
3054 return Err("trapezoid requires 2 arguments (xs, ys)".into());
3055 }
3056 let xs = value_to_f64_vec(&args[0])?;
3057 let ys = value_to_f64_vec(&args[1])?;
3058 let result = crate::integrate::trapezoid(&xs, &ys)?;
3059 Ok(Some(Value::Float(result)))
3060 }
3061 "simpson" | "simps" => {
3062 if args.len() != 2 {
3063 return Err("simpson requires 2 arguments (xs, ys)".into());
3064 }
3065 let xs = value_to_f64_vec(&args[0])?;
3066 let ys = value_to_f64_vec(&args[1])?;
3067 let result = crate::integrate::simpson(&xs, &ys)?;
3068 Ok(Some(Value::Float(result)))
3069 }
3070 "cumtrapz" => {
3071 if args.len() != 2 {
3072 return Err("cumtrapz requires 2 arguments (xs, ys)".into());
3073 }
3074 let xs = value_to_f64_vec(&args[0])?;
3075 let ys = value_to_f64_vec(&args[1])?;
3076 let result = crate::integrate::cumtrapz(&xs, &ys)?;
3077 Ok(Some(Value::Array(Rc::new(
3078 result.into_iter().map(Value::Float).collect(),
3079 ))))
3080 }
3081 "diff_central" => {
3083 if args.len() != 2 {
3084 return Err("diff_central requires 2 arguments (xs, ys)".into());
3085 }
3086 let xs = value_to_f64_vec(&args[0])?;
3087 let ys = value_to_f64_vec(&args[1])?;
3088 let result = crate::differentiate::diff_central(&xs, &ys)?;
3089 Ok(Some(Value::Array(Rc::new(
3090 result.into_iter().map(Value::Float).collect(),
3091 ))))
3092 }
3093 "diff_forward" => {
3094 if args.len() != 2 {
3095 return Err("diff_forward requires 2 arguments (xs, ys)".into());
3096 }
3097 let xs = value_to_f64_vec(&args[0])?;
3098 let ys = value_to_f64_vec(&args[1])?;
3099 let result = crate::differentiate::diff_forward(&xs, &ys)?;
3100 Ok(Some(Value::Array(Rc::new(
3101 result.into_iter().map(Value::Float).collect(),
3102 ))))
3103 }
3104 "gradient_1d" => {
3105 if args.len() != 2 {
3106 return Err("gradient_1d requires 2 arguments (ys, dx)".into());
3107 }
3108 let ys = value_to_f64_vec(&args[0])?;
3109 let dx = value_to_f64(&args[1])?;
3110 let result = crate::differentiate::gradient_1d(&ys, dx);
3111 Ok(Some(Value::Array(Rc::new(
3112 result.into_iter().map(Value::Float).collect(),
3113 ))))
3114 }
3115 "penalty_objective" => {
3117 if args.len() != 3 {
3118 return Err(
3119 "penalty_objective requires 3 arguments (f_val, constraint_violations, penalty)"
3120 .into(),
3121 );
3122 }
3123 let f_val = value_to_f64(&args[0])?;
3124 let violations = value_to_f64_vec(&args[1])?;
3125 let penalty = value_to_f64(&args[2])?;
3126 let result = crate::optimize::penalty_objective(f_val, &violations, penalty);
3127 Ok(Some(Value::Float(result)))
3128 }
3129 "project_box" => {
3130 if args.len() != 3 {
3131 return Err("project_box requires 3 arguments (x, lower, upper)".into());
3132 }
3133 let x = value_to_f64_vec(&args[0])?;
3134 let lower = value_to_f64_vec(&args[1])?;
3135 let upper = value_to_f64_vec(&args[2])?;
3136 let result = crate::optimize::project_box(&x, &lower, &upper)?;
3137 Ok(Some(Value::Array(Rc::new(
3138 result.into_iter().map(Value::Float).collect(),
3139 ))))
3140 }
3141 "projected_gd_step" => {
3142 if args.len() != 5 {
3143 return Err(
3144 "projected_gd_step requires 5 arguments (x, grad, lr, lower, upper)".into(),
3145 );
3146 }
3147 let x = value_to_f64_vec(&args[0])?;
3148 let grad = value_to_f64_vec(&args[1])?;
3149 let lr = value_to_f64(&args[2])?;
3150 let lower = value_to_f64_vec(&args[3])?;
3151 let upper = value_to_f64_vec(&args[4])?;
3152 let result = crate::optimize::projected_gd_step(&x, &grad, lr, &lower, &upper)?;
3153 Ok(Some(Value::Array(Rc::new(
3154 result.into_iter().map(Value::Float).collect(),
3155 ))))
3156 }
3157
3158 "lstm_cell" => {
3160 if args.len() != 7 {
3161 return Err("lstm_cell requires 7 Tensor arguments: x, h_prev, c_prev, w_ih, w_hh, b_ih, b_hh".into());
3162 }
3163 let x = value_to_tensor(&args[0])?;
3164 let h_prev = value_to_tensor(&args[1])?;
3165 let c_prev = value_to_tensor(&args[2])?;
3166 let w_ih = value_to_tensor(&args[3])?;
3167 let w_hh = value_to_tensor(&args[4])?;
3168 let b_ih = value_to_tensor(&args[5])?;
3169 let b_hh = value_to_tensor(&args[6])?;
3170 let (h_new, c_new) = crate::ml::lstm_cell(x, h_prev, c_prev, w_ih, w_hh, b_ih, b_hh)?;
3171 Ok(Some(Value::Tuple(Rc::new(vec![
3172 Value::Tensor(h_new),
3173 Value::Tensor(c_new),
3174 ]))))
3175 }
3176
3177 "gru_cell" => {
3179 if args.len() != 6 {
3180 return Err("gru_cell requires 6 Tensor arguments: x, h_prev, w_ih, w_hh, b_ih, b_hh".into());
3181 }
3182 let x = value_to_tensor(&args[0])?;
3183 let h_prev = value_to_tensor(&args[1])?;
3184 let w_ih = value_to_tensor(&args[2])?;
3185 let w_hh = value_to_tensor(&args[3])?;
3186 let b_ih = value_to_tensor(&args[4])?;
3187 let b_hh = value_to_tensor(&args[5])?;
3188 let h_new = crate::ml::gru_cell(x, h_prev, w_ih, w_hh, b_ih, b_hh)?;
3189 Ok(Some(Value::Tensor(h_new)))
3190 }
3191
3192 "multi_head_attention" => {
3194 if args.len() != 12 {
3195 return Err("multi_head_attention requires 12 arguments: q, k, v, w_q, w_k, w_v, w_o, b_q, b_k, b_v, b_o, num_heads".into());
3196 }
3197 let q = value_to_tensor(&args[0])?;
3198 let k = value_to_tensor(&args[1])?;
3199 let v = value_to_tensor(&args[2])?;
3200 let w_q = value_to_tensor(&args[3])?;
3201 let w_k = value_to_tensor(&args[4])?;
3202 let w_v = value_to_tensor(&args[5])?;
3203 let w_o = value_to_tensor(&args[6])?;
3204 let b_q = value_to_tensor(&args[7])?;
3205 let b_k = value_to_tensor(&args[8])?;
3206 let b_v = value_to_tensor(&args[9])?;
3207 let b_o = value_to_tensor(&args[10])?;
3208 let num_heads = value_to_usize(&args[11])?;
3209 let out = crate::ml::multi_head_attention(
3210 q, k, v, w_q, w_k, w_v, w_o, b_q, b_k, b_v, b_o, num_heads,
3211 )?;
3212 Ok(Some(Value::Tensor(out)))
3213 }
3214
3215 "ar_fit" => {
3217 if args.len() != 2 {
3218 return Err("ar_fit requires 2 arguments: data (array), p (int)".into());
3219 }
3220 let data = value_to_f64_vec(&args[0])?;
3221 let p = value_to_usize(&args[1])?;
3222 let coeffs = crate::timeseries::ar_fit(&data, p)?;
3223 Ok(Some(Value::Array(Rc::new(
3224 coeffs.into_iter().map(Value::Float).collect(),
3225 ))))
3226 }
3227
3228 "arima_diff" => {
3230 if args.len() != 2 {
3231 return Err("arima_diff requires 2 arguments: data (array), d (int)".into());
3232 }
3233 let data = value_to_f64_vec(&args[0])?;
3234 let d = value_to_usize(&args[1])?;
3235 let result = crate::timeseries::arima_diff(&data, d);
3236 Ok(Some(Value::Array(Rc::new(
3237 result.into_iter().map(Value::Float).collect(),
3238 ))))
3239 }
3240
3241 "ar_forecast" => {
3243 if args.len() != 3 {
3244 return Err("ar_forecast requires 3 arguments: coeffs (array), history (array), steps (int)".into());
3245 }
3246 let coeffs = value_to_f64_vec(&args[0])?;
3247 let history = value_to_f64_vec(&args[1])?;
3248 let steps = value_to_usize(&args[2])?;
3249 let result = crate::timeseries::ar_forecast(&coeffs, &history, steps)?;
3250 Ok(Some(Value::Array(Rc::new(
3251 result.into_iter().map(Value::Float).collect(),
3252 ))))
3253 }
3254
3255 "fillna" => {
3257 if args.len() != 2 { return Err("fillna requires 2 arguments (array, fill_value)".into()); }
3258 let arr = match &args[0] {
3259 Value::Array(a) => a.as_ref().clone(),
3260 _ => return Err(format!("fillna: first argument must be Array, got {}", args[0].type_name())),
3261 };
3262 let fill = &args[1];
3263 let result: Vec<Value> = arr.iter().map(|v| {
3264 match v {
3265 Value::Na => fill.clone(),
3266 Value::Void => fill.clone(),
3267 Value::Float(f) if f.is_nan() => fill.clone(),
3268 other => other.clone(),
3269 }
3270 }).collect();
3271 Ok(Some(Value::Array(Rc::new(result))))
3272 }
3273
3274 "is_na" => {
3275 if args.len() != 1 { return Err("is_na requires 1 argument".into()); }
3276 let result = matches!(&args[0], Value::Na);
3277 Ok(Some(Value::Bool(result)))
3278 }
3279
3280 "is_not_null" => {
3281 if args.len() != 1 { return Err("is_not_null requires 1 argument".into()); }
3282 let result = match &args[0] {
3283 Value::Na => false,
3284 Value::Void => false,
3285 Value::Float(f) => !f.is_nan(),
3286 _ => true,
3287 };
3288 Ok(Some(Value::Bool(result)))
3289 }
3290
3291 "drop_na" => {
3292 if args.len() != 1 { return Err("drop_na requires 1 argument (array)".into()); }
3293 let arr = match &args[0] {
3294 Value::Array(a) => a.as_ref().clone(),
3295 _ => return Err(format!("drop_na: first argument must be Array, got {}", args[0].type_name())),
3296 };
3297 let result: Vec<Value> = arr.into_iter().filter(|v| !matches!(v, Value::Na)).collect();
3298 Ok(Some(Value::Array(Rc::new(result))))
3299 }
3300
3301 "interpolate_linear" => {
3302 if args.len() != 1 { return Err("interpolate_linear requires 1 argument (array of f64)".into()); }
3303 let data = value_to_f64_vec(&args[0])?;
3304 let n = data.len();
3305 if n == 0 {
3306 return Ok(Some(Value::Array(Rc::new(vec![]))));
3307 }
3308 let mut result = data.clone();
3309
3310 let first_valid = result.iter().position(|x| !x.is_nan());
3312 let last_valid = result.iter().rposition(|x| !x.is_nan());
3313
3314 if let (Some(fv), Some(lv)) = (first_valid, last_valid) {
3315 for i in 0..fv {
3317 result[i] = result[fv];
3318 }
3319 for i in (lv + 1)..n {
3321 result[i] = result[lv];
3322 }
3323 let mut i = fv + 1;
3325 while i < lv {
3326 if result[i].is_nan() {
3327 let start = i - 1;
3329 let mut end = i + 1;
3330 while end < n && result[end].is_nan() {
3331 end += 1;
3332 }
3333 let v0 = result[start];
3334 let v1 = result[end];
3335 let span = (end - start) as f64;
3336 for j in (start + 1)..end {
3337 let t = (j - start) as f64 / span;
3338 use cjc_repro::KahanAccumulatorF64;
3341 let mut acc = KahanAccumulatorF64::new();
3342 acc.add(v0 * (1.0 - t));
3343 acc.add(v1 * t);
3344 result[j] = acc.finalize();
3345 }
3346 i = end + 1;
3347 } else {
3348 i += 1;
3349 }
3350 }
3351 }
3352 Ok(Some(Value::Array(Rc::new(
3355 result.into_iter().map(Value::Float).collect(),
3356 ))))
3357 }
3358
3359 "coalesce" => {
3360 if args.is_empty() { return Err("coalesce requires at least 1 argument".into()); }
3361 let first_is_array = matches!(&args[0], Value::Array(_));
3363 if args.len() == 2 && first_is_array {
3364 let a = match &args[0] {
3366 Value::Array(a) => a.as_ref().clone(),
3367 _ => unreachable!(),
3368 };
3369 let b = match &args[1] {
3370 Value::Array(b) => b.as_ref().clone(),
3371 _ => return Err(format!("coalesce: second argument must be Array, got {}", args[1].type_name())),
3372 };
3373 if a.len() != b.len() {
3374 return Err(format!("coalesce: arrays must have equal length, got {} and {}", a.len(), b.len()));
3375 }
3376 let result: Vec<Value> = a.iter().zip(b.iter()).map(|(va, vb)| {
3377 let is_null_a = matches!(va, Value::Na) || matches!(va, Value::Void) || matches!(va, Value::Float(f) if f.is_nan());
3378 if is_null_a { vb.clone() } else { va.clone() }
3379 }).collect();
3380 Ok(Some(Value::Array(Rc::new(result))))
3381 } else {
3382 for arg in args {
3384 let is_null = matches!(arg, Value::Na) || matches!(arg, Value::Void) || matches!(arg, Value::Float(f) if f.is_nan());
3385 if !is_null { return Ok(Some(arg.clone())); }
3386 }
3387 Ok(Some(args.last().cloned().unwrap_or(Value::Na)))
3389 }
3390 }
3391
3392 "cut" => {
3393 if args.len() != 2 { return Err("cut requires 2 arguments (array, breaks)".into()); }
3394 let data = value_to_f64_vec(&args[0])?;
3395 let breaks = value_to_f64_vec(&args[1])?;
3396 if breaks.is_empty() {
3397 return Err("cut: breaks array must not be empty".into());
3398 }
3399 let mut sorted_breaks = breaks.clone();
3400 sorted_breaks.sort_by(f64::total_cmp);
3401 let labels: Vec<Value> = data.iter().map(|&x| {
3402 let label = if x <= sorted_breaks[0] {
3404 format!("(-inf,{}]", sorted_breaks[0])
3405 } else if x > sorted_breaks[sorted_breaks.len() - 1] {
3406 format!("({},inf)", sorted_breaks[sorted_breaks.len() - 1])
3407 } else {
3408 let mut found = String::new();
3409 for i in 1..sorted_breaks.len() {
3410 if x <= sorted_breaks[i] {
3411 found = format!("({},{}]", sorted_breaks[i - 1], sorted_breaks[i]);
3412 break;
3413 }
3414 }
3415 if found.is_empty() {
3416 format!("({},inf)", sorted_breaks[sorted_breaks.len() - 1])
3418 } else {
3419 found
3420 }
3421 };
3422 Value::String(Rc::new(label))
3423 }).collect();
3424 Ok(Some(Value::Array(Rc::new(labels))))
3425 }
3426
3427 "qcut" => {
3428 if args.len() != 2 { return Err("qcut requires 2 arguments (array, n_bins)".into()); }
3429 let data = value_to_f64_vec(&args[0])?;
3430 let n = value_to_usize(&args[1])?;
3431 if n == 0 {
3432 return Err("qcut: n_bins must be > 0".into());
3433 }
3434 let mut breaks = Vec::with_capacity(n - 1);
3436 for i in 1..n {
3437 let p = i as f64 / n as f64;
3438 let q = crate::stats::quantile(&data, p)?;
3439 breaks.push(q);
3440 }
3441 breaks.dedup_by(|a, b| *a == *b);
3443
3444 let mut sorted_breaks = breaks.clone();
3446 sorted_breaks.sort_by(f64::total_cmp);
3447 let labels: Vec<Value> = data.iter().map(|&x| {
3448 let label = if sorted_breaks.is_empty() || x <= sorted_breaks[0] {
3449 format!("(-inf,{}]", sorted_breaks.first().copied().unwrap_or(x))
3450 } else if x > sorted_breaks[sorted_breaks.len() - 1] {
3451 format!("({},inf)", sorted_breaks[sorted_breaks.len() - 1])
3452 } else {
3453 let mut found = String::new();
3454 for i in 1..sorted_breaks.len() {
3455 if x <= sorted_breaks[i] {
3456 found = format!("({},{}]", sorted_breaks[i - 1], sorted_breaks[i]);
3457 break;
3458 }
3459 }
3460 if found.is_empty() {
3461 format!("({},inf)", sorted_breaks[sorted_breaks.len() - 1])
3462 } else {
3463 found
3464 }
3465 };
3466 Value::String(Rc::new(label))
3467 }).collect();
3468 Ok(Some(Value::Array(Rc::new(labels))))
3469 }
3470
3471 "min_max_scale" => {
3472 if args.len() != 3 { return Err("min_max_scale requires 3 arguments (array, low, high)".into()); }
3473 let data = value_to_f64_vec(&args[0])?;
3474 let low = value_to_f64(&args[1])?;
3475 let high = value_to_f64(&args[2])?;
3476 if data.is_empty() {
3477 return Ok(Some(Value::Array(Rc::new(vec![]))));
3478 }
3479 let mut min_val = f64::INFINITY;
3480 let mut max_val = f64::NEG_INFINITY;
3481 for &x in &data {
3482 if x < min_val { min_val = x; }
3483 if x > max_val { max_val = x; }
3484 }
3485 let range = max_val - min_val;
3486 let result: Vec<Value> = if range == 0.0 {
3487 let mid = (low + high) / 2.0;
3489 data.iter().map(|_| Value::Float(mid)).collect()
3490 } else {
3491 data.iter().map(|&x| {
3492 use cjc_repro::KahanAccumulatorF64;
3493 let t = (x - min_val) / range;
3494 let mut acc = KahanAccumulatorF64::new();
3495 acc.add(low * (1.0 - t));
3496 acc.add(high * t);
3497 Value::Float(acc.finalize())
3498 }).collect()
3499 };
3500 Ok(Some(Value::Array(Rc::new(result))))
3501 }
3502
3503 "robust_scale" => {
3504 if args.len() != 1 { return Err("robust_scale requires 1 argument (array)".into()); }
3505 let data = value_to_f64_vec(&args[0])?;
3506 if data.is_empty() {
3507 return Ok(Some(Value::Array(Rc::new(vec![]))));
3508 }
3509 let med = crate::stats::median(&data)?;
3510 let iqr_val = crate::stats::iqr(&data)?;
3511 let result: Vec<Value> = if iqr_val == 0.0 {
3512 data.iter().map(|&x| Value::Float(x - med)).collect()
3513 } else {
3514 data.iter().map(|&x| Value::Float((x - med) / iqr_val)).collect()
3515 };
3516 Ok(Some(Value::Array(Rc::new(result))))
3517 }
3518
3519 "as_factor" => {
3521 if args.len() != 1 { return Err("as_factor requires 1 argument (string array)".into()); }
3523 let arr = match &args[0] {
3524 Value::Array(a) => a.as_ref().clone(),
3525 _ => return Err("as_factor: argument must be Array of strings".into()),
3526 };
3527 let mut levels: Vec<String> = Vec::new();
3529 let mut level_index = std::collections::BTreeMap::new();
3530 let mut codes: Vec<Value> = Vec::with_capacity(arr.len());
3531 for item in &arr {
3532 let s = match item {
3533 Value::String(s) => s.as_str().to_string(),
3534 _ => format!("{}", item),
3535 };
3536 let idx = if let Some(&idx) = level_index.get(&s) {
3537 idx
3538 } else {
3539 let idx = levels.len() as i64;
3540 level_index.insert(s.clone(), idx);
3541 levels.push(s);
3542 idx
3543 };
3544 codes.push(Value::Int(idx));
3545 }
3546 let level_values: Vec<Value> = levels.into_iter()
3547 .map(|s| Value::String(Rc::new(s)))
3548 .collect();
3549 let mut fields = std::collections::BTreeMap::new();
3550 fields.insert("__type".to_string(), Value::String(Rc::new("Factor".to_string())));
3551 fields.insert("levels".to_string(), Value::Array(Rc::new(level_values)));
3552 fields.insert("codes".to_string(), Value::Array(Rc::new(codes)));
3553 Ok(Some(Value::Struct { name: "Factor".to_string(), fields }))
3554 }
3555
3556 "factor_levels" => {
3557 if args.len() != 1 { return Err("factor_levels requires 1 argument (Factor)".into()); }
3559 match &args[0] {
3560 Value::Struct { name, fields } if name == "Factor" => {
3561 match fields.get("levels") {
3562 Some(v) => Ok(Some(v.clone())),
3563 None => Err("factor_levels: Factor missing 'levels' field".into()),
3564 }
3565 }
3566 _ => Err("factor_levels: argument must be a Factor".into()),
3567 }
3568 }
3569
3570 "factor_codes" => {
3571 if args.len() != 1 { return Err("factor_codes requires 1 argument (Factor)".into()); }
3573 match &args[0] {
3574 Value::Struct { name, fields } if name == "Factor" => {
3575 match fields.get("codes") {
3576 Some(v) => Ok(Some(v.clone())),
3577 None => Err("factor_codes: Factor missing 'codes' field".into()),
3578 }
3579 }
3580 _ => Err("factor_codes: argument must be a Factor".into()),
3581 }
3582 }
3583
3584 "fct_relevel" => {
3585 if args.len() != 2 { return Err("fct_relevel requires 2 arguments (Factor, new_level_order)".into()); }
3587 let (old_levels, old_codes) = match &args[0] {
3588 Value::Struct { name, fields } if name == "Factor" => {
3589 let levels = match fields.get("levels") {
3590 Some(Value::Array(a)) => a.as_ref().clone(),
3591 _ => return Err("fct_relevel: Factor missing 'levels'".into()),
3592 };
3593 let codes = match fields.get("codes") {
3594 Some(Value::Array(a)) => a.as_ref().clone(),
3595 _ => return Err("fct_relevel: Factor missing 'codes'".into()),
3596 };
3597 (levels, codes)
3598 }
3599 _ => return Err("fct_relevel: first argument must be a Factor".into()),
3600 };
3601 let new_order = match &args[1] {
3602 Value::Array(a) => a.as_ref().clone(),
3603 _ => return Err("fct_relevel: second argument must be array of level strings".into()),
3604 };
3605 let old_strs: Vec<String> = old_levels.iter().map(|v| match v {
3607 Value::String(s) => s.as_str().to_string(),
3608 _ => format!("{}", v),
3609 }).collect();
3610 let new_strs: Vec<String> = new_order.iter().map(|v| match v {
3612 Value::String(s) => s.as_str().to_string(),
3613 _ => format!("{}", v),
3614 }).collect();
3615 let mut remap = std::collections::BTreeMap::new();
3617 for (old_idx, s) in old_strs.iter().enumerate() {
3618 if let Some(new_idx) = new_strs.iter().position(|ns| ns == s) {
3619 remap.insert(old_idx as i64, new_idx as i64);
3620 }
3621 }
3622 let new_codes: Vec<Value> = old_codes.iter().map(|v| {
3624 if let Value::Int(c) = v {
3625 Value::Int(remap.get(c).copied().unwrap_or(*c))
3626 } else { v.clone() }
3627 }).collect();
3628 let new_level_values: Vec<Value> = new_strs.into_iter()
3629 .map(|s| Value::String(Rc::new(s)))
3630 .collect();
3631 let mut fields = std::collections::BTreeMap::new();
3632 fields.insert("__type".to_string(), Value::String(Rc::new("Factor".to_string())));
3633 fields.insert("levels".to_string(), Value::Array(Rc::new(new_level_values)));
3634 fields.insert("codes".to_string(), Value::Array(Rc::new(new_codes)));
3635 Ok(Some(Value::Struct { name: "Factor".to_string(), fields }))
3636 }
3637
3638 "fct_lump" => {
3639 if args.len() != 2 { return Err("fct_lump requires 2 arguments (Factor, n)".into()); }
3642 let (old_levels, old_codes) = match &args[0] {
3643 Value::Struct { name, fields } if name == "Factor" => {
3644 let levels = match fields.get("levels") {
3645 Some(Value::Array(a)) => a.as_ref().clone(),
3646 _ => return Err("fct_lump: Factor missing 'levels'".into()),
3647 };
3648 let codes = match fields.get("codes") {
3649 Some(Value::Array(a)) => a.as_ref().clone(),
3650 _ => return Err("fct_lump: Factor missing 'codes'".into()),
3651 };
3652 (levels, codes)
3653 }
3654 _ => return Err("fct_lump: first argument must be a Factor".into()),
3655 };
3656 let n = match &args[1] {
3657 Value::Int(n) => *n as usize,
3658 _ => return Err("fct_lump: second argument must be Int".into()),
3659 };
3660 let mut freq = std::collections::BTreeMap::new();
3662 for v in &old_codes {
3663 if let Value::Int(c) = v { *freq.entry(*c).or_insert(0usize) += 1; }
3664 }
3665 let mut freq_vec: Vec<(i64, usize)> = freq.into_iter().collect();
3667 freq_vec.sort_by(|a, b| b.1.cmp(&a.1).then(a.0.cmp(&b.0)));
3668 let keep_codes: std::collections::BTreeSet<i64> = freq_vec.iter().take(n).map(|(c, _)| *c).collect();
3670 let old_strs: Vec<String> = old_levels.iter().map(|v| match v {
3672 Value::String(s) => s.as_str().to_string(),
3673 _ => format!("{}", v),
3674 }).collect();
3675 let mut new_levels: Vec<String> = Vec::new();
3676 let mut code_remap = std::collections::BTreeMap::new();
3677 for (old_idx, s) in old_strs.iter().enumerate() {
3678 if keep_codes.contains(&(old_idx as i64)) {
3679 let new_idx = new_levels.len() as i64;
3680 code_remap.insert(old_idx as i64, new_idx);
3681 new_levels.push(s.clone());
3682 }
3683 }
3684 let other_idx = new_levels.len() as i64;
3685 new_levels.push("Other".to_string());
3686 let new_codes: Vec<Value> = old_codes.iter().map(|v| {
3688 if let Value::Int(c) = v {
3689 Value::Int(*code_remap.get(c).unwrap_or(&other_idx))
3690 } else { v.clone() }
3691 }).collect();
3692 let new_level_values: Vec<Value> = new_levels.into_iter()
3693 .map(|s| Value::String(Rc::new(s)))
3694 .collect();
3695 let mut fields = std::collections::BTreeMap::new();
3696 fields.insert("__type".to_string(), Value::String(Rc::new("Factor".to_string())));
3697 fields.insert("levels".to_string(), Value::Array(Rc::new(new_level_values)));
3698 fields.insert("codes".to_string(), Value::Array(Rc::new(new_codes)));
3699 Ok(Some(Value::Struct { name: "Factor".to_string(), fields }))
3700 }
3701
3702 "fct_count" => {
3703 if args.len() != 1 { return Err("fct_count requires 1 argument (Factor)".into()); }
3705 let (levels, codes) = match &args[0] {
3706 Value::Struct { name, fields } if name == "Factor" => {
3707 let levels = match fields.get("levels") {
3708 Some(Value::Array(a)) => a.as_ref().clone(),
3709 _ => return Err("fct_count: Factor missing 'levels'".into()),
3710 };
3711 let codes = match fields.get("codes") {
3712 Some(Value::Array(a)) => a.as_ref().clone(),
3713 _ => return Err("fct_count: Factor missing 'codes'".into()),
3714 };
3715 (levels, codes)
3716 }
3717 _ => return Err("fct_count: argument must be a Factor".into()),
3718 };
3719 let mut freq = std::collections::BTreeMap::new();
3720 for v in &codes {
3721 if let Value::Int(c) = v { *freq.entry(*c).or_insert(0i64) += 1; }
3722 }
3723 let result: Vec<Value> = levels.iter().enumerate().map(|(i, lev)| {
3724 let count = freq.get(&(i as i64)).copied().unwrap_or(0);
3725 Value::Tuple(Rc::new(vec![lev.clone(), Value::Int(count)]))
3726 }).collect();
3727 Ok(Some(Value::Array(Rc::new(result))))
3728 }
3729
3730 "jarque_bera" => {
3732 if args.len() != 1 { return Err("jarque_bera requires 1 argument (data array)".into()); }
3733 let data = value_to_f64_vec(&args[0])?;
3734 let r = crate::hypothesis::jarque_bera(&data)?;
3735 let mut fields = std::collections::BTreeMap::new();
3736 fields.insert("statistic".to_string(), Value::Float(r.statistic));
3737 fields.insert("p_value".to_string(), Value::Float(r.p_value));
3738 Ok(Some(Value::Struct { name: "JarqueBeraResult".to_string(), fields }))
3739 }
3740
3741 "anderson_darling" => {
3742 if args.len() != 1 { return Err("anderson_darling requires 1 argument (data array)".into()); }
3743 let data = value_to_f64_vec(&args[0])?;
3744 let r = crate::hypothesis::anderson_darling(&data)?;
3745 let mut fields = std::collections::BTreeMap::new();
3746 fields.insert("statistic".to_string(), Value::Float(r.statistic));
3747 fields.insert("p_value".to_string(), Value::Float(r.p_value));
3748 Ok(Some(Value::Struct { name: "AndersonDarlingResult".to_string(), fields }))
3749 }
3750
3751 "ks_test" => {
3752 if args.len() != 1 { return Err("ks_test requires 1 argument (data array)".into()); }
3753 let data = value_to_f64_vec(&args[0])?;
3754 let r = crate::hypothesis::ks_test_normal(&data)?;
3755 let mut fields = std::collections::BTreeMap::new();
3756 fields.insert("statistic".to_string(), Value::Float(r.statistic));
3757 fields.insert("p_value".to_string(), Value::Float(r.p_value));
3758 Ok(Some(Value::Struct { name: "KSResult".to_string(), fields }))
3759 }
3760
3761 "cohens_d" => {
3763 if args.len() != 2 { return Err("cohens_d requires 2 arguments (x, y)".into()); }
3764 let x = value_to_f64_vec(&args[0])?;
3765 let y = value_to_f64_vec(&args[1])?;
3766 let d = crate::hypothesis::cohens_d(&x, &y)?;
3767 Ok(Some(Value::Float(d)))
3768 }
3769
3770 "eta_squared" => {
3771 if args.len() < 2 { return Err("eta_squared requires at least 2 group arguments".into()); }
3772 let groups: Vec<Vec<f64>> = args.iter().map(|a| value_to_f64_vec(a)).collect::<Result<Vec<_>, _>>()?;
3773 let refs: Vec<&[f64]> = groups.iter().map(|g| g.as_slice()).collect();
3774 let es = crate::hypothesis::eta_squared(&refs)?;
3775 Ok(Some(Value::Float(es)))
3776 }
3777
3778 "cramers_v" => {
3779 if args.len() != 3 { return Err("cramers_v requires 3 arguments (table, nrows, ncols)".into()); }
3781 let table = value_to_f64_vec(&args[0])?;
3782 let nrows = match &args[1] { Value::Int(n) => *n as usize, _ => return Err("cramers_v: nrows must be Int".into()) };
3783 let ncols = match &args[2] { Value::Int(n) => *n as usize, _ => return Err("cramers_v: ncols must be Int".into()) };
3784 let v = crate::hypothesis::cramers_v(&table, nrows, ncols)?;
3785 Ok(Some(Value::Float(v)))
3786 }
3787
3788 "levene_test" => {
3790 if args.len() < 2 { return Err("levene_test requires at least 2 group arguments".into()); }
3791 let groups: Vec<Vec<f64>> = args.iter().map(|a| value_to_f64_vec(a)).collect::<Result<Vec<_>, _>>()?;
3792 let refs: Vec<&[f64]> = groups.iter().map(|g| g.as_slice()).collect();
3793 let (w, p) = crate::hypothesis::levene_test(&refs)?;
3794 let mut fields = std::collections::BTreeMap::new();
3795 fields.insert("statistic".to_string(), Value::Float(w));
3796 fields.insert("p_value".to_string(), Value::Float(p));
3797 Ok(Some(Value::Struct { name: "LeveneResult".to_string(), fields }))
3798 }
3799
3800 "bartlett_test" => {
3801 if args.len() < 2 { return Err("bartlett_test requires at least 2 group arguments".into()); }
3802 let groups: Vec<Vec<f64>> = args.iter().map(|a| value_to_f64_vec(a)).collect::<Result<Vec<_>, _>>()?;
3803 let refs: Vec<&[f64]> = groups.iter().map(|g| g.as_slice()).collect();
3804 let (t, p) = crate::hypothesis::bartlett_test(&refs)?;
3805 let mut fields = std::collections::BTreeMap::new();
3806 fields.insert("statistic".to_string(), Value::Float(t));
3807 fields.insert("p_value".to_string(), Value::Float(p));
3808 Ok(Some(Value::Struct { name: "BartlettResult".to_string(), fields }))
3809 }
3810
3811 "latin_hypercube" => {
3813 if args.len() != 3 { return Err("latin_hypercube requires 3 arguments (n, dims, seed)".into()); }
3815 let n = match &args[0] { Value::Int(n) => *n as usize, _ => return Err("latin_hypercube: n must be Int".into()) };
3816 let dims = match &args[1] { Value::Int(d) => *d as usize, _ => return Err("latin_hypercube: dims must be Int".into()) };
3817 let seed = match &args[2] { Value::Int(s) => *s as u64, _ => return Err("latin_hypercube: seed must be Int".into()) };
3818 let t = crate::distributions::latin_hypercube_sample(n, dims, seed);
3819 Ok(Some(Value::Tensor(t)))
3820 }
3821
3822 "sobol_sequence" => {
3823 if args.len() != 2 { return Err("sobol_sequence requires 2 arguments (n, dims)".into()); }
3825 let n = match &args[0] { Value::Int(n) => *n as usize, _ => return Err("sobol_sequence: n must be Int".into()) };
3826 let dims = match &args[1] { Value::Int(d) => *d as usize, _ => return Err("sobol_sequence: dims must be Int".into()) };
3827 let t = crate::distributions::sobol_sequence(n, dims);
3828 Ok(Some(Value::Tensor(t)))
3829 }
3830
3831 "train_test_split" => {
3832 if args.len() != 3 { return Err("train_test_split requires 3 arguments (n, test_fraction, seed)".into()); }
3834 let n = match &args[0] { Value::Int(n) => *n as usize, _ => return Err("train_test_split: n must be Int".into()) };
3835 let frac = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("train_test_split: test_fraction must be Float".into()) };
3836 let seed = match &args[2] { Value::Int(s) => *s as u64, _ => return Err("train_test_split: seed must be Int".into()) };
3837 let (train, test) = crate::ml::train_test_split(n, frac, seed);
3838 let train_vals: Vec<Value> = train.into_iter().map(|i| Value::Int(i as i64)).collect();
3839 let test_vals: Vec<Value> = test.into_iter().map(|i| Value::Int(i as i64)).collect();
3840 Ok(Some(Value::Tuple(Rc::new(vec![
3841 Value::Array(Rc::new(train_vals)),
3842 Value::Array(Rc::new(test_vals)),
3843 ]))))
3844 }
3845
3846 "kfold_indices" => {
3847 if args.len() != 3 { return Err("kfold_indices requires 3 arguments (n, k, seed)".into()); }
3849 let n = match &args[0] { Value::Int(n) => *n as usize, _ => return Err("kfold_indices: n must be Int".into()) };
3850 let k = match &args[1] { Value::Int(k) => *k as usize, _ => return Err("kfold_indices: k must be Int".into()) };
3851 let seed = match &args[2] { Value::Int(s) => *s as u64, _ => return Err("kfold_indices: seed must be Int".into()) };
3852 let folds = crate::ml::kfold_indices(n, k, seed);
3853 let result: Vec<Value> = folds.into_iter().map(|(train, test)| {
3854 let train_vals: Vec<Value> = train.into_iter().map(|i| Value::Int(i as i64)).collect();
3855 let test_vals: Vec<Value> = test.into_iter().map(|i| Value::Int(i as i64)).collect();
3856 Value::Tuple(Rc::new(vec![
3857 Value::Array(Rc::new(train_vals)),
3858 Value::Array(Rc::new(test_vals)),
3859 ]))
3860 }).collect();
3861 Ok(Some(Value::Array(Rc::new(result))))
3862 }
3863
3864 "bootstrap" => {
3865 if args.len() != 4 { return Err("bootstrap requires 4 arguments (data, n_resamples, stat_fn, seed)".into()); }
3868 let data = match &args[0] {
3869 Value::Array(a) => {
3870 let mut v = Vec::with_capacity(a.len());
3871 for val in a.iter() {
3872 match val {
3873 Value::Float(f) => v.push(*f),
3874 Value::Int(i) => v.push(*i as f64),
3875 _ => return Err("bootstrap: data elements must be numeric".into()),
3876 }
3877 }
3878 v
3879 }
3880 _ => return Err("bootstrap: data must be an array".into()),
3881 };
3882 let n_resamples = match &args[1] { Value::Int(n) => *n as usize, _ => return Err("bootstrap: n_resamples must be Int".into()) };
3883 let stat_fn = match &args[2] { Value::Int(s) => *s as usize, _ => return Err("bootstrap: stat_fn must be Int (0=mean, 1=median)".into()) };
3884 let seed = match &args[3] { Value::Int(s) => *s as u64, _ => return Err("bootstrap: seed must be Int".into()) };
3885 let (point, ci_lower, ci_upper, se) = crate::ml::bootstrap(&data, n_resamples, stat_fn, seed)?;
3886 let mut fields = std::collections::BTreeMap::new();
3887 fields.insert("point".to_string(), Value::Float(point));
3888 fields.insert("ci_lower".to_string(), Value::Float(ci_lower));
3889 fields.insert("ci_upper".to_string(), Value::Float(ci_upper));
3890 fields.insert("se".to_string(), Value::Float(se));
3891 Ok(Some(Value::Struct {
3892 name: "BootstrapResult".into(),
3893 fields,
3894 }))
3895 }
3896 "permutation_test" => {
3897 if args.len() != 4 { return Err("permutation_test requires 4 arguments (x, y, n_perms, seed)".into()); }
3899 let extract_floats = |val: &Value, name: &str| -> Result<Vec<f64>, String> {
3900 match val {
3901 Value::Array(a) => {
3902 let mut v = Vec::with_capacity(a.len());
3903 for el in a.iter() {
3904 match el {
3905 Value::Float(f) => v.push(*f),
3906 Value::Int(i) => v.push(*i as f64),
3907 _ => return Err(format!("permutation_test: {} elements must be numeric", name)),
3908 }
3909 }
3910 Ok(v)
3911 }
3912 _ => Err(format!("permutation_test: {} must be an array", name)),
3913 }
3914 };
3915 let x = extract_floats(&args[0], "x")?;
3916 let y = extract_floats(&args[1], "y")?;
3917 let n_perms = match &args[2] { Value::Int(n) => *n as usize, _ => return Err("permutation_test: n_perms must be Int".into()) };
3918 let seed = match &args[3] { Value::Int(s) => *s as u64, _ => return Err("permutation_test: seed must be Int".into()) };
3919 let (observed, p_value) = crate::ml::permutation_test(&x, &y, n_perms, seed)?;
3920 let mut fields = std::collections::BTreeMap::new();
3921 fields.insert("observed_diff".to_string(), Value::Float(observed));
3922 fields.insert("p_value".to_string(), Value::Float(p_value));
3923 Ok(Some(Value::Struct {
3924 name: "PermutationResult".into(),
3925 fields,
3926 }))
3927 }
3928
3929 "stratified_split" => {
3930 if args.len() != 3 { return Err("stratified_split requires 3 arguments (labels, test_fraction, seed)".into()); }
3932 let labels = match &args[0] {
3933 Value::Array(a) => {
3934 let mut v = Vec::with_capacity(a.len());
3935 for val in a.iter() {
3936 match val {
3937 Value::Int(i) => v.push(*i),
3938 _ => return Err("stratified_split: labels must be integer array".into()),
3939 }
3940 }
3941 v
3942 }
3943 _ => return Err("stratified_split: labels must be an array".into()),
3944 };
3945 let frac = match &args[1] { Value::Float(f) => *f, Value::Int(i) => *i as f64, _ => return Err("stratified_split: test_fraction must be Float".into()) };
3946 let seed = match &args[2] { Value::Int(s) => *s as u64, _ => return Err("stratified_split: seed must be Int".into()) };
3947 let (train, test) = crate::ml::stratified_split(&labels, frac, seed);
3948 let train_vals: Vec<Value> = train.into_iter().map(|i| Value::Int(i as i64)).collect();
3949 let test_vals: Vec<Value> = test.into_iter().map(|i| Value::Int(i as i64)).collect();
3950 Ok(Some(Value::Tuple(Rc::new(vec![
3951 Value::Array(Rc::new(train_vals)),
3952 Value::Array(Rc::new(test_vals)),
3953 ]))))
3954 }
3955
3956 _ => Ok(None), }
3958}
3959
3960fn value_to_sparse(val: &Value) -> Result<&crate::sparse::SparseCsr, String> {
3962 match val {
3963 Value::SparseTensor(s) => Ok(s),
3964 _ => Err(format!("expected SparseTensor, got {}", val.type_name())),
3965 }
3966}
3967
3968pub fn peak_rss_kb() -> u64 {
3980 #[cfg(target_os = "windows")]
3981 {
3982 peak_rss_windows()
3983 }
3984 #[cfg(target_os = "linux")]
3985 {
3986 peak_rss_linux()
3987 }
3988 #[cfg(target_os = "macos")]
3989 {
3990 peak_rss_macos()
3991 }
3992 #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
3993 {
3994 0
3995 }
3996}
3997
3998#[cfg(target_os = "windows")]
3999fn peak_rss_windows() -> u64 {
4000 use std::mem::{size_of, MaybeUninit};
4001
4002 #[repr(C)]
4003 #[allow(non_snake_case)]
4004 struct ProcessMemoryCounters {
4005 cb: u32,
4006 PageFaultCount: u32,
4007 PeakWorkingSetSize: usize,
4008 WorkingSetSize: usize,
4009 QuotaPeakPagedPoolUsage: usize,
4010 QuotaPagedPoolUsage: usize,
4011 QuotaPeakNonPagedPoolUsage: usize,
4012 QuotaNonPagedPoolUsage: usize,
4013 PagefileUsage: usize,
4014 PeakPagefileUsage: usize,
4015 }
4016
4017 extern "system" {
4018 fn GetCurrentProcess() -> isize;
4019 fn K32GetProcessMemoryInfo(
4020 hProcess: isize,
4021 ppsmemCounters: *mut ProcessMemoryCounters,
4022 cb: u32,
4023 ) -> i32;
4024 }
4025
4026 unsafe {
4027 let mut pmc = MaybeUninit::<ProcessMemoryCounters>::zeroed();
4028 let pmc_ref = pmc.as_mut_ptr();
4029 (*pmc_ref).cb = size_of::<ProcessMemoryCounters>() as u32;
4030 let handle = GetCurrentProcess();
4031 if K32GetProcessMemoryInfo(handle, pmc_ref, (*pmc_ref).cb) != 0 {
4032 let pmc = pmc.assume_init();
4033 (pmc.PeakWorkingSetSize / 1024) as u64
4034 } else {
4035 0
4036 }
4037 }
4038}
4039
4040#[cfg(target_os = "linux")]
4041fn peak_rss_linux() -> u64 {
4042 if let Ok(contents) = std::fs::read_to_string("/proc/self/status") {
4044 for line in contents.lines() {
4045 if line.starts_with("VmHWM:") {
4046 let parts: Vec<&str> = line.split_whitespace().collect();
4047 if parts.len() >= 2 {
4048 if let Ok(kb) = parts[1].parse::<u64>() {
4049 return kb;
4050 }
4051 }
4052 }
4053 }
4054 }
4055 0
4056}
4057
4058#[cfg(target_os = "macos")]
4059fn peak_rss_macos() -> u64 {
4060 #[repr(C)]
4061 struct Rusage {
4062 ru_utime: [i64; 2], ru_stime: [i64; 2], ru_maxrss: i64, _padding: [i64; 11],
4067 }
4068
4069 extern "C" {
4070 fn getrusage(who: i32, usage: *mut Rusage) -> i32;
4071 }
4072
4073 unsafe {
4074 let mut usage = std::mem::MaybeUninit::<Rusage>::zeroed();
4075 if getrusage(0 , usage.as_mut_ptr()) == 0 {
4076 let usage = usage.assume_init();
4077 (usage.ru_maxrss as u64) / 1024
4079 } else {
4080 0
4081 }
4082 }
4083}
4084
4085#[cfg(test)]
4090mod tests {
4091 use super::*;
4092
4093 #[test]
4094 fn test_peak_rss_returns_nonzero() {
4095 let rss = peak_rss_kb();
4096 assert!(rss > 0, "peak_rss_kb() should return non-zero, got {rss}");
4098 }
4099
4100 #[test]
4101 fn test_peak_rss_builtin_dispatch() {
4102 let result = dispatch_builtin("peak_rss", &[]);
4103 match result {
4104 Ok(Some(Value::Int(kb))) => {
4105 assert!(kb > 0, "peak_rss should return positive value, got {kb}");
4106 }
4107 other => panic!("Expected Ok(Some(Int)), got: {other:?}"),
4108 }
4109 }
4110
4111 #[test]
4112 fn test_complex_constructor() {
4113 let result = dispatch_builtin("Complex", &[Value::Float(3.0), Value::Float(4.0)]);
4114 match result {
4115 Ok(Some(Value::Complex(c))) => {
4116 assert_eq!(c.re, 3.0);
4117 assert_eq!(c.im, 4.0);
4118 }
4119 _ => panic!("expected Complex value"),
4120 }
4121 }
4122
4123 #[test]
4124 fn test_complex_constructor_from_ints() {
4125 let result = dispatch_builtin("Complex", &[Value::Int(1), Value::Int(-2)]);
4126 match result {
4127 Ok(Some(Value::Complex(c))) => {
4128 assert_eq!(c.re, 1.0);
4129 assert_eq!(c.im, -2.0);
4130 }
4131 _ => panic!("expected Complex value"),
4132 }
4133 }
4134
4135 #[test]
4136 fn test_complex_real_only() {
4137 let result = dispatch_builtin("Complex", &[Value::Float(5.0)]);
4138 match result {
4139 Ok(Some(Value::Complex(c))) => {
4140 assert_eq!(c.re, 5.0);
4141 assert_eq!(c.im, 0.0);
4142 }
4143 _ => panic!("expected Complex value"),
4144 }
4145 }
4146
4147 #[test]
4148 fn test_to_string() {
4149 let result = dispatch_builtin("to_string", &[Value::Int(42)]);
4150 match result {
4151 Ok(Some(Value::String(s))) => assert_eq!(s.as_str(), "42"),
4152 _ => panic!("expected String value"),
4153 }
4154 }
4155
4156 #[test]
4157 fn test_len_array() {
4158 let arr = Value::Array(Rc::new(vec![Value::Int(1), Value::Int(2), Value::Int(3)]));
4159 let result = dispatch_builtin("len", &[arr]);
4160 assert!(matches!(result, Ok(Some(Value::Int(3)))));
4161 }
4162
4163 #[test]
4164 fn test_len_string() {
4165 let s = Value::String(Rc::new("hello".to_string()));
4166 let result = dispatch_builtin("len", &[s]);
4167 assert!(matches!(result, Ok(Some(Value::Int(5)))));
4168 }
4169
4170 #[test]
4171 fn test_assert_pass() {
4172 let result = dispatch_builtin("assert", &[Value::Bool(true)]);
4173 assert!(matches!(result, Ok(Some(Value::Void))));
4174 }
4175
4176 #[test]
4177 fn test_assert_fail() {
4178 let result = dispatch_builtin("assert", &[Value::Bool(false)]);
4179 assert!(result.is_err());
4180 }
4181
4182 #[test]
4183 fn test_assert_eq_pass() {
4184 let result = dispatch_builtin("assert_eq", &[Value::Int(42), Value::Int(42)]);
4185 assert!(matches!(result, Ok(Some(Value::Void))));
4186 }
4187
4188 #[test]
4189 fn test_assert_eq_fail() {
4190 let result = dispatch_builtin("assert_eq", &[Value::Int(1), Value::Int(2)]);
4191 assert!(result.is_err());
4192 }
4193
4194 #[test]
4195 fn test_sqrt() {
4196 let result = dispatch_builtin("sqrt", &[Value::Float(4.0)]);
4197 match result {
4198 Ok(Some(Value::Float(v))) => assert_eq!(v, 2.0),
4199 _ => panic!("expected Float"),
4200 }
4201 }
4202
4203 #[test]
4204 fn test_abs_float() {
4205 let result = dispatch_builtin("abs", &[Value::Float(-3.14)]);
4206 match result {
4207 Ok(Some(Value::Float(v))) => assert_eq!(v, 3.14),
4208 _ => panic!("expected Float"),
4209 }
4210 }
4211
4212 #[test]
4213 fn test_abs_int() {
4214 let result = dispatch_builtin("abs", &[Value::Int(-42)]);
4215 assert!(matches!(result, Ok(Some(Value::Int(42)))));
4216 }
4217
4218 #[test]
4219 fn test_floor() {
4220 let result = dispatch_builtin("floor", &[Value::Float(3.7)]);
4221 match result {
4222 Ok(Some(Value::Float(v))) => assert_eq!(v, 3.0),
4223 _ => panic!("expected Float"),
4224 }
4225 }
4226
4227 #[test]
4228 fn test_int_conversion() {
4229 let result = dispatch_builtin("int", &[Value::Float(3.9)]);
4230 assert!(matches!(result, Ok(Some(Value::Int(3)))));
4231 }
4232
4233 #[test]
4234 fn test_float_conversion() {
4235 let result = dispatch_builtin("float", &[Value::Int(42)]);
4236 match result {
4237 Ok(Some(Value::Float(v))) => assert_eq!(v, 42.0),
4238 _ => panic!("expected Float"),
4239 }
4240 }
4241
4242 #[test]
4243 fn test_isnan() {
4244 let result = dispatch_builtin("isnan", &[Value::Float(f64::NAN)]);
4245 assert!(matches!(result, Ok(Some(Value::Bool(true)))));
4246
4247 let result = dispatch_builtin("isnan", &[Value::Float(1.0)]);
4248 assert!(matches!(result, Ok(Some(Value::Bool(false)))));
4249
4250 let result = dispatch_builtin("isnan", &[Value::Int(0)]);
4251 assert!(matches!(result, Ok(Some(Value::Bool(false)))));
4252 }
4253
4254 #[test]
4255 fn test_isinf() {
4256 let result = dispatch_builtin("isinf", &[Value::Float(f64::INFINITY)]);
4257 assert!(matches!(result, Ok(Some(Value::Bool(true)))));
4258
4259 let result = dispatch_builtin("isinf", &[Value::Float(1.0)]);
4260 assert!(matches!(result, Ok(Some(Value::Bool(false)))));
4261 }
4262
4263 #[test]
4264 fn test_push() {
4265 let arr = Value::Array(Rc::new(vec![Value::Int(1)]));
4266 let result = dispatch_builtin("push", &[arr, Value::Int(2)]);
4267 match result {
4268 Ok(Some(Value::Array(a))) => {
4269 assert_eq!(a.len(), 2);
4270 assert!(matches!(&a[1], Value::Int(2)));
4271 }
4272 _ => panic!("expected Array"),
4273 }
4274 }
4275
4276 #[test]
4277 fn test_sort() {
4278 let arr = Value::Array(Rc::new(vec![
4279 Value::Float(3.0),
4280 Value::Float(1.0),
4281 Value::Float(2.0),
4282 ]));
4283 let result = dispatch_builtin("sort", &[arr]);
4284 match result {
4285 Ok(Some(Value::Array(a))) => {
4286 assert!(matches!(&a[0], Value::Float(v) if *v == 1.0));
4287 assert!(matches!(&a[1], Value::Float(v) if *v == 2.0));
4288 assert!(matches!(&a[2], Value::Float(v) if *v == 3.0));
4289 }
4290 _ => panic!("expected sorted Array"),
4291 }
4292 }
4293
4294 #[test]
4295 fn test_unknown_builtin_returns_none() {
4296 let result = dispatch_builtin("unknown_function", &[]);
4297 assert!(matches!(result, Ok(None)));
4298 }
4299
4300 #[test]
4301 fn test_tensor_zeros() {
4302 let shape = Value::Array(Rc::new(vec![Value::Int(2), Value::Int(3)]));
4303 let result = dispatch_builtin("Tensor.zeros", &[shape]);
4304 match result {
4305 Ok(Some(Value::Tensor(t))) => {
4306 assert_eq!(t.shape(), &[2, 3]);
4307 }
4308 _ => panic!("expected Tensor"),
4309 }
4310 }
4311
4312 #[test]
4313 fn test_values_equal() {
4314 assert!(values_equal(&Value::Int(42), &Value::Int(42)));
4315 assert!(!values_equal(&Value::Int(1), &Value::Int(2)));
4316 assert!(values_equal(&Value::Float(3.14), &Value::Float(3.14)));
4317 assert!(values_equal(&Value::Bool(true), &Value::Bool(true)));
4318 assert!(values_equal(&Value::Void, &Value::Void));
4319 assert!(!values_equal(&Value::Int(1), &Value::Float(1.0)));
4320 }
4321
4322 #[test]
4323 fn test_value_to_shape() {
4324 let arr = Value::Array(Rc::new(vec![Value::Int(2), Value::Int(3), Value::Int(4)]));
4325 assert_eq!(value_to_shape(&arr).unwrap(), vec![2, 3, 4]);
4326 }
4327
4328 #[test]
4329 fn test_value_to_shape_negative_rejected() {
4330 let arr = Value::Array(Rc::new(vec![Value::Int(-1)]));
4331 assert!(value_to_shape(&arr).is_err());
4332 }
4333
4334 #[test]
4335 fn test_value_to_f64_vec() {
4336 let arr = Value::Array(Rc::new(vec![
4337 Value::Float(1.0),
4338 Value::Int(2),
4339 Value::Float(3.5),
4340 ]));
4341 assert_eq!(value_to_f64_vec(&arr).unwrap(), vec![1.0, 2.0, 3.5]);
4342 }
4343
4344 #[test]
4345 fn test_log_float() {
4346 let result = dispatch_builtin("log", &[Value::Float(1.0)]);
4347 match result {
4348 Ok(Some(Value::Float(v))) => assert!((v - 0.0).abs() < 1e-15),
4349 _ => panic!("expected Float"),
4350 }
4351 }
4352
4353 #[test]
4354 fn test_log_e() {
4355 let result = dispatch_builtin("log", &[Value::Float(std::f64::consts::E)]);
4356 match result {
4357 Ok(Some(Value::Float(v))) => assert!((v - 1.0).abs() < 1e-15),
4358 _ => panic!("expected Float"),
4359 }
4360 }
4361
4362 #[test]
4363 fn test_exp_float() {
4364 let result = dispatch_builtin("exp", &[Value::Float(0.0)]);
4365 match result {
4366 Ok(Some(Value::Float(v))) => assert!((v - 1.0).abs() < 1e-15),
4367 _ => panic!("expected Float"),
4368 }
4369 }
4370
4371 #[test]
4372 fn test_exp_one() {
4373 let result = dispatch_builtin("exp", &[Value::Float(1.0)]);
4374 match result {
4375 Ok(Some(Value::Float(v))) => assert!((v - std::f64::consts::E).abs() < 1e-15),
4376 _ => panic!("expected Float"),
4377 }
4378 }
4379
4380 #[test]
4381 fn test_categorical_sample_deterministic() {
4382 let probs = Tensor::from_vec(vec![0.0, 0.0, 1.0], &[3]).unwrap();
4383 assert_eq!(categorical_sample_with_u(&probs, 0.5).unwrap(), 2);
4385 }
4386
4387 #[test]
4388 fn test_categorical_sample_first() {
4389 let probs = Tensor::from_vec(vec![0.5, 0.3, 0.2], &[3]).unwrap();
4390 assert_eq!(categorical_sample_with_u(&probs, 0.1).unwrap(), 0);
4392 }
4393
4394 #[test]
4395 fn test_categorical_sample_middle() {
4396 let probs = Tensor::from_vec(vec![0.2, 0.5, 0.3], &[3]).unwrap();
4397 assert_eq!(categorical_sample_with_u(&probs, 0.6).unwrap(), 1);
4399 }
4400
4401 #[test]
4402 fn test_categorical_sample_last() {
4403 let probs = Tensor::from_vec(vec![0.2, 0.3, 0.5], &[3]).unwrap();
4404 assert_eq!(categorical_sample_with_u(&probs, 0.99).unwrap(), 2);
4406 }
4407}