use num::{PrimInt, Integer, Float, Zero, One, range};
use std::iter::{Product};
use std::ops::{Sub};
use std::iter::Iterator;
use std::f64::consts::PI;
use basics::pow;
use basics::convert_trait::Convert;
use stochastics::probability::{factorial, combination};
use error::*;
pub fn binomial_distribution<T, U>(n: T, p: U) -> Result<Vec<f64>, MatholError>
where T: Zero + PrimInt + Integer + Product + Convert,
U: One + Sub + Float + Convert
{
if n < T::one() {
return Err(MatholError::NegativeValueCause(NegativeValueError {
message: "Parameter n must be a positive integer!".to_string(),
}));
}
if p < U::zero() || p > U::one() {
return Err(MatholError::RangeCause(RangeError {
message: "p must be in a range between 0 and 1!".to_string(),
}));
}
let q = U::one() - p;
let binomial = range(T::zero(), n + T::one()).fold(Vec::new(), |mut vec, x| {
let a = factorial(n).unwrap() / (factorial(x).unwrap() * factorial(n - x).unwrap());
let b = pow(p, x);
let c = pow(q, n - x);
let result = a.to_f64() * b.to_f64() * c.to_f64();
vec.push(result);
vec
});
Ok(binomial)
}
#[allow(non_snake_case)]
pub fn hypergeometric_distribution<T>(N: T, M: T, n: T) -> Result<Vec<f64>, MatholError>
where T: PrimInt + Integer + Product + Sub + Convert
{
if N < T::one() {
return Err(MatholError::NegativeValueCause(NegativeValueError {
message: "Parameter N must be a positive integer!".to_string(),
}));
}
if M < T::one() {
return Err(MatholError::NegativeValueCause(NegativeValueError {
message: "Parameter M must be a positive integer!".to_string(),
}));
}
if n < T::one() {
return Err(MatholError::NegativeValueCause(NegativeValueError {
message: "Parameter n must be a positive integer!".to_string(),
}));
}
if M > N {
return Err(MatholError::ComparisonCause(ComparisonError {
message: "Parameter M must be smaller than N!".to_string(),
}));
}
if n > N {
return Err(MatholError::ComparisonCause(ComparisonError {
message: "Parameter n must be smaller than N!".to_string(),
}));
}
let hypergeometric = range(T::zero(), n + T::one()).fold(Vec::new(), |mut vec, x| {
let a = combination(M, x).unwrap();
let b = combination(N - M, n - x).unwrap();
let c = combination(N, n).unwrap();
let result = a.to_f64() * b.to_f64() / c.to_f64();
vec.push(result);
vec
});
Ok(hypergeometric)
}
pub fn poisson_distribution<T>(my: T, x: T) -> Result<f64, MatholError>
where T: PrimInt + Integer + Product + Convert
{
if my <= T::zero() {
return Err(MatholError::NegativeValueCause(NegativeValueError {
message: "Parameter ยต must be a positive integer!".to_string(),
}));
}
if x < T::zero() {
return Err(MatholError::NegativeValueCause(NegativeValueError {
message: "Parameter x must be a positive Integer!".to_string(),
}));
}
let a = pow(my, x).to_f64();
let b = factorial(x)?.to_f64();
let c = (my.to_f64() * (-1.0)).exp();
Ok(a / b * c)
}
pub fn gaussian_distribution<T>(my: T, sigma: T, x: T) -> Result<f64, MatholError>
where T: Convert + Copy + Zero + PartialOrd
{
if sigma <= T::zero() {
return Err(MatholError::ComparisonCause(ComparisonError {
message: "Parameter \u{03c3} must be bigger than 0!".to_string(),
}));
}
let a = (2.0 * PI).sqrt() * sigma.to_f64();
let b = (x.to_f64() - my.to_f64()) / sigma.to_f64();
let c = -0.5 * pow(b, 2);
let d = c.exp();
Ok((1.0 / a) * d)
}
pub fn standard_distribution<T>(x: T) -> f64
where T: Convert
{
let a = (2.0 * PI).sqrt();
let b = -0.5 * pow(x.to_f64(), 2);
let c = b.exp();
(1.0 / a) * c
}
pub fn exponential_distribution<T>(lambda: T, x: T) -> Result<f64, MatholError>
where T: Zero + Convert + PartialOrd + Copy
{
if lambda <= T::zero() {
return Err(MatholError::ComparisonCause(ComparisonError {
message: "Parameter \u{03bb} must be bigger than 0!".to_string(),
}));
}
if x < T::zero() {
Ok(0.0)
} else {
let a = lambda.to_f64() * (-1.0) * x.to_f64();
Ok(lambda.to_f64() * a.exp())
}
}