ranty 1.0.0

The Ranty procedural templating language
Documentation
use super::*;

pub fn rand(vm: &mut VM, (a, b): (i64, i64)) -> RantyStdResult {
    let n = vm.rng().next_i64(a, b);
    vm.cur_frame_mut().write(n);
    Ok(())
}

pub fn randf(vm: &mut VM, (a, b): (f64, f64)) -> RantyStdResult {
    let n = vm.rng().next_f64(a, b);
    vm.cur_frame_mut().write(n);
    Ok(())
}

pub fn rand_list(vm: &mut VM, (a, b, n): (i64, i64, usize)) -> RantyStdResult {
    let mut list = RantyList::new();
    let rng = vm.rng();
    for _ in 0..n {
        list.push(RantyValue::Int(rng.next_i64(a, b)));
    }
    vm.cur_frame_mut().write(list);
    Ok(())
}

pub fn randf_list(vm: &mut VM, (a, b, n): (f64, f64, usize)) -> RantyStdResult {
    let mut list = RantyList::new();
    let rng = vm.rng();
    for _ in 0..n {
        list.push(RantyValue::Float(rng.next_f64(a, b)));
    }
    vm.cur_frame_mut().write(list);
    Ok(())
}

pub fn alpha(vm: &mut VM, count: Option<usize>) -> RantyStdResult {
    const CHARS: &[u8] = b"abcdefghijklmnopqrstuvwxyz";
    let count = count.unwrap_or(1);
    let mut s = String::with_capacity(count);
    let rng = vm.rng();
    for _ in 0..count {
        let ch = CHARS[rng.next_usize(CHARS.len())] as char;
        s.push(ch);
    }
    vm.cur_frame_mut().write_frag(s.as_str());
    Ok(())
}

pub fn digh(vm: &mut VM, count: Option<usize>) -> RantyStdResult {
    const CHARS: &[u8] = b"0123456789abcdef";
    let count = count.unwrap_or(1);
    let mut s = String::with_capacity(count);
    let rng = vm.rng();
    for _ in 0..count {
        let ch = CHARS[rng.next_usize(CHARS.len())] as char;
        s.push(ch);
    }
    vm.cur_frame_mut().write_frag(s.as_str());
    Ok(())
}

pub fn dig(vm: &mut VM, count: Option<usize>) -> RantyStdResult {
    const CHARS: &[u8] = b"0123456789";
    let count = count.unwrap_or(1);
    let mut s = String::with_capacity(count);
    let rng = vm.rng();
    for _ in 0..count {
        let ch = CHARS[rng.next_usize(CHARS.len())] as char;
        s.push(ch);
    }
    vm.cur_frame_mut().write_frag(s.as_str());
    Ok(())
}

pub fn dignz(vm: &mut VM, count: Option<usize>) -> RantyStdResult {
    const CHARS: &[u8] = b"123456789";
    let count = count.unwrap_or(1);
    let mut s = String::with_capacity(count);
    let rng = vm.rng();
    for _ in 0..count {
        let ch = CHARS[rng.next_usize(CHARS.len())] as char;
        s.push(ch);
    }
    vm.cur_frame_mut().write_frag(s.as_str());
    Ok(())
}

pub fn rand_list_sum(
    vm: &mut VM,
    (value, n, variance): (RantyValue, i64, Option<f64>),
) -> RantyStdResult {
    if n <= 0 {
        return Err(RuntimeError {
            error_type: RuntimeErrorType::ArgumentError,
            description: Some("shred count must be greater than zero".to_owned()),
            stack_trace: None,
        });
    }

    let rng = vm.rng();

    match value {
        RantyValue::Int(m) => {
            let mut shreds = vec![];
            let variance = variance.unwrap_or_default().abs() as i64;
            let quotient = m / n;
            let remainder = m % n;

            let (head_chunk, tail_chunk) = (quotient + remainder, quotient);

            // Populate chunks
            for i in 0..n {
                shreds.push(if i == 0 { head_chunk } else { tail_chunk });
            }

            // Redistribute chunk size randomly
            for i in 0..n {
                let shift = rng.next_i64(0, variance + 1);
                let cell = shreds.get_mut(i as usize).unwrap();
                *cell -= shift;
                let cell = shreds.get_mut(((i + 1) % n) as usize).unwrap();
                *cell += shift;
            }

            vm.cur_frame_mut().write(shreds);
        }
        RantyValue::Float(m) => {
            let mut shreds = vec![];
            let variance = variance.unwrap_or_default().abs() as f64;
            let nf = n as f64;
            let quotient = m / nf;
            let remainder = m % nf;

            let (head_chunk, tail_chunk) = (quotient + remainder, quotient);

            // Populate chunks
            for i in 0..n {
                shreds.push(if i == 0 { head_chunk } else { tail_chunk });
            }

            // Redistribute chunk size randomly
            for i in 0..n {
                let shift = rng.next_f64(0.0, variance);
                let cell = shreds.get_mut(i as usize).unwrap();
                *cell -= shift;
                let cell = shreds.get_mut(((i + 1) % n) as usize).unwrap();
                *cell += shift;
            }

            vm.cur_frame_mut().write(shreds);
        }
        other => {
            return Err(RuntimeError {
                error_type: RuntimeErrorType::ArgumentError,
                description: Some(format!("cannot shred '{}' value", other.type_name())),
                stack_trace: None,
            })
        }
    }

    Ok(())
}

pub fn maybe(vm: &mut VM, p: Option<f64>) -> RantyStdResult {
    let b = vm.rng().next_bool(p.unwrap_or(0.5));
    vm.cur_frame_mut().write(b);
    Ok(())
}

pub fn pick(vm: &mut VM, list: RantyValue) -> RantyStdResult {
    let n = list.len();
    if n > 0 {
        let index = vm.rng().next_usize(n);
        let item = list.index_get(index as i64).into_runtime_result()?;
        vm.cur_frame_mut().write(item);
    }
    Ok(())
}

pub fn pickn(vm: &mut VM, (input, count): (RantyOrderedCollection, usize)) -> RantyStdResult {
    let rng = vm.rng();
    let len = input.len();
    let list = (0..count)
        .into_iter()
        .map(|_| {
            input
                .index_get(rng.next_usize(len).try_into().unwrap_or(i64::MAX))
                .into_runtime_result()
        })
        .collect::<RuntimeResult<RantyList>>()?;
    vm.cur_frame_mut().write(list);
    Ok(())
}

pub fn pick_sparse(vm: &mut VM, mut items: RequiredVarArgs<RantyValue>) -> RantyStdResult {
    let len_sum = items
        .iter()
        .map(|v| v.len())
        .fold(0usize, |acc, x| acc.saturating_add(x));

    let mut rem_sum = vm.rng().next_usize(len_sum);

    for item in items.drain(..) {
        let item_len = item.len();
        if rem_sum < item_len {
            vm.cur_frame_mut().write(if item.is_indexable() {
                item.index_get(rem_sum.try_into().unwrap_or(i64::MAX))
                    .into_runtime_result()?
            } else {
                item
            });
            break;
        }
        rem_sum = rem_sum.saturating_sub(item_len);
    }

    Ok(())
}