Commit fef116697a5f39740ccdb8b7ad7442e77dd19ad2
1 parent
d70c0220
add trigon functions sin and cos as static tables of Fractional
Showing
5 changed files
with
463 additions
and
0 deletions
fractional/Cargo.toml
0 → 100644
fractional/src/fractional.rs
0 → 100644
1 | +// | |
2 | +// Some code to support fractional numbers for full precision rational number | |
3 | +// calculations. | |
4 | +// TODO | |
5 | +// - maybe this could be build as a generic for all integral numbers. | |
6 | +// (Question, how can I assure that it is build from integral numbers? | |
7 | +// | |
8 | +// Georg Hopp <georg@steffers.org> | |
9 | +// | |
10 | +// Copyright © 2019 Georg Hopp | |
11 | +// | |
12 | +// This program is free software: you can redistribute it and/or modify | |
13 | +// it under the terms of the GNU General Public License as published by | |
14 | +// the Free Software Foundation, either version 3 of the License, or | |
15 | +// (at your option) any later version. | |
16 | +// | |
17 | +// This program is distributed in the hope that it will be useful, | |
18 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | +// GNU General Public License for more details. | |
21 | +// | |
22 | +// You should have received a copy of the GNU General Public License | |
23 | +// along with this program. If not, see <http://www.gnu.org/licenses/>. | |
24 | +// | |
25 | +use std::cmp::Ordering; | |
26 | +use std::ops::{Add,Sub,Neg,Mul,Div}; | |
27 | +use std::fmt; | |
28 | +use std::convert::{TryFrom, TryInto}; | |
29 | +use std::num::TryFromIntError; | |
30 | + | |
31 | +#[derive(Debug, Eq, Clone, Copy)] | |
32 | +pub struct Fractional (pub i64, pub i64); | |
33 | + | |
34 | +#[inline] | |
35 | +fn hcf(x :i64, y :i64) -> i64 { | |
36 | + match y { | |
37 | + 0 => x, | |
38 | + _ => hcf(y, x % y), | |
39 | + } | |
40 | +} | |
41 | + | |
42 | +impl Fractional { | |
43 | + #[inline] | |
44 | + pub fn gcd(self, other: Self) -> i64 { | |
45 | + let Fractional(_, d1) = self; | |
46 | + let Fractional(_, d2) = other; | |
47 | + (d1 * d2) / hcf(d1, d2) | |
48 | + } | |
49 | + | |
50 | + #[inline] | |
51 | + pub fn reduce(self) -> Self { | |
52 | + let Fractional(n, d) = self; | |
53 | + Self(n / hcf(n, d), d / hcf(n, d)) | |
54 | + } | |
55 | + | |
56 | + #[inline] | |
57 | + pub fn numerator(self) -> i64 { | |
58 | + self.0 | |
59 | + } | |
60 | + | |
61 | + #[inline] | |
62 | + pub fn denominator(self) -> i64 { | |
63 | + self.1 | |
64 | + } | |
65 | +} | |
66 | + | |
67 | +impl fmt::Display for Fractional { | |
68 | + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
69 | + write!(f, "({}/{})", self.0, self.1) | |
70 | + } | |
71 | +} | |
72 | + | |
73 | +impl From<i64> for Fractional { | |
74 | + fn from(x: i64) -> Self { | |
75 | + Self(x, 1) | |
76 | + } | |
77 | +} | |
78 | + | |
79 | +impl TryFrom<usize> for Fractional { | |
80 | + type Error = &'static str; | |
81 | + | |
82 | + fn try_from(x: usize) -> Result<Self, Self::Error> { | |
83 | + let v = i64::try_from(x); | |
84 | + match v { | |
85 | + Err(_) => Err("Conversion from usize to i32 failed"), | |
86 | + Ok(_v) => Ok(Self(_v, 1)), | |
87 | + } | |
88 | + } | |
89 | +} | |
90 | + | |
91 | +pub fn from_vector(xs: &Vec<i64>) -> Vec<Fractional> { | |
92 | + xs.iter().map(|x| Fractional(*x, 1)).collect() | |
93 | +} | |
94 | + | |
95 | +impl TryInto<f64> for Fractional { | |
96 | + type Error = TryFromIntError; | |
97 | + | |
98 | + fn try_into(self) -> Result<f64, Self::Error> { | |
99 | + let n :i32 = self.0.try_into()?; | |
100 | + let d :i32 = self.1.try_into()?; | |
101 | + Ok(f64::from(n) / f64::from(d)) | |
102 | + } | |
103 | +} | |
104 | + | |
105 | +impl TryInto<(i32, i32)> for Fractional { | |
106 | + type Error = TryFromIntError; | |
107 | + | |
108 | + fn try_into(self) -> Result<(i32, i32), Self::Error> { | |
109 | + let a :i32 = (self.0 / self.1).try_into()?; | |
110 | + let b :i32 = (self.0 % self.1).try_into()?; | |
111 | + Ok((a, b)) | |
112 | + } | |
113 | +} | |
114 | + | |
115 | +impl PartialEq for Fractional { | |
116 | + fn eq(&self, other: &Self) -> bool { | |
117 | + let Fractional(n1, d1) = self; | |
118 | + let Fractional(n2, d2) = other; | |
119 | + n1 * (self.gcd(*other) / d1) == n2 * (self.gcd(*other) / d2) | |
120 | + } | |
121 | +} | |
122 | + | |
123 | +impl PartialOrd for Fractional { | |
124 | + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { | |
125 | + Some(self.cmp(other)) | |
126 | + } | |
127 | +} | |
128 | + | |
129 | +impl Ord for Fractional { | |
130 | + fn cmp(&self, other: &Self) -> Ordering { | |
131 | + let Fractional(n1, d1) = self; | |
132 | + let Fractional(n2, d2) = other; | |
133 | + let x = n1 * (self.gcd(*other) / d1); | |
134 | + let y = n2 * (self.gcd(*other) / d2); | |
135 | + x.cmp(&y) | |
136 | + } | |
137 | +} | |
138 | + | |
139 | +impl Add for Fractional { | |
140 | + type Output = Self; | |
141 | + | |
142 | + fn add(self, other: Self) -> Self { | |
143 | + let Fractional(n1, d1) = self; | |
144 | + let Fractional(n2, d2) = other; | |
145 | + let n = n1 * (self.gcd(other) / d1) + n2 * (self.gcd(other) / d2); | |
146 | + Self(n, self.gcd(other)).reduce() | |
147 | + } | |
148 | +} | |
149 | + | |
150 | +impl Sub for Fractional { | |
151 | + type Output = Self; | |
152 | + | |
153 | + fn sub(self, other: Self) -> Self { | |
154 | + let Fractional(n1, d1) = self; | |
155 | + let Fractional(n2, d2) = other; | |
156 | + let n = n1 * (self.gcd(other) / d1) - n2 * (self.gcd(other) / d2); | |
157 | + Self(n, self.gcd(other)).reduce() | |
158 | + } | |
159 | +} | |
160 | + | |
161 | +impl Neg for Fractional { | |
162 | + type Output = Self; | |
163 | + | |
164 | + fn neg(self) -> Self { | |
165 | + let Fractional(n, d) = self; | |
166 | + Self(-n, d).reduce() | |
167 | + } | |
168 | +} | |
169 | + | |
170 | +impl Mul for Fractional { | |
171 | + type Output = Self; | |
172 | + | |
173 | + fn mul(self, other :Self) -> Self { | |
174 | + let Fractional(n1, d1) = self; | |
175 | + let Fractional(n2, d2) = other; | |
176 | + Self(n1 * n2, d1 * d2).reduce() | |
177 | + } | |
178 | +} | |
179 | + | |
180 | +impl Div for Fractional { | |
181 | + type Output = Self; | |
182 | + | |
183 | + fn div(self, other: Self) -> Self { | |
184 | + let Fractional(n, d) = other; | |
185 | + self * Fractional(d, n) | |
186 | + } | |
187 | +} | |
188 | + | |
189 | + /* some stuff that could be tested... | |
190 | + let x = Fractional(1, 3); | |
191 | + let y = Fractional(1, 6); | |
192 | + | |
193 | + println!( | |
194 | + "Greatest common denominator of {} and {}: {}", x, y, x.gcd(y)); | |
195 | + println!("Numerator of {}: {}", x, x.numerator()); | |
196 | + println!("Denominator of {}: {}", x, x.denominator()); | |
197 | + assert_eq!(Fractional(1, 3), Fractional(2, 6)); | |
198 | + assert_eq!(Fractional(1, 3), Fractional(1, 3)); | |
199 | + assert_eq!(y < x, true); | |
200 | + assert_eq!(y > x, false); | |
201 | + assert_eq!(x == y, false); | |
202 | + assert_eq!(x == x, true); | |
203 | + assert_eq!(x + y, Fractional(1, 2)); | |
204 | + println!("{} + {} = {}", x, y, x + y); | |
205 | + assert_eq!(x - y, Fractional(1, 6)); | |
206 | + println!("{} - {} = {}", x, y, x - y); | |
207 | + assert_eq!(y - x, Fractional(-1, 6)); | |
208 | + println!("{} - {} = {}", y, x, y - x); | |
209 | + assert_eq!(-x, Fractional(-1, 3)); | |
210 | + println!("-{} = {}", x, -x); | |
211 | + assert_eq!(x * y, Fractional(1, 18)); | |
212 | + println!("{} * {} = {}", x, y, x * y); | |
213 | + assert_eq!(x / y, Fractional(2, 1)); | |
214 | + println!("{} / {} = {}", x, y, x / y); | |
215 | + assert_eq!(y / x, Fractional(1, 2)); | |
216 | + println!("{} / {} = {}", y, x, y / x); | |
217 | + | |
218 | + println!("Fractional from 3: {}", Fractional::from(3)); | |
219 | + let z :f64 = Fractional::into(x); | |
220 | + println!("Floating point of {}: {}", x, z); | |
221 | + let (d, r) = Fractional::into(x); | |
222 | + println!("(div, rest) of {}: ({}, {})", x, d, r); | |
223 | + */ | |
224 | + | ... | ... |
fractional/src/lib.rs
0 → 100644
1 | +// | |
2 | +// Lib for fractional calculations. | |
3 | +// | |
4 | +// Georg Hopp <georg@steffers.org> | |
5 | +// | |
6 | +// Copyright © 2019 Georg Hopp | |
7 | +// | |
8 | +// This program is free software: you can redistribute it and/or modify | |
9 | +// it under the terms of the GNU General Public License as published by | |
10 | +// the Free Software Foundation, either version 3 of the License, or | |
11 | +// (at your option) any later version. | |
12 | +// | |
13 | +// This program is distributed in the hope that it will be useful, | |
14 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | +// GNU General Public License for more details. | |
17 | +// | |
18 | +// You should have received a copy of the GNU General Public License | |
19 | +// along with this program. If not, see <http://www.gnu.org/licenses/>. | |
20 | +// | |
21 | +extern crate lazy_static; | |
22 | + | |
23 | +pub mod fractional; | |
24 | +pub mod trigonometry; | |
25 | + | |
26 | +use fractional::{Fractional}; | ... | ... |
fractional/src/main.rs
0 → 100644
1 | +// | |
2 | +// Test our fractional crate / module... | |
3 | +// | |
4 | +// Georg Hopp <georg@steffers.org> | |
5 | +// | |
6 | +// Copyright © 2019 Georg Hopp | |
7 | +// | |
8 | +// This program is free software: you can redistribute it and/or modify | |
9 | +// it under the terms of the GNU General Public License as published by | |
10 | +// the Free Software Foundation, either version 3 of the License, or | |
11 | +// (at your option) any later version. | |
12 | +// | |
13 | +// This program is distributed in the hope that it will be useful, | |
14 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | +// GNU General Public License for more details. | |
17 | +// | |
18 | +// You should have received a copy of the GNU General Public License | |
19 | +// along with this program. If not, see <http://www.gnu.org/licenses/>. | |
20 | +// | |
21 | +use std::convert::{TryFrom, TryInto}; | |
22 | +use std::num::TryFromIntError; | |
23 | +use std::f64::consts::PI as FPI; | |
24 | + | |
25 | +use fractional::fractional::{Fractional, from_vector}; | |
26 | +use fractional::trigonometry::{sin, cos, PI}; | |
27 | + | |
28 | +struct Vector { | |
29 | + x: Fractional, | |
30 | + y: Fractional, | |
31 | + z: Fractional, | |
32 | +} | |
33 | + | |
34 | +fn mean(v: &Vec<i64>) -> Result<Fractional, TryFromIntError> { | |
35 | + let r = v.iter().fold(0, |acc, x| acc + x); | |
36 | + let l = i64::try_from(v.len())?; | |
37 | + Ok(Fractional(r, l)) | |
38 | +} | |
39 | + | |
40 | +fn main() { | |
41 | + let a = vec![3, 6, 1, 9]; | |
42 | + let b = from_vector(&a); | |
43 | + let c = mean(&a).unwrap(); // This might fail if the len of the | |
44 | + // vector (usize) does not fit into i32. | |
45 | + let d :f64 = c.try_into().unwrap(); | |
46 | + let e :f64 = Fractional::try_into(c).unwrap(); | |
47 | + | |
48 | + println!(" [i32] : {:?}" , a); | |
49 | + println!(" [Fractional] : {:?}" , b); | |
50 | + println!(" mean of [i32] : {}" , c); | |
51 | + println!(" as f64 : {}" , d); | |
52 | + println!(" and as f64 : {}" , e); | |
53 | + println!(" again as f64 : {}" , TryInto::<f64>::try_into(c).unwrap()); | |
54 | + println!(" Rust π : {}" , FPI); | |
55 | + println!(" π : {} {}" , TryInto::<f64>::try_into(PI).unwrap(), PI); | |
56 | + println!(" π as tuple : {:?}" , TryInto::<(i32, i32)>::try_into(PI).unwrap()); | |
57 | + println!(" Rust π² : {}" , FPI * FPI); | |
58 | + println!(" π² : {} {}" , TryInto::<f64>::try_into(PI * PI).unwrap(), PI * PI); | |
59 | + println!(" sin 9 : {}" , sin(9)); | |
60 | + println!(" sin 9 : {}" , TryInto::<f64>::try_into(sin(9)).unwrap()); | |
61 | + println!(" Rust sin 9 : {}" , f64::sin(9.0 * FPI / 180.0)); | |
62 | + println!(" sin 17 : {}" , sin(17)); | |
63 | + println!(" sin 17 : {}" , TryInto::<f64>::try_into(sin(17)).unwrap()); | |
64 | + println!(" Rust sin 17 : {}" , f64::sin(17.0 * FPI / 180.0)); | |
65 | + println!(" sin 31 : {}" , sin(31)); | |
66 | + println!(" sin 31 : {}" , TryInto::<f64>::try_into(sin(31)).unwrap()); | |
67 | + println!(" Rust sin 31 : {}" , f64::sin(31.0 * FPI / 180.0)); | |
68 | + println!(" sin 45 : {}" , sin(45)); | |
69 | + println!(" sin 45 : {}" , TryInto::<f64>::try_into(sin(45)).unwrap()); | |
70 | + println!(" Rust sin 45 : {}" , f64::sin(45.0 * FPI / 180.0)); | |
71 | + println!(" sin 73 : {}" , sin(73)); | |
72 | + println!(" sin 73 : {}" , TryInto::<f64>::try_into(sin(73)).unwrap()); | |
73 | + println!(" Rust sin 73 : {}" , f64::sin(73.0 * FPI / 180.0)); | |
74 | + println!(" sin 123 : {}" , sin(123)); | |
75 | + println!(" sin 123 : {}" , TryInto::<f64>::try_into(sin(123)).unwrap()); | |
76 | + println!(" Rust sin 123 : {}" , f64::sin(123.0 * FPI / 180.0)); | |
77 | + println!(" sin 213 : {}" , sin(213)); | |
78 | + println!(" sin 213 : {}" , TryInto::<f64>::try_into(sin(213)).unwrap()); | |
79 | + println!(" Rust sin 213 : {}" , f64::sin(213.0 * FPI / 180.0)); | |
80 | + println!(" sin 312 : {}" , sin(312)); | |
81 | + println!(" sin 312 : {}" , TryInto::<f64>::try_into(sin(312)).unwrap()); | |
82 | + println!(" Rust sin 312 : {}" , f64::sin(312.0 * FPI / 180.0)); | |
83 | + println!(" sin 876 : {}" , sin(876)); | |
84 | + println!(" sin 876 : {}" , TryInto::<f64>::try_into(sin(876)).unwrap()); | |
85 | + println!(" Rust sin 876 : {}" , f64::sin(876.0 * FPI / 180.0)); | |
86 | + println!(" cos 9 : {}" , cos(9)); | |
87 | + println!(" cos 9 : {}" , TryInto::<f64>::try_into(cos(9)).unwrap()); | |
88 | + println!(" Rust cos 9 : {}" , f64::cos(9.0 * FPI / 180.0)); | |
89 | + println!(" cos 17 : {}" , cos(17)); | |
90 | + println!(" cos 17 : {}" , TryInto::<f64>::try_into(cos(17)).unwrap()); | |
91 | + println!(" Rust cos 17 : {}" , f64::cos(17.0 * FPI / 180.0)); | |
92 | + println!(" cos 31 : {}" , cos(31)); | |
93 | + println!(" cos 31 : {}" , TryInto::<f64>::try_into(cos(31)).unwrap()); | |
94 | + println!(" Rust cos 31 : {}" , f64::cos(31.0 * FPI / 180.0)); | |
95 | + println!(" cos 45 : {}" , cos(45)); | |
96 | + println!(" cos 45 : {}" , TryInto::<f64>::try_into(cos(45)).unwrap()); | |
97 | + println!(" Rust cos 45 : {}" , f64::cos(45.0 * FPI / 180.0)); | |
98 | + println!(" cos 73 : {}" , cos(73)); | |
99 | + println!(" cos 73 : {}" , TryInto::<f64>::try_into(cos(73)).unwrap()); | |
100 | + println!(" Rust cos 73 : {}" , f64::cos(73.0 * FPI / 180.0)); | |
101 | + println!(" cos 123 : {}" , cos(123)); | |
102 | + println!(" cos 123 : {}" , TryInto::<f64>::try_into(cos(123)).unwrap()); | |
103 | + println!(" Rust cos 123 : {}" , f64::cos(123.0 * FPI / 180.0)); | |
104 | + println!(" cos 213 : {}" , cos(213)); | |
105 | + println!(" cos 213 : {}" , TryInto::<f64>::try_into(cos(213)).unwrap()); | |
106 | + println!(" Rust cos 213 : {}" , f64::cos(213.0 * FPI / 180.0)); | |
107 | + println!(" cos 312 : {}" , cos(312)); | |
108 | + println!(" cos 312 : {}" , TryInto::<f64>::try_into(cos(312)).unwrap()); | |
109 | + println!(" Rust cos 312 : {}" , f64::cos(312.0 * FPI / 180.0)); | |
110 | + println!(" cos 876 : {}" , cos(876)); | |
111 | + println!(" cos 876 : {}" , TryInto::<f64>::try_into(cos(876)).unwrap()); | |
112 | + println!(" Rust cos 876 : {}" , f64::cos(876.0 * FPI / 180.0)); | |
113 | +} | ... | ... |
fractional/src/trigonometry.rs
0 → 100644
1 | +// | |
2 | +// Test our fractional crate / module... | |
3 | +// | |
4 | +// Georg Hopp <georg@steffers.org> | |
5 | +// | |
6 | +// Copyright © 2019 Georg Hopp | |
7 | +// | |
8 | +// This program is free software: you can redistribute it and/or modify | |
9 | +// it under the terms of the GNU General Public License as published by | |
10 | +// the Free Software Foundation, either version 3 of the License, or | |
11 | +// (at your option) any later version. | |
12 | +// | |
13 | +// This program is distributed in the hope that it will be useful, | |
14 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | +// GNU General Public License for more details. | |
17 | +// | |
18 | +// You should have received a copy of the GNU General Public License | |
19 | +// along with this program. If not, see <http://www.gnu.org/licenses/>. | |
20 | +// | |
21 | +use crate::{Fractional}; | |
22 | + | |
23 | +pub const PI :Fractional = Fractional(355, 113); // This is a really close | |
24 | + // fractional approximation | |
25 | + // for pi | |
26 | + | |
27 | +const PRECISION :i64 = 100000; | |
28 | + | |
29 | +#[inline] | |
30 | +pub fn rad(d: u32) -> f64 { | |
31 | + use std::f64::consts::PI; | |
32 | + d as f64 * PI / 180.0 | |
33 | +} | |
34 | + | |
35 | +pub fn sin(d: i32) -> Fractional { | |
36 | + // hold sin Fractionals from 0 to 89 ... | |
37 | + lazy_static::lazy_static! { | |
38 | + static ref SINTAB :Vec<Fractional> = | |
39 | + (0..90).map(|x| _sin(x)).collect(); | |
40 | + } | |
41 | + | |
42 | + // fractional sin from f64 sin. (From 1° to 89°) | |
43 | + fn _sin(d: u32) -> Fractional { | |
44 | + match d { | |
45 | + 0 => Fractional(0, 1), | |
46 | + _ => { | |
47 | + // This is undefined behaviour for very large f64, but our f64 | |
48 | + // is always between 0.0 and 10000.0 which should be fine. | |
49 | + let s = (f64::sin(rad(d)) * PRECISION as f64).round() as i64; | |
50 | + Fractional(s, PRECISION).reduce() | |
51 | + } | |
52 | + } | |
53 | + } | |
54 | + | |
55 | + match d { | |
56 | + 90 => Fractional(1, 1), | |
57 | + 180 => SINTAB[0], | |
58 | + 270 => -Fractional(1, 1), | |
59 | + 1..=89 => SINTAB[d as usize], | |
60 | + 91..=179 => SINTAB[180 - d as usize], | |
61 | + 181..=269 => -SINTAB[d as usize - 180], | |
62 | + 271..=359 => -SINTAB[360 - d as usize], | |
63 | + _ => sin(d % 360), | |
64 | + } | |
65 | +} | |
66 | + | |
67 | +pub fn cos(d: i32) -> Fractional { | |
68 | + lazy_static::lazy_static! { | |
69 | + static ref COSTAB :Vec<Fractional> = | |
70 | + (0..90).map(|x| _cos(x)).collect(); | |
71 | + } | |
72 | + | |
73 | + fn _cos(d: u32) -> Fractional { | |
74 | + match d { | |
75 | + 0 => Fractional(1, 1), | |
76 | + _ => { | |
77 | + let s = (f64::cos(rad(d)) * PRECISION as f64).round() as i64; | |
78 | + Fractional(s, PRECISION).reduce() | |
79 | + } | |
80 | + } | |
81 | + } | |
82 | + | |
83 | + match d { | |
84 | + 90 | 270 => Fractional(0, 1), | |
85 | + 180 => -COSTAB[0], | |
86 | + 1..=89 => COSTAB[d as usize], | |
87 | + 91..=179 => -COSTAB[180 - d as usize], | |
88 | + 181..=269 => -COSTAB[d as usize - 180], | |
89 | + 271..=359 => COSTAB[360 - d as usize], | |
90 | + _ => cos(d % 360), | |
91 | + } | |
92 | +} | ... | ... |
Please
register
or
login
to post a comment