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