randcli/
eval.rs

1use crate::expr::Expr;
2use crate::prng::PRNG;
3
4extern crate anyhow;
5use anyhow::{bail, Result};
6
7macro_rules! assert_arity {
8    (= $arity:expr, $term:expr) => {
9        if $arity != $term.1.len() {
10            bail!("Arity Error: {} required {} args", $term.0, $arity);
11        }
12    };
13    (<= $arity:expr, $term:expr) => {
14        if $arity < $term.1.len() {
15            bail!("Arity Error: {} required <= {} args", $term.0, $arity);
16        }
17    };
18}
19
20pub fn eval(expr: &Expr) -> Result<f64> {
21    let mut rng = PRNG::new();
22    let mut ret: f64 = 0.0;
23    for t in expr.code.iter() {
24        match t.0.as_str() {
25            "seed" => {
26                assert_arity!(= 1, &t);
27                let state = t.1[0] as u64;
28                rng.setseed(state);
29            }
30            "int" | "floor" => {
31                assert_arity!(= 0, &t);
32                ret = ret.floor();
33            }
34            "round" => {
35                assert_arity!(= 0, &t);
36                ret = ret.round();
37            }
38            "uniform" => {
39                assert_arity!(<= 2, &t);
40                let args = fillarg(&t.1, &mut vec![0.0, 1.0]);
41                if args[0] >= args[1] {
42                    bail!("uniform required non-empty range");
43                }
44                ret = rng.uniform(args[0], args[1]);
45            }
46            "gauss" | "gaussian" | "normal" | "norm" => {
47                assert_arity!(<= 2, &t);
48                let args = fillarg(&t.1, &mut vec![0.0, 1.0]);
49                if args[1] <= 0.0 {
50                    bail!("variance for gauss must be positive");
51                }
52                ret = rng.gaussian(args[0], args[1]);
53            }
54            "exp" | "exponential" => {
55                assert_arity!(<= 1, &t);
56                let args = fillarg(&t.1, &mut vec![1.0]);
57                if args[0] <= 0.0 {
58                    bail!("lambda for exp must be positive");
59                }
60                ret = rng.exponential(args[0]);
61            }
62            "binom" => {
63                assert_arity!(= 2, &t);
64                let n = t.1[0] as usize;
65                let p = t.1[1];
66                if n as f64 != t.1[0] {
67                    bail!("n for binom must be unsigned int");
68                }
69                if p < 0.0 || p > 1.0 {
70                    bail!("p for binom must be `0 <= p <= 1`");
71                }
72                ret = rng.binom(n, p);
73            }
74            "bernoulli" => {
75                assert_arity!(<= 1, &t);
76                let args = fillarg(&t.1, &mut vec![0.5]);
77                let p = args[0];
78                if p < 0.0 || p > 1.0 {
79                    bail!("p for bernoulli must be `0 <= p <= 1`");
80                }
81                ret = rng.binom(1, p);
82            }
83            _ => {
84                bail!("Unknown function: {}", t.0);
85            }
86        }
87    }
88    Ok(ret)
89}
90
91fn fillarg(given: &Vec<f64>, args: &mut Vec<f64>) -> Vec<f64> {
92    let n = given.len();
93    let m = args.len();
94    for i in 0..n.min(m) {
95        args[i] = given[i];
96    }
97    args.to_vec()
98}