use cas_attrs::builtin;
use crate::consts::TAU;
use crate::numerical::value::Value;
use crate::primitive::{complex, float_from_str, float, int};
use once_cell::sync::Lazy;
use rand::Rng;
use rug::{integer::Order, ops::Pow, rand::RandState, Complex, Float, Integer};
#[derive(Debug)]
pub struct Abs;
#[cfg_attr(feature = "numerical", builtin)]
impl Abs {
pub fn eval_static(v: Complex) -> Float {
v.abs().into_real_imag().0
}
}
#[derive(Debug)]
pub struct Bool;
#[cfg_attr(feature = "numerical", builtin)]
impl Bool {
pub fn eval_static(v: Value) -> bool {
v.is_truthy()
}
}
#[derive(Debug)]
pub struct Rand;
#[cfg_attr(feature = "numerical", builtin)]
impl Rand {
pub fn eval_static() -> Float {
let mut seed = Integer::new();
let mut digits = [0u128; 2]; rand::thread_rng().fill(&mut digits);
seed.assign_digits(&digits, Order::Lsf);
let mut rand_state = RandState::new();
rand_state.seed(&seed);
float(Float::random_bits(&mut rand_state))
}
}
pub fn partial_factorial(mut n: Integer, k: Integer) -> Integer {
let mut result = int(1);
while n > k {
result *= &n;
n -= 1;
}
result
}
#[derive(Debug)]
pub struct Factorial;
#[cfg_attr(feature = "numerical", builtin)]
impl Factorial {
pub fn eval_static(n: Float) -> Value {
if !n.is_integer() || n.is_sign_negative() {
Value::Float((n + 1u8).gamma())
} else {
let n_int = n.to_integer().unwrap();
if let Some(n) = n_int.to_u16() {
Value::Integer(int(Integer::factorial(u32::from(n))))
} else {
Value::Float((n + 1u8).gamma())
}
}
}
}
static GAMMA_P: Lazy<[Float; 9]> = Lazy::new(|| [
float_from_str("0.99999999999980993"),
float_from_str("676.5203681218851"),
float_from_str("-1259.1392167224028"),
float_from_str("771.32342877765313"),
float_from_str("-176.61502916214059"),
float_from_str("12.507343278686905"),
float_from_str("-0.13857109526572012"),
float_from_str("9.9843695780195716e-6"),
float_from_str("1.5056327351493116e-7"),
]);
static GAMMA_G: Lazy<Float> = Lazy::new(|| float(7));
#[derive(Debug)]
pub struct Gamma;
#[cfg_attr(feature = "numerical", builtin)]
impl Gamma {
pub fn eval_static(mut z: Complex) -> Complex {
if z.imag().is_zero() {
return complex(z.into_real_imag().0.gamma());
}
z -= 1;
let mut x = complex(&GAMMA_P[0]);
for (i, p) in GAMMA_P.iter().enumerate().skip(1) {
x += p / complex(&z + i);
}
let t = complex(complex(&z + &*GAMMA_G) + 0.5);
let tau_sqrt = float(&*TAU).sqrt();
let t_pow = (&t).pow(z + 0.5);
let exp_t = complex(t.as_neg().exp_ref());
x * t_pow * exp_t * tau_sqrt
}
}
#[derive(Debug)]
pub struct Lerp;
#[cfg_attr(feature = "numerical", builtin)]
impl Lerp {
pub fn eval_static(v1: Complex, v2: Complex, t: Float) -> Complex {
&v1 + (v2 - &v1) * t
}
}
#[derive(Debug)]
pub struct Invlerp;
#[cfg_attr(feature = "numerical", builtin)]
impl Invlerp {
pub fn eval_static(v1: Float, v2: Float, v: Float) -> Float {
(v - &v1) / (v2 - &v1)
}
}
#[derive(Debug)]
pub struct Min;
#[cfg_attr(feature = "numerical", builtin)]
impl Min {
pub fn eval_static(v1: Float, v2: Float) -> Float {
v1.min(&v2)
}
}
#[derive(Debug)]
pub struct Max;
#[cfg_attr(feature = "numerical", builtin)]
impl Max {
pub fn eval_static(v1: Float, v2: Float) -> Float {
v1.max(&v2)
}
}
#[derive(Debug)]
pub struct Clamp;
#[cfg_attr(feature = "numerical", builtin)]
impl Clamp {
pub fn eval_static(v: Float, min: Float, max: Float) -> Float {
v.clamp(&min, &max)
}
}
#[derive(Debug)]
pub struct Gcf;
#[cfg_attr(feature = "numerical", builtin)]
impl Gcf {
pub fn eval_static(a: Integer, b: Integer) -> Integer {
a.gcd(&b)
}
}
#[derive(Debug)]
pub struct Lcm;
#[cfg_attr(feature = "numerical", builtin)]
impl Lcm {
pub fn eval_static(a: Integer, b: Integer) -> Integer {
a.lcm(&b)
}
}
#[derive(Debug)]
pub struct Sign;
#[cfg_attr(feature = "numerical", builtin)]
impl Sign {
pub fn eval_static(v: Float) -> Float {
if v.is_zero() {
v
} else {
v.signum()
}
}
}
#[derive(Debug)]
pub struct Size;
#[cfg_attr(feature = "numerical", builtin)]
impl Size {
pub fn eval_static(v: Integer) -> Integer {
v.significant_bits().into()
}
}