use std::ops::{Add, Sub, Mul, Div, Neg};
pub(self) fn approx_eq(left: f64, right: f64) -> bool {
(left - right).abs() <= f64::EPSILON
}
#[derive(Debug, Clone)]
pub struct DomainError;
impl std::fmt::Display for DomainError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "evaluation error due to domain restriction")
}
}
#[derive(Debug, Clone)]
pub struct ConversionError;
impl std::fmt::Display for ConversionError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "conversion error due to invalid predicate")
}
}
#[derive(Copy, Clone)]
pub struct Angle {
radian_form: Option<f64>,
degree_form: Option<f64>
}
impl Angle {
pub fn into_degrees(value: f64) -> f64 {
value * 180.0 / std::f64::consts::PI
}
pub fn into_radians(value: f64) -> f64 {
value * std::f64::consts::PI / 180.0
}
pub fn from_radians(radian_value: f64) -> Self {
Angle {
radian_form: Some(radian_value),
degree_form: None
}
}
pub fn from_degrees(degree_value: f64) -> Self {
Angle {
radian_form: None,
degree_form: Some(degree_value)
}
}
pub fn to_radians(self) -> f64 {
match self.radian_form {
Some(in_radians) => in_radians,
None => Angle::into_radians(self.degree_form.unwrap())
}
}
pub fn to_degrees(self) -> f64 {
match self.degree_form {
Some(in_degrees) => in_degrees,
None => Angle::into_degrees(self.radian_form.unwrap())
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct Real {
inner: f64
}
impl Real {
pub const ONE: Real = Real { inner: 1.0 };
pub const ZERO: Real = Real { inner: 0.0 };
pub fn new(inner: f64) -> Self {
Real { inner }
}
pub fn value(&self) -> f64 {
self.inner
}
pub fn positive(&self) -> bool {
if self.inner > 0.0 {
return true;
}
false
}
pub fn negative(&self) -> bool {
if self.inner < 0.0 {
return true;
}
false
}
pub fn abs(&self) -> Self {
Real::new(self.inner.abs())
}
pub fn ceil(&self) -> Self {
Real::new(self.inner.ceil())
}
pub fn floor(&self) -> Self {
Real::new(self.inner.floor())
}
pub fn sin(&self) -> Self {
Real::new(self.inner.sin())
}
pub fn cos(&self) -> Self {
Real::new(self.inner.cos())
}
pub fn tan(&self) -> Self {
Real::new(self.inner.tan())
}
pub fn arcsin(&self) -> Result<Angle, DomainError> {
if self.inner < -1.0 || self.inner > 1.0 {
return Err(DomainError);
}
Ok(Angle::from_radians(self.inner.asin()))
}
pub fn arccos(&self) -> Result<Angle, DomainError> {
if self.inner < -1.0 || self.inner > 1.0 {
return Err(DomainError);
}
Ok(Angle::from_radians(self.inner.acos()))
}
pub fn arctan(&self) -> Angle {
Angle::from_radians(self.inner.atan())
}
pub fn sinh(&self) -> Self {
Real::new(self.inner.sinh())
}
pub fn cosh(&self) -> Self {
Real::new(self.inner.cosh())
}
pub fn tanh(&self) -> Self {
Real::new(self.inner.tanh())
}
pub fn arcsinh(&self) -> Angle {
Angle::from_radians(self.inner.asinh())
}
pub fn arccosh(&self) -> Result<Angle, DomainError> {
if self.inner < 1.0 {
return Err(DomainError);
}
Ok(Angle::from_radians(self.inner.acosh()))
}
pub fn arctanh(&self) -> Result<Angle, DomainError> {
if self.inner <= -1.0 || self.inner >= 1.0 {
return Err(DomainError);
}
Ok(Angle::from_radians(self.inner.atanh()))
}
pub fn exp(&self) -> Self {
Real::new(self.inner.exp())
}
pub fn pow(&self, power: Self) -> Self {
Real::new(self.inner.powf(power.inner))
}
pub fn powf(&self, power: f64) -> Result<Self, DomainError> {
if self.negative() {
return Err(DomainError);
}
Ok(Real::new(self.inner.powf(power)))
}
pub fn powi(&self, power: i64) -> Self {
Real::new(self.inner.powf(power as f64))
}
pub fn squared(&self) -> Self {
self.powi(2)
}
pub fn sqrt(&self) -> Result<Self, DomainError> {
self.powf(0.5)
}
pub fn ln(&self) -> Result<Self, DomainError> {
if self.inner <= 0.0 {
return Err(DomainError);
}
Ok(Real::new(self.inner.ln()))
}
pub fn log(&self, base: Real) -> Result<Self, DomainError> {
if self.inner <= 0.0 || base.inner <= 0.0 {
return Err(DomainError);
}
Ok(Real::new(self.inner.log(base.inner)))
}
pub fn logf(&self, base: f64) -> Result<Self, DomainError> {
if self.inner <= 0.0 || base <= 0.0 {
return Err(DomainError);
}
Ok(Real::new(self.inner.log(base)))
}
pub fn logi(&self, base: i64) -> Result<Self, DomainError> {
if self.inner <= 0.0 || base <= 0 {
return Err(DomainError);
}
Ok(Real::new(self.inner.log(base as f64)))
}
pub fn to_complex(&self) -> Complex {
Complex::new(self.inner, 0.0)
}
}
impl std::fmt::Display for Real {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.inner)
}
}
impl PartialEq for Real {
fn eq(&self, other: &Self) -> bool {
approx_eq(self.inner, other.inner)
}
}
impl Eq for Real {}
impl PartialOrd for Real {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Real {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
if self.inner < other.inner {
return std::cmp::Ordering::Less;
}
if self.inner > other.inner {
return std::cmp::Ordering::Greater;
}
std::cmp::Ordering::Equal
}
}
impl Add<Self> for Real {
type Output = Self;
fn add(self, other: Self) -> Self {
Real::new(self.inner + other.inner)
}
}
impl Sub<Self> for Real {
type Output = Self;
fn sub(self, other: Self) -> Self {
Real::new(self.inner - other.inner)
}
}
impl Mul<Self> for Real {
type Output = Self;
fn mul(self, other: Self) -> Self {
Real::new(self.inner * other.inner)
}
}
impl Div<Self> for Real {
type Output = Self;
fn div(self, other: Self) -> Self {
Real::new(self.inner / other.inner)
}
}
impl Neg for Real {
type Output = Self;
fn neg(self) -> Self {
Real::new(-self.inner)
}
}
macro_rules! impl_real_from_primitive {
($t:ty) => {
impl From<$t> for Real {
fn from(value: $t) -> Self {
Real::new(value as f64)
}
}
}
}
impl_real_from_primitive![u8];
impl_real_from_primitive![u16];
impl_real_from_primitive![u32];
impl_real_from_primitive![u64];
impl_real_from_primitive![i8];
impl_real_from_primitive![i16];
impl_real_from_primitive![i32];
impl_real_from_primitive![i64];
impl_real_from_primitive![f32];
impl_real_from_primitive![f64];
#[derive(Copy, Clone, Debug)]
pub struct Complex {
real: Real,
imag: Real
}
impl Complex {
pub const ZERO: Complex = Complex { real: Real::ZERO, imag: Real::ZERO };
pub const ONE: Complex = Complex { real: Real::ONE, imag: Real::ZERO };
pub const I: Complex = Complex { real: Real::ZERO, imag: Real::ONE };
pub fn new(real: f64, imag: f64) -> Self {
Self {
real: Real::new(real),
imag: Real::new(imag)
}
}
pub fn from_polar(r: f64, theta: Angle) -> Self {
let as_radians = theta.to_radians();
Self {
real: Real::new(r*as_radians.cos()),
imag: Real::new(r*as_radians.sin())
}
}
pub fn is_real(&self) -> bool {
if approx_eq(self.imag.inner, 0.0) {
return true;
}
false
}
pub fn is_imaginary(&self) -> bool {
if approx_eq(self.real.inner, 0.0) {
return true;
}
false
}
pub fn conjugate(&self) -> Complex {
Complex::new(self.real.inner, -self.imag.inner)
}
pub fn abs(&self) -> Complex {
let norm = (self.real*self.real + self.imag*self.imag).sqrt();
Complex::new(norm.unwrap().inner, 0.0)
}
pub fn exp(&self) -> Complex {
let real_exp = Complex::new(self.real.exp().inner, 0.0);
let imag_exp = Complex::new(self.imag.cos().inner, self.imag.sin().inner);
real_exp*imag_exp
}
pub fn sin(&self) -> Complex {
let power = Complex::I * *self;
let num = power.exp() - (-power).exp();
let den = Complex::new(0.0, 2.0);
num / den
}
pub fn cos(&self) -> Complex {
let power = Complex::I * *self;
let num = power.exp() + (-power).exp();
let den = Complex::new(2.0, 0.0);
num / den
}
pub fn tan(&self) -> Complex {
self.sin() / self.cos()
}
pub fn arcsin(&self) -> Complex {
-Complex::I * (*self*Complex::I + (Complex::ONE - self.squared()).sqrt()).ln()
}
pub fn arccos(&self) -> Complex {
-Complex::I * (*self + (self.squared() - Complex::ONE).sqrt()).ln()
}
pub fn arctan(&self) -> Complex {
let coefficient = -Complex::I / Real::new(2.0).to_complex();
let num = Complex::I - *self;
let den = Complex::I + *self;
coefficient * (num / den).ln()
}
pub fn sinh(&self) -> Complex {
(self.exp() - ((-*self).exp())) / Real::new(2.0).to_complex()
}
pub fn cosh(&self) -> Complex {
(self.exp() + ((-*self).exp())) / Real::new(2.0).to_complex()
}
pub fn tanh(&self) -> Complex {
(self.exp() - (-*self).exp()) / (self.exp() + (-*self).exp())
}
pub fn arcsinh(&self) -> Complex {
(*self + (self.squared() + Complex::ONE).sqrt()).ln()
}
pub fn arccosh(&self) -> Complex {
(*self + (self.squared() - Complex::ONE).sqrt()).ln()
}
pub fn arctanh(&self) -> Result<Complex, DomainError> {
if self.norm() >= Real::ONE {
return Err(DomainError);
}
let num = Complex::ONE - *self;
let den = Complex::ONE + *self;
Ok(Real::new(0.5).to_complex() * (num / den).ln())
}
pub fn ln(&self) -> Complex {
if self.is_real() {
return Complex::new(self.real.ln().unwrap().inner, 0.0);
}
Complex::new(self.norm().ln().unwrap().inner, self.azimuthal().to_radians())
}
pub fn norm(&self) -> Real {
let length = (self.real.squared() + self.imag.squared()).sqrt();
Real::new(length.unwrap().inner)
}
pub fn azimuthal(&self) -> Angle {
if approx_eq(self.imag.inner, 0.0) {
if self.real > Real::ZERO {
return Angle::from_radians(0.0);
}
return Angle::from_radians(std::f64::consts::PI);
}
if approx_eq(self.real.inner, 0.0) {
if self.imag > Real::ZERO {
eprintln!("{}, {}", self.real.inner, approx_eq(self.real.inner, 0.0));
eprintln!("{}, norm: {}", self, self.norm());
return Angle::from_radians(std::f64::consts::FRAC_PI_2);
}
return Angle::from_radians(3.0*std::f64::consts::FRAC_PI_2);
}
if self.real > Real::ZERO && self.imag > Real::ZERO {
return (self.imag / self.norm()).arcsin().unwrap();
}
if self.real < Real::ZERO && self.imag > Real::ZERO {
let angle = std::f64::consts::PI - (self.imag / self.norm()).arcsin().unwrap().to_radians();
return Angle::from_radians(angle);
}
if self.real < Real::ZERO && self.imag < Real::ZERO {
let angle = std::f64::consts::PI + (-self.imag / self.norm()).arcsin().unwrap().to_radians();
return Angle::from_radians(angle);
}
if self.real > Real::ZERO && self.imag < Real::ZERO {
return (self.imag / self.norm()).arcsin().unwrap();
}
return Angle::from_radians(0.0);
}
pub fn squared(&self) -> Complex {
*self * *self
}
pub fn sqrt(&self) -> Complex {
let root_r = self.norm().sqrt().unwrap().to_complex();
let theta = self.azimuthal().to_radians();
root_r * Complex::new(((1.0 + theta.cos()) / 2.0).sqrt(), ((1.0 - theta.cos()) / 2.0).sqrt())
}
}
impl std::fmt::Display for Complex {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} + {}i", self.real, self.imag)
}
}
impl PartialEq for Complex {
fn eq(&self, other: &Self) -> bool {
let reals_eq = approx_eq(self.real.inner, other.real.inner);
let imags_eq = approx_eq(self.imag.inner, other.imag.inner);
reals_eq && imags_eq
}
}
impl Eq for Complex {}
impl Add<Self> for Complex {
type Output = Self;
fn add(self, other: Self) -> Self {
let real = (self.real + other.real).inner;
let imag = (self.imag + other.imag).inner;
Complex::new(real, imag)
}
}
impl Sub<Self> for Complex {
type Output = Self;
fn sub(self, other: Self) -> Self {
let real = (self.real - other.real).inner;
let imag = (self.imag - other.imag).inner;
Complex::new(real, imag)
}
}
impl Mul<Self> for Complex {
type Output = Self;
fn mul(self, other: Self) -> Self {
let real_part = self.real*other.real - self.imag*other.imag;
let imag_part = self.real*other.imag + self.imag*other.real;
Complex::new(real_part.inner, imag_part.inner)
}
}
impl Div<Self> for Complex {
type Output = Self;
fn div(self, other: Self) -> Self {
let real_num = self.real*other.real + self.imag*other.imag;
let imag_num = self.imag*other.real - self.real*other.imag;
let den = other.real.squared() + other.imag.squared();
Complex::new((real_num / den).inner, (imag_num / den).inner)
}
}
impl Neg for Complex {
type Output = Self;
fn neg(self) -> Self {
Complex::new(-self.real.inner, -self.imag.inner)
}
}
impl TryFrom<Complex> for Real {
type Error = ConversionError;
fn try_from(value: Complex) -> Result<Real, ConversionError> {
if value.is_real() {
return Ok(value.real);
}
Err(ConversionError)
}
}
#[cfg(test)]
mod tests {
use super::*;
mod real {
use super::*;
#[test]
fn addition() {
assert_eq!(Real::ONE + Real::new(2.0), Real::new(3.0));
assert_eq!(Real::ONE + Real::new(-1.0), Real::ZERO);
}
#[test]
fn subtraction() {
assert_eq!(Real::ONE - Real::ONE, Real::ZERO);
assert_eq!(Real::new(2.0) - Real::new(3.0), Real::new(-1.0));
}
#[test]
fn multiplication() {
assert_eq!(Real::new(3.0) * Real::new(2.0), Real::new(6.0));
assert_eq!(Real::ONE * Real::new(3.0), Real::new(3.0));
}
#[test]
fn division() {
assert_eq!(Real::new(3.0) / Real::new(2.0), Real::new(1.5));
assert_eq!(Real::new(12.0) / Real::ONE, Real::new(12.0));
}
}
mod complex {
use super::*;
#[test]
fn addition() {
assert_eq!(Complex::ONE + Complex::new(12.0, 23.0), Complex::new(13.0, 23.0));
assert_eq!(Complex::I + Complex::new(12.0, 23.0), Complex::new(12.0, 24.0));
}
#[test]
fn subtraction() {
assert_eq!(Complex::ONE - Complex::new(12.0, 23.0), Complex::new(-11.0, -23.0));
assert_eq!(Complex::I - Complex::new(12.0, 23.0), Complex::new(-12.0, -22.0));
}
#[test]
fn multiplication() {
assert_eq!(Complex::ONE*Complex::new(12.0, 1.0), Complex::new(12.0, 1.0));
assert_eq!(Complex::new(1.0, 2.0)*Complex::new(3.0, 4.0), Complex::new(-5.0, 10.0));
}
#[test]
fn division() {
assert_eq!(Complex::new(4.0, 2.0) / Complex::new(2.0, 0.0), Complex::new(2.0, 1.0));
assert_eq!(Complex::new(4.0, 2.0) / Complex::new(0.0, 2.0), Complex::new(1.0, -2.0));
}
}
}