Skip to main content

truecalc_core/eval/functions/math/randarray/
mod.rs

1use crate::eval::coercion::to_number;
2use crate::eval::functions::check_arity;
3use crate::types::{ErrorKind, Value};
4
5/// LCG PRNG seeded from system time (Numerical Recipes constants).
6/// Returns successive values by stepping the state forward on each call.
7fn lcg_sequence(count: usize) -> Vec<f64> {
8    let seed = std::time::SystemTime::now()
9        .duration_since(std::time::UNIX_EPOCH)
10        .map(|d| d.as_nanos() as u64)
11        .unwrap_or(12345);
12    // Mix in a higher-entropy seed using the full nanosecond count
13    let mut state = seed
14        .wrapping_mul(6_364_136_223_846_793_005)
15        .wrapping_add(1_442_695_040_888_963_407);
16    let mut out = Vec::with_capacity(count);
17    for _ in 0..count {
18        state = state
19            .wrapping_mul(6_364_136_223_846_793_005)
20            .wrapping_add(1_442_695_040_888_963_407);
21        // Use upper 32 bits for better quality
22        let val = (state >> 32) as u32;
23        out.push((val as f64) / (u32::MAX as f64 + 1.0));
24    }
25    out
26}
27
28/// `RANDARRAY([rows], [cols], [min], [max], [integer])`
29///
30/// Returns an array of random numbers.
31/// With no args: returns a single random number (equivalent to RAND()).
32/// With rows/cols: returns a nested 2D array (rows × cols).
33pub fn randarray_fn(args: &[Value]) -> Value {
34    if args.is_empty() {
35        // No args: return single random number
36        let vals = lcg_sequence(1);
37        return Value::Number(vals[0]);
38    }
39    if let Some(err) = check_arity(args, 1, 5) {
40        return err;
41    }
42    let rows = match to_number(args[0].clone()) {
43        Err(e) => return e,
44        Ok(v) => v,
45    };
46    if rows <= 0.0 {
47        return Value::Error(ErrorKind::Num);
48    }
49    let rows = rows as usize;
50    let cols = if args.len() >= 2 {
51        match to_number(args[1].clone()) {
52            Err(e) => return e,
53            Ok(v) => {
54                if v <= 0.0 {
55                    return Value::Error(ErrorKind::Num);
56                }
57                v as usize
58            }
59        }
60    } else {
61        1
62    };
63
64    let total = rows * cols;
65    let nums = lcg_sequence(total);
66    // Always return nested 2D array so ROWS/COLUMNS work correctly
67    let outer: Vec<Value> = (0..rows)
68        .map(|r| {
69            let row: Vec<Value> = (0..cols)
70                .map(|c| Value::Number(nums[r * cols + c]))
71                .collect();
72            Value::Array(row)
73        })
74        .collect();
75    Value::Array(outer)
76}
77
78#[cfg(test)]
79mod tests;