1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
use r_derive::builtin;
use rand::distributions::{Distribution, Uniform};
use rand::Rng;
use crate::callable::core::*;
use crate::error::Error;
use crate::formals;
use crate::lang::*;
use crate::object::*;
/// Uniform Random Number Generation
///
/// Create a vector of uniformly distributed random numbers.
///
/// # In-Language
///
/// ## Usage
///
/// ```custom,{class=r}
/// runif(n = 1, min = 0, max = 1)
/// ```
///
/// ## Arguments
///
/// * `n`: The number of values to generate
/// * `min`,`max`: The range in which values should be generated
///
/// ## Examples
///
/// Produce a vector of values between `0` and `1`:
///
/// ```custom,{class=r-repl}
/// runif(3)
/// ```
///
/// Modify range with a minimum and maximum:
///
/// ```custom,{class=r-repl}
/// runif(10, min = 10, max = 20)
/// ```
///
#[doc(alias = "runif")]
#[builtin(sym = "runif")]
#[derive(Debug, Clone, PartialEq)]
pub struct PrimitiveRunif;
formals!(PrimitiveRunif, "(n = 1, min = 0, max = 1)");
impl Callable for PrimitiveRunif {
    fn call(&self, args: ExprList, stack: &mut CallStack) -> EvalResult {
        use Error::ArgumentInvalid;
        let (vals, _) = self.match_arg_exprs(args, stack)?;
        let vals = force_promises(vals, stack)?;
        let mut vals = Obj::List(List::from(vals));
        let n: i32 = vals.try_get_named("n")?.try_into()?;
        let min: Vec<f64> = vals.try_get_named("min")?.try_into()?;
        let max: Vec<f64> = vals.try_get_named("max")?.try_into()?;
        let len = std::cmp::max(min.len(), max.len());
        let mut rng = rand::thread_rng();
        // TODO: perhaps these branches can be unified by creating a
        // run-length-encoding of the iterator?
        // special case when both min and max are length 1, sampling
        // all random numbers at once from the same distribution
        if len == 1 {
            let min = min
                .first()
                .map_or(ArgumentInvalid(String::from("min")).into(), Ok)?;
            let max = max
                .first()
                .map_or(ArgumentInvalid(String::from("max")).into(), Ok)?;
            let between = Uniform::from(*min..=*max);
            Ok(Obj::Vector(Vector::from(
                (1..=n)
                    .map(|_| between.sample(&mut rng))
                    .collect::<Vec<f64>>(),
            )))
        // otherwise we need to zip through mins and maxes to get distributions
        } else {
            Ok(Obj::Vector(Vector::from(
                min.into_iter()
                    .cycle()
                    .zip(max.into_iter().cycle())
                    .take(len)
                    .map(|(min, max)| rng.gen_range(min..=max))
                    .collect::<Vec<f64>>(),
            )))
        }
    }
}