diff --git a/fractional/Cargo.toml b/fractional/Cargo.toml new file mode 100644 index 0000000..44b056f --- /dev/null +++ b/fractional/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "fractional" +version = "0.1.0" +authors = ["Georg Hopp "] +edition = "2018" + +[dependencies] +lazy_static = "1.4.0" diff --git a/fractional/src/fractional.rs b/fractional/src/fractional.rs new file mode 100644 index 0000000..27ce83a --- /dev/null +++ b/fractional/src/fractional.rs @@ -0,0 +1,224 @@ +// +// Some code to support fractional numbers for full precision rational number +// calculations. +// TODO +// - maybe this could be build as a generic for all integral numbers. +// (Question, how can I assure that it is build from integral numbers? +// +// Georg Hopp +// +// Copyright © 2019 Georg Hopp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +use std::cmp::Ordering; +use std::ops::{Add,Sub,Neg,Mul,Div}; +use std::fmt; +use std::convert::{TryFrom, TryInto}; +use std::num::TryFromIntError; + +#[derive(Debug, Eq, Clone, Copy)] +pub struct Fractional (pub i64, pub i64); + +#[inline] +fn hcf(x :i64, y :i64) -> i64 { + match y { + 0 => x, + _ => hcf(y, x % y), + } +} + +impl Fractional { + #[inline] + pub fn gcd(self, other: Self) -> i64 { + let Fractional(_, d1) = self; + let Fractional(_, d2) = other; + (d1 * d2) / hcf(d1, d2) + } + + #[inline] + pub fn reduce(self) -> Self { + let Fractional(n, d) = self; + Self(n / hcf(n, d), d / hcf(n, d)) + } + + #[inline] + pub fn numerator(self) -> i64 { + self.0 + } + + #[inline] + pub fn denominator(self) -> i64 { + self.1 + } +} + +impl fmt::Display for Fractional { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({}/{})", self.0, self.1) + } +} + +impl From for Fractional { + fn from(x: i64) -> Self { + Self(x, 1) + } +} + +impl TryFrom for Fractional { + type Error = &'static str; + + fn try_from(x: usize) -> Result { + let v = i64::try_from(x); + match v { + Err(_) => Err("Conversion from usize to i32 failed"), + Ok(_v) => Ok(Self(_v, 1)), + } + } +} + +pub fn from_vector(xs: &Vec) -> Vec { + xs.iter().map(|x| Fractional(*x, 1)).collect() +} + +impl TryInto for Fractional { + type Error = TryFromIntError; + + fn try_into(self) -> Result { + let n :i32 = self.0.try_into()?; + let d :i32 = self.1.try_into()?; + Ok(f64::from(n) / f64::from(d)) + } +} + +impl TryInto<(i32, i32)> for Fractional { + type Error = TryFromIntError; + + fn try_into(self) -> Result<(i32, i32), Self::Error> { + let a :i32 = (self.0 / self.1).try_into()?; + let b :i32 = (self.0 % self.1).try_into()?; + Ok((a, b)) + } +} + +impl PartialEq for Fractional { + fn eq(&self, other: &Self) -> bool { + let Fractional(n1, d1) = self; + let Fractional(n2, d2) = other; + n1 * (self.gcd(*other) / d1) == n2 * (self.gcd(*other) / d2) + } +} + +impl PartialOrd for Fractional { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Fractional { + fn cmp(&self, other: &Self) -> Ordering { + let Fractional(n1, d1) = self; + let Fractional(n2, d2) = other; + let x = n1 * (self.gcd(*other) / d1); + let y = n2 * (self.gcd(*other) / d2); + x.cmp(&y) + } +} + +impl Add for Fractional { + type Output = Self; + + fn add(self, other: Self) -> Self { + let Fractional(n1, d1) = self; + let Fractional(n2, d2) = other; + let n = n1 * (self.gcd(other) / d1) + n2 * (self.gcd(other) / d2); + Self(n, self.gcd(other)).reduce() + } +} + +impl Sub for Fractional { + type Output = Self; + + fn sub(self, other: Self) -> Self { + let Fractional(n1, d1) = self; + let Fractional(n2, d2) = other; + let n = n1 * (self.gcd(other) / d1) - n2 * (self.gcd(other) / d2); + Self(n, self.gcd(other)).reduce() + } +} + +impl Neg for Fractional { + type Output = Self; + + fn neg(self) -> Self { + let Fractional(n, d) = self; + Self(-n, d).reduce() + } +} + +impl Mul for Fractional { + type Output = Self; + + fn mul(self, other :Self) -> Self { + let Fractional(n1, d1) = self; + let Fractional(n2, d2) = other; + Self(n1 * n2, d1 * d2).reduce() + } +} + +impl Div for Fractional { + type Output = Self; + + fn div(self, other: Self) -> Self { + let Fractional(n, d) = other; + self * Fractional(d, n) + } +} + + /* some stuff that could be tested... + let x = Fractional(1, 3); + let y = Fractional(1, 6); + + println!( + "Greatest common denominator of {} and {}: {}", x, y, x.gcd(y)); + println!("Numerator of {}: {}", x, x.numerator()); + println!("Denominator of {}: {}", x, x.denominator()); + assert_eq!(Fractional(1, 3), Fractional(2, 6)); + assert_eq!(Fractional(1, 3), Fractional(1, 3)); + assert_eq!(y < x, true); + assert_eq!(y > x, false); + assert_eq!(x == y, false); + assert_eq!(x == x, true); + assert_eq!(x + y, Fractional(1, 2)); + println!("{} + {} = {}", x, y, x + y); + assert_eq!(x - y, Fractional(1, 6)); + println!("{} - {} = {}", x, y, x - y); + assert_eq!(y - x, Fractional(-1, 6)); + println!("{} - {} = {}", y, x, y - x); + assert_eq!(-x, Fractional(-1, 3)); + println!("-{} = {}", x, -x); + assert_eq!(x * y, Fractional(1, 18)); + println!("{} * {} = {}", x, y, x * y); + assert_eq!(x / y, Fractional(2, 1)); + println!("{} / {} = {}", x, y, x / y); + assert_eq!(y / x, Fractional(1, 2)); + println!("{} / {} = {}", y, x, y / x); + + println!("Fractional from 3: {}", Fractional::from(3)); + let z :f64 = Fractional::into(x); + println!("Floating point of {}: {}", x, z); + let (d, r) = Fractional::into(x); + println!("(div, rest) of {}: ({}, {})", x, d, r); + */ + diff --git a/fractional/src/lib.rs b/fractional/src/lib.rs new file mode 100644 index 0000000..dc6389a --- /dev/null +++ b/fractional/src/lib.rs @@ -0,0 +1,26 @@ +// +// Lib for fractional calculations. +// +// Georg Hopp +// +// Copyright © 2019 Georg Hopp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +extern crate lazy_static; + +pub mod fractional; +pub mod trigonometry; + +use fractional::{Fractional}; diff --git a/fractional/src/main.rs b/fractional/src/main.rs new file mode 100644 index 0000000..a4e6d23 --- /dev/null +++ b/fractional/src/main.rs @@ -0,0 +1,113 @@ +// +// Test our fractional crate / module... +// +// Georg Hopp +// +// Copyright © 2019 Georg Hopp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +use std::convert::{TryFrom, TryInto}; +use std::num::TryFromIntError; +use std::f64::consts::PI as FPI; + +use fractional::fractional::{Fractional, from_vector}; +use fractional::trigonometry::{sin, cos, PI}; + +struct Vector { + x: Fractional, + y: Fractional, + z: Fractional, +} + +fn mean(v: &Vec) -> Result { + let r = v.iter().fold(0, |acc, x| acc + x); + let l = i64::try_from(v.len())?; + Ok(Fractional(r, l)) +} + +fn main() { + let a = vec![3, 6, 1, 9]; + let b = from_vector(&a); + let c = mean(&a).unwrap(); // This might fail if the len of the + // vector (usize) does not fit into i32. + let d :f64 = c.try_into().unwrap(); + let e :f64 = Fractional::try_into(c).unwrap(); + + println!(" [i32] : {:?}" , a); + println!(" [Fractional] : {:?}" , b); + println!(" mean of [i32] : {}" , c); + println!(" as f64 : {}" , d); + println!(" and as f64 : {}" , e); + println!(" again as f64 : {}" , TryInto::::try_into(c).unwrap()); + println!(" Rust π : {}" , FPI); + println!(" π : {} {}" , TryInto::::try_into(PI).unwrap(), PI); + println!(" π as tuple : {:?}" , TryInto::<(i32, i32)>::try_into(PI).unwrap()); + println!(" Rust π² : {}" , FPI * FPI); + println!(" π² : {} {}" , TryInto::::try_into(PI * PI).unwrap(), PI * PI); + println!(" sin 9 : {}" , sin(9)); + println!(" sin 9 : {}" , TryInto::::try_into(sin(9)).unwrap()); + println!(" Rust sin 9 : {}" , f64::sin(9.0 * FPI / 180.0)); + println!(" sin 17 : {}" , sin(17)); + println!(" sin 17 : {}" , TryInto::::try_into(sin(17)).unwrap()); + println!(" Rust sin 17 : {}" , f64::sin(17.0 * FPI / 180.0)); + println!(" sin 31 : {}" , sin(31)); + println!(" sin 31 : {}" , TryInto::::try_into(sin(31)).unwrap()); + println!(" Rust sin 31 : {}" , f64::sin(31.0 * FPI / 180.0)); + println!(" sin 45 : {}" , sin(45)); + println!(" sin 45 : {}" , TryInto::::try_into(sin(45)).unwrap()); + println!(" Rust sin 45 : {}" , f64::sin(45.0 * FPI / 180.0)); + println!(" sin 73 : {}" , sin(73)); + println!(" sin 73 : {}" , TryInto::::try_into(sin(73)).unwrap()); + println!(" Rust sin 73 : {}" , f64::sin(73.0 * FPI / 180.0)); + println!(" sin 123 : {}" , sin(123)); + println!(" sin 123 : {}" , TryInto::::try_into(sin(123)).unwrap()); + println!(" Rust sin 123 : {}" , f64::sin(123.0 * FPI / 180.0)); + println!(" sin 213 : {}" , sin(213)); + println!(" sin 213 : {}" , TryInto::::try_into(sin(213)).unwrap()); + println!(" Rust sin 213 : {}" , f64::sin(213.0 * FPI / 180.0)); + println!(" sin 312 : {}" , sin(312)); + println!(" sin 312 : {}" , TryInto::::try_into(sin(312)).unwrap()); + println!(" Rust sin 312 : {}" , f64::sin(312.0 * FPI / 180.0)); + println!(" sin 876 : {}" , sin(876)); + println!(" sin 876 : {}" , TryInto::::try_into(sin(876)).unwrap()); + println!(" Rust sin 876 : {}" , f64::sin(876.0 * FPI / 180.0)); + println!(" cos 9 : {}" , cos(9)); + println!(" cos 9 : {}" , TryInto::::try_into(cos(9)).unwrap()); + println!(" Rust cos 9 : {}" , f64::cos(9.0 * FPI / 180.0)); + println!(" cos 17 : {}" , cos(17)); + println!(" cos 17 : {}" , TryInto::::try_into(cos(17)).unwrap()); + println!(" Rust cos 17 : {}" , f64::cos(17.0 * FPI / 180.0)); + println!(" cos 31 : {}" , cos(31)); + println!(" cos 31 : {}" , TryInto::::try_into(cos(31)).unwrap()); + println!(" Rust cos 31 : {}" , f64::cos(31.0 * FPI / 180.0)); + println!(" cos 45 : {}" , cos(45)); + println!(" cos 45 : {}" , TryInto::::try_into(cos(45)).unwrap()); + println!(" Rust cos 45 : {}" , f64::cos(45.0 * FPI / 180.0)); + println!(" cos 73 : {}" , cos(73)); + println!(" cos 73 : {}" , TryInto::::try_into(cos(73)).unwrap()); + println!(" Rust cos 73 : {}" , f64::cos(73.0 * FPI / 180.0)); + println!(" cos 123 : {}" , cos(123)); + println!(" cos 123 : {}" , TryInto::::try_into(cos(123)).unwrap()); + println!(" Rust cos 123 : {}" , f64::cos(123.0 * FPI / 180.0)); + println!(" cos 213 : {}" , cos(213)); + println!(" cos 213 : {}" , TryInto::::try_into(cos(213)).unwrap()); + println!(" Rust cos 213 : {}" , f64::cos(213.0 * FPI / 180.0)); + println!(" cos 312 : {}" , cos(312)); + println!(" cos 312 : {}" , TryInto::::try_into(cos(312)).unwrap()); + println!(" Rust cos 312 : {}" , f64::cos(312.0 * FPI / 180.0)); + println!(" cos 876 : {}" , cos(876)); + println!(" cos 876 : {}" , TryInto::::try_into(cos(876)).unwrap()); + println!(" Rust cos 876 : {}" , f64::cos(876.0 * FPI / 180.0)); +} diff --git a/fractional/src/trigonometry.rs b/fractional/src/trigonometry.rs new file mode 100644 index 0000000..9f137dd --- /dev/null +++ b/fractional/src/trigonometry.rs @@ -0,0 +1,92 @@ +// +// Test our fractional crate / module... +// +// Georg Hopp +// +// Copyright © 2019 Georg Hopp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +use crate::{Fractional}; + +pub const PI :Fractional = Fractional(355, 113); // This is a really close + // fractional approximation + // for pi + +const PRECISION :i64 = 100000; + +#[inline] +pub fn rad(d: u32) -> f64 { + use std::f64::consts::PI; + d as f64 * PI / 180.0 +} + +pub fn sin(d: i32) -> Fractional { + // hold sin Fractionals from 0 to 89 ... + lazy_static::lazy_static! { + static ref SINTAB :Vec = + (0..90).map(|x| _sin(x)).collect(); + } + + // fractional sin from f64 sin. (From 1° to 89°) + fn _sin(d: u32) -> Fractional { + match d { + 0 => Fractional(0, 1), + _ => { + // This is undefined behaviour for very large f64, but our f64 + // is always between 0.0 and 10000.0 which should be fine. + let s = (f64::sin(rad(d)) * PRECISION as f64).round() as i64; + Fractional(s, PRECISION).reduce() + } + } + } + + match d { + 90 => Fractional(1, 1), + 180 => SINTAB[0], + 270 => -Fractional(1, 1), + 1..=89 => SINTAB[d as usize], + 91..=179 => SINTAB[180 - d as usize], + 181..=269 => -SINTAB[d as usize - 180], + 271..=359 => -SINTAB[360 - d as usize], + _ => sin(d % 360), + } +} + +pub fn cos(d: i32) -> Fractional { + lazy_static::lazy_static! { + static ref COSTAB :Vec = + (0..90).map(|x| _cos(x)).collect(); + } + + fn _cos(d: u32) -> Fractional { + match d { + 0 => Fractional(1, 1), + _ => { + let s = (f64::cos(rad(d)) * PRECISION as f64).round() as i64; + Fractional(s, PRECISION).reduce() + } + } + } + + match d { + 90 | 270 => Fractional(0, 1), + 180 => -COSTAB[0], + 1..=89 => COSTAB[d as usize], + 91..=179 => -COSTAB[180 - d as usize], + 181..=269 => -COSTAB[d as usize - 180], + 271..=359 => COSTAB[360 - d as usize], + _ => cos(d % 360), + } +}