formualizer_eval/builtins/
random.rs1use crate::args::ArgSchema;
3use crate::function::Function;
4use crate::traits::{ArgumentHandle, FunctionContext};
5use formualizer_common::{ExcelError, LiteralValue};
6use formualizer_macros::func_caps;
7use rand::Rng;
8
9#[derive(Debug)]
10pub struct RandFn;
11
12impl Function for RandFn {
13 func_caps!(VOLATILE);
14
15 fn name(&self) -> &'static str {
16 "RAND"
17 }
18 fn min_args(&self) -> usize {
19 0
20 }
21
22 fn eval_scalar<'a, 'b>(
23 &self,
24 _args: &'a [ArgumentHandle<'a, 'b>],
25 ctx: &dyn FunctionContext,
26 ) -> Result<LiteralValue, ExcelError> {
27 let mut rng = ctx.rng_for_current(self.function_salt());
28 Ok(LiteralValue::Number(rng.gen_range(0.0..1.0)))
29 }
30}
31
32pub fn register_builtins() {
33 crate::function_registry::register_function(std::sync::Arc::new(RandFn));
34 crate::function_registry::register_function(std::sync::Arc::new(RandBetweenFn));
35}
36
37#[cfg(test)]
38mod tests {
39 use super::*;
40 use crate::{interpreter::Interpreter, test_workbook::TestWorkbook};
41 use formualizer_parse::LiteralValue;
42
43 fn interp(wb: &TestWorkbook) -> Interpreter<'_> {
44 wb.interpreter()
45 }
46
47 #[test]
48 fn test_rand_caps() {
49 let rand_fn = RandFn;
50 let caps = rand_fn.caps();
51
52 assert!(caps.contains(crate::function::FnCaps::VOLATILE));
54
55 assert!(!caps.contains(crate::function::FnCaps::PURE));
57 }
58
59 #[test]
60 fn test_rand() {
61 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RandFn));
62 let ctx = interp(&wb);
63
64 let f = ctx.context.get_function("", "RAND").unwrap();
65 let fctx = ctx.function_context(None);
66 let result = f.eval_scalar(&[], &fctx).unwrap();
67 match result {
68 LiteralValue::Number(n) => assert!((0.0..1.0).contains(&n)),
69 _ => panic!("Expected a number"),
70 }
71 }
72
73 #[test]
74 fn test_randbetween_basic() {
75 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RandBetweenFn));
76 let ctx = interp(&wb);
77 let f = ctx.context.get_function("", "RANDBETWEEN").unwrap();
78 let fctx = ctx.function_context(None);
79 let lo = formualizer_parse::ASTNode::new(
81 formualizer_parse::ASTNodeType::Literal(LiteralValue::Int(1)),
82 None,
83 );
84 let hi = formualizer_parse::ASTNode::new(
85 formualizer_parse::ASTNodeType::Literal(LiteralValue::Int(3)),
86 None,
87 );
88 let args = vec![
89 ArgumentHandle::new(&lo, &ctx),
90 ArgumentHandle::new(&hi, &ctx),
91 ];
92 let v = f.eval_scalar(&args, &fctx).unwrap();
93 match v {
94 LiteralValue::Int(n) => assert!((1..=3).contains(&n)),
95 _ => panic!("Expected Int"),
96 }
97 }
98}
99
100#[derive(Debug)]
101pub struct RandBetweenFn;
102
103impl Function for RandBetweenFn {
104 func_caps!(VOLATILE);
105
106 fn name(&self) -> &'static str {
107 "RANDBETWEEN"
108 }
109 fn min_args(&self) -> usize {
110 2
111 }
112 fn arg_schema(&self) -> &'static [ArgSchema] {
113 &crate::builtins::utils::ARG_NUM_LENIENT_TWO[..]
114 }
115
116 fn eval_scalar<'a, 'b>(
117 &self,
118 args: &'a [ArgumentHandle<'a, 'b>],
119 ctx: &dyn FunctionContext,
120 ) -> Result<LiteralValue, ExcelError> {
121 let lo_v = args[0].value()?.into_owned();
123 let hi_v = args[1].value()?.into_owned();
124 let lo = match lo_v {
125 LiteralValue::Int(n) => n,
126 LiteralValue::Number(n) => n as i64,
127 _ => 0,
128 };
129 let hi = match hi_v {
130 LiteralValue::Int(n) => n,
131 LiteralValue::Number(n) => n as i64,
132 _ => 0,
133 };
134 if hi < lo {
135 return Err(ExcelError::new(formualizer_common::ExcelErrorKind::Num)
136 .with_message("RANDBETWEEN: hi < lo".to_string()));
137 }
138 let mut rng = ctx.rng_for_current(self.function_salt());
139 let n = if (hi - lo) as u64 == u64::MAX {
140 lo
141 } else {
142 rng.gen_range(lo..=hi)
143 };
144 Ok(LiteralValue::Int(n))
145 }
146}