use num::{PrimInt, Integer, range};
use std::iter::{Sum, Product};
use std::ops::{Mul};
use std::iter::Iterator;
use basics::pow;
use error::*;
use std::fmt::{Debug, Display};
pub fn factorial<T>(n: T) -> Result<T, MatholError>
where T: PrimInt + Integer + Product
{
if n < T::zero() {
return Err(MatholError::NegativeValueCause(NegativeValueError {
message: "Value for factorial must be a positive integer".to_string(),
}));
}
Ok(range(T::one(), n + T::one()).product())
}
pub fn permutation<T>(n: T, karr: Vec<T>) -> Result<T, MatholError>
where T: PrimInt + Integer + Product + Mul + Sum + Debug + Display
{
if n < T::one() {
return Err(MatholError::NegativeValueCause(NegativeValueError {
message: "Parameter n must be a positive integer!".to_string(),
}));
}
if karr.is_empty() {
return Err(MatholError::EmptyVecCause(EmptyVectorError {
message: "Parameter karr is an empty vector!".to_string(),
}));
}
let karr_2 = karr.clone();
let sum: T = karr.into_iter().sum();
if sum != n {
return Err(MatholError::SummationCause(SummationError {
message: format!("Number of total elements is {:?}, but the summed number of elements from the subset is {:?}", n, sum),
}));
}
let divisor = karr_2.into_iter().fold(T::one(), |prod, x| prod * factorial(x).unwrap());
Ok(factorial(n)? / divisor)
}
pub fn combination<T>(n: T, k: T) -> Result<T, MatholError>
where T: PrimInt + Integer + Product
{
if n < T::zero() {
return Err(MatholError::NegativeValueCause(NegativeValueError {
message: "Parameter n must be a positive integer!".to_string(),
}));
}
if k < T::zero() {
return Err(MatholError::NegativeValueCause(NegativeValueError {
message: "Parameter k must be a positive integer!".to_string(),
}));
}
if k > n {
return Err(MatholError::OutgrowCause(OutgrowError {
message: format!("k is bigger than n"),
}));
}
Ok(factorial(n)? / factorial(n - k)? / factorial(k)?)
}
pub fn combination_with_repetition<T>(n: T, k: T) -> Result<T, MatholError>
where T: PrimInt + Integer + Product
{
if n < T::zero() {
return Err(MatholError::NegativeValueCause(NegativeValueError {
message: "Parameter n must be a positive integer!".to_string(),
}));
}
if k < T::zero() {
return Err(MatholError::NegativeValueCause(NegativeValueError {
message: "Parameter k must be a positive integer!".to_string(),
}));
}
let m = n + k - T::one();
Ok(factorial(m)? / factorial(m - k)? / factorial(k)?)
}
pub fn variation<T>(n: T, k: T) -> Result<T, MatholError>
where T: PrimInt + Integer + Product
{
if n < T::zero() {
return Err(MatholError::NegativeValueCause(NegativeValueError {
message: "Parameter n must be a positive integer!".to_string(),
}));
}
if k < T::zero() {
return Err(MatholError::NegativeValueCause(NegativeValueError {
message: "Parameter k must be a positive integer!".to_string(),
}));
}
if k > n {
return Err(MatholError::OutgrowCause(OutgrowError {
message: format!("k is bigger than n"),
}));
}
Ok(factorial(n)? / factorial(n - k)?)
}
pub fn variation_with_repetition<T>(n: T, k: T) -> Result<T, MatholError>
where T: PrimInt + Integer
{
if n < T::zero() {
return Err(MatholError::NegativeValueCause(NegativeValueError {
message: "Parameter n must be a positive integer!".to_string(),
}));
}
if k < T::zero() {
return Err(MatholError::NegativeValueCause(NegativeValueError {
message: "Parameter k must be a positive integer!".to_string(),
}));
}
Ok(pow(n, k))
}