mod combinatorics;
mod linear_algebra;
mod logic;
mod number_theory;
use std::rc::Rc;
use num::Signed;
use savage_macros::functions;
use crate::expression::{Expression, Function as FunctionImplementation, Integer, Matrix};
pub(crate) type NonNegativeInteger = Integer;
pub(crate) type PositiveInteger = Integer;
pub(crate) type SquareMatrix = Matrix;
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum Parameter {
Expression,
Integer,
NonNegativeInteger,
PositiveInteger,
Rational,
Complex,
Vector,
Matrix,
SquareMatrix,
Boolean,
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct Metadata {
pub name: &'static str,
pub description: &'static str,
pub parameters: &'static [Parameter],
pub examples: &'static [(&'static str, &'static str)],
pub categories: &'static [&'static str],
}
pub struct Function {
pub metadata: Metadata,
pub implementation: Rc<FunctionImplementation>,
}
fn wrap_proxy(
parameters: &'static [Parameter],
proxy: impl Fn(&[Expression]) -> Result<Expression, Expression> + 'static,
) -> Rc<FunctionImplementation> {
use crate::evaluate::Error::*;
use crate::expression::Type::{Arithmetic, Boolean as Bool, Unknown};
use Parameter::*;
Rc::new(move |expression, arguments, _| {
if arguments.len() != parameters.len() {
return Err(InvalidNumberOfArguments {
expression: expression.clone(),
min_number: parameters.len(),
max_number: parameters.len(),
given_number: arguments.len(),
});
}
for (argument, parameter) in arguments.iter().zip(parameters) {
if let Bool(None) | Arithmetic | Unknown = argument.typ() {
if *parameter != Expression {
return Ok(expression.clone());
}
}
let mut argument_valid = true;
match parameter {
NonNegativeInteger => {
if let Ok(integer) = crate::expression::Integer::try_from(argument.clone()) {
argument_valid = !integer.is_negative();
}
}
PositiveInteger => {
if let Ok(integer) = crate::expression::Integer::try_from(argument.clone()) {
argument_valid = integer.is_positive();
}
}
SquareMatrix => {
if let Ok(matrix) = crate::expression::Matrix::try_from(argument.clone()) {
argument_valid = matrix.is_square() || matrix.is_empty();
}
}
_ => (),
}
if !argument_valid {
return Err(InvalidArgument {
expression: expression.clone(),
argument: argument.clone(),
});
}
}
proxy(arguments).map_err(|argument| InvalidArgument {
expression: expression.clone(),
argument,
})
})
}
pub fn functions() -> Vec<Function> {
functions!(
logic::and,
combinatorics::factorial,
linear_algebra::determinant,
number_theory::is_prime,
number_theory::nth_prime,
number_theory::prime_pi,
)
}
pub fn function_expression(name: &str) -> Option<Expression> {
for function in functions() {
if function.metadata.name == name {
return Some(Expression::Function(
function.metadata.name.to_owned(),
function.implementation,
));
}
}
None
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use crate::expression::Expression;
use crate::functions::functions;
#[track_caller]
fn t(expression: &str, result: &str) {
assert_eq!(
expression
.parse::<Expression>()
.unwrap()
.evaluate(HashMap::new())
.unwrap()
.to_string(),
result,
);
}
#[test]
fn examples() {
for function in functions() {
for (expression, result) in function.metadata.examples {
t(expression, result);
}
}
}
}