use std::ops::{
	Add,
	Sub,
	Mul,
	Div
};
use std::sync::Arc;
pub trait Rational: Clone + Copy + Add<Output=Self> + Sub<Output=Self> + Mul<Output=Self> + Div<Output=Self> + From<f64> + PartialEq + std::fmt::Debug {
	fn pow(self, rhs: Self) -> Self;
}
impl Rational for f64 {
	fn pow(self, rhs: Self) -> Self {
		self.powf(rhs)
	}
}
#[derive(Clone)]
pub struct Function<T = f64> where T: Rational, {
	function: Arc<dyn Fn(T) -> T>
}
unsafe impl<T: Rational> Send for Function<T> {}
impl<T: Rational + 'static> Function<T> {
	pub fn new(value: f64) -> Self {
		Self {
			function: Arc::new(move |_x| value.into())
		}
	}
	
	pub fn pow(self, rhs: Self) -> Self {
		Self { function: Arc::new(move |x| (self.function)(x).pow((rhs.function)(x))) }
	}
	
	pub fn call(&self, x: T) -> T {
		(self.function)(x)
	}
}
impl<T: Rational + 'static> std::fmt::Debug for Function<T> {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		write!(
			f,
			"f(-2) {:?}|f(-1) {:?}|f(0) {:?}|f(1) {:?}|f(2) {:?}",
			self.call((-2.).into()),
			self.call((-1.).into()),
			self.call((0.).into()),
			self.call((1.).into()),
			self.call((2.).into()),
		)
	}
}
impl<T: Rational> From<f64> for Function<T> {
	fn from(x: f64) -> Self {
		Self {
			function: Arc::new(move |_x| x.into())
		}
	}
}
impl<T: Rational> Default for Function<T> {
	fn default() -> Self {
		Self {
			function: Arc::new(|x| x)
		}
	}
}
impl<T: Rational + 'static> Add for Function<T> {
	type Output = Self;
	fn add(self, rhs: Self) -> Self {
		Self { function: Arc::new(move |x| (self.function)(x)+(rhs.function)(x)) }
	}
}
impl<T: Rational + 'static> Sub for Function<T> {
	type Output = Self;
	fn sub(self, rhs: Self) -> Self {
		Self { function: Arc::new(move |x| (self.function)(x)-(rhs.function)(x)) }
	}
}
impl<T: Rational + 'static> Mul for Function<T> {
	type Output = Self;
	fn mul(self, rhs: Self) -> Self {
		Self { function: Arc::new(move |x| (self.function)(x)*(rhs.function)(x)) }
	}
}
impl<T: Rational + 'static> Div for Function<T> {
	type Output = Self;
	fn div(self, rhs: Self) -> Self {
		Self { function: Arc::new(move |x| (self.function)(x)/(rhs.function)(x)) }
	}
}
impl<T: Rational> PartialEq for Function<T> {
	fn eq(&self, rhs: &Self) -> bool {
		let mut roughly_equal = true;
		for iter in -10..=10 {
			if (self.function)((iter as f64).into()) != (rhs.function)((iter as f64).into()) {roughly_equal = false;}
		}
		roughly_equal
	}
}
#[cfg(test)]
mod tests {
    use super::*;
    
    fn define_shit() -> (Function, Function, Function, Function) {
    	return (Function::default(), Function::new(3.), Function::new(5.), Function::from(3.))
    }
		
		#[test]
		fn test_equality() {
			let (y, f ,g , h) = define_shit();
			assert_eq!(
				f,
				h
			);
			assert_eq!(
				Function::from(4.),
				Function::<f64>::new(4.),
			);
			let (y, f ,g , h) = define_shit();
			assert_eq!(
				Function::from(1.)/y.clone(),
				(f/h)/y,
			);
		}
		
		#[test]
		fn test_add() {
			let (y, f ,g , h) = define_shit();
			assert_eq!(
				g+Function::new(4.),
				f+Function::new(6.),
			);
			let (y, f ,g , h) = define_shit();
			assert_eq!(
				f+g.clone(),
				g+h
			);
		}
		
		#[test]
		fn test_sub() {
			let (y, f ,g , h) = define_shit();
			assert_eq!(
				g-h,
				Function::new(2.)
			);
			let (y, f ,g , h) = define_shit();
			assert_eq!(
				f-g,
				Function::new(-2.)
			);
		}
		
		#[test]
		fn test_mul() {
			let (y, f ,g , h) = define_shit();
			assert_eq!(
				g.clone()*h,
				f*g
			);
			let (y, f ,g , h) = define_shit();
			assert_eq!(
				f*g,
				Function::new(15.)
			);
		}
		
		#[test]
		fn test_div() {
			let (y, f ,g , h) = define_shit();
			assert_eq!(
				f/g,
				Function::new(3./5.),
			);
			assert_eq!(
				y/Function::new(1.),
				Function::default()
			);
		}
		
		#[test]
		fn test_call() {
			let (y, f, g, h) = define_shit();
			assert_eq!(
				g.call(2.),
				5.
			);
			assert_eq!(
				h.call(10000.47654),
				3.
			);
			assert_eq!(
				y.call(536.5),
				536.5
			);
		}
}