Showing
4 changed files
with
70 additions
and
53 deletions
@@ -39,20 +39,55 @@ impl Continuous { | @@ -39,20 +39,55 @@ impl Continuous { | ||
39 | v[0] = an; | 39 | v[0] = an; |
40 | // The convergence criteria „an_1 == 2 * a0“ is not good for | 40 | // The convergence criteria „an_1 == 2 * a0“ is not good for |
41 | // very small x thus I decided to break the iteration at constant | 41 | // very small x thus I decided to break the iteration at constant |
42 | - // time. Which is the 10 below. | 42 | + // time. Which is the 5 below. |
43 | if v.len() > 1 { | 43 | if v.len() > 1 { |
44 | inner(&mut v[1..], x, a0, mn_1, dn_1, an_1); | 44 | inner(&mut v[1..], x, a0, mn_1, dn_1, an_1); |
45 | } | 45 | } |
46 | } | 46 | } |
47 | 47 | ||
48 | - let mut v :Vec<i64> = vec!(0; 10); | 48 | + let mut v :Vec<i64> = vec!(0; 5); |
49 | inner(&mut v, x, a0, 0, 1, a0); | 49 | inner(&mut v, x, a0, 0, 1, a0); |
50 | Continuous(v) | 50 | Continuous(v) |
51 | } | 51 | } |
52 | 52 | ||
53 | - pub fn into_prec(&self, prec :usize) -> Fractional { | 53 | + // general continous fraction form of a fractional... |
54 | + pub fn from_prec(f :&Fractional, prec :Option<usize>) -> Self { | ||
55 | + fn inner(v :&mut Vec<i64>, f :Fractional, prec :Option<usize>) { | ||
56 | + let mut process = |prec :Option<usize>| { | ||
57 | + let Fractional(n, d) = f; | ||
58 | + let a = n / d; | ||
59 | + let Fractional(_n, _d) = f.noreduce_sub(a.into()); | ||
60 | + | ||
61 | + v.push(a); | ||
62 | + match _n { | ||
63 | + 1 => v.push(_d), | ||
64 | + 0 => {}, | ||
65 | + _ => inner(v, Fractional(_d, _n), prec), | ||
66 | + } | ||
67 | + }; | ||
68 | + | ||
69 | + match prec { | ||
70 | + Some(0) => {}, | ||
71 | + None => process(None), | ||
72 | + Some(p) => process(Some(p - 1)), | ||
73 | + } | ||
74 | + } | ||
75 | + | ||
76 | + let mut v = match prec { | ||
77 | + None => Vec::with_capacity(100), | ||
78 | + Some(p) => Vec::with_capacity(p + 1), | ||
79 | + }; | ||
80 | + | ||
81 | + inner(&mut v, *f, prec); | ||
82 | + Continuous(v) | ||
83 | + } | ||
84 | + | ||
85 | + pub fn into_prec(&self, prec :Option<usize>) -> Fractional { | ||
54 | let Continuous(c) = self; | 86 | let Continuous(c) = self; |
55 | - let p = if prec <= c.len() { prec } else { c.len() }; | 87 | + let p = match prec { |
88 | + Some(p) => if p <= c.len() { p } else { c.len() }, | ||
89 | + None => c.len(), | ||
90 | + }; | ||
56 | 91 | ||
57 | let to_frac = |acc :Fractional, x :&i64| { | 92 | let to_frac = |acc :Fractional, x :&i64| { |
58 | let Fractional(an, ad) = acc.noreduce_add((*x).into()); | 93 | let Fractional(an, ad) = acc.noreduce_add((*x).into()); |
@@ -68,33 +103,13 @@ impl Continuous { | @@ -68,33 +103,13 @@ impl Continuous { | ||
68 | } | 103 | } |
69 | 104 | ||
70 | impl From<&Fractional> for Continuous { | 105 | impl From<&Fractional> for Continuous { |
71 | - // general continous fraction form of a fractional... | ||
72 | fn from(x :&Fractional) -> Self { | 106 | fn from(x :&Fractional) -> Self { |
73 | - fn inner(mut v :Vec<i64>, f :Fractional) -> Vec<i64> { | ||
74 | - let Fractional(n, d) = f; | ||
75 | - let a = n / d; | ||
76 | - let Fractional(_n, _d) = f.noreduce_sub(a.into()); | ||
77 | - | ||
78 | - v.push(a); | ||
79 | - match _n { | ||
80 | - 1 => { v.push(_d); v }, | ||
81 | - 0 => v, | ||
82 | - _ => inner(v, Fractional(_d, _n)), | ||
83 | - } | ||
84 | - } | ||
85 | - | ||
86 | - Continuous(inner(Vec::new(), *x)) | 107 | + Self::from_prec(x, None) |
87 | } | 108 | } |
88 | } | 109 | } |
89 | 110 | ||
90 | impl Into<Fractional> for &Continuous { | 111 | impl Into<Fractional> for &Continuous { |
91 | fn into(self) -> Fractional { | 112 | fn into(self) -> Fractional { |
92 | - let Continuous(c) = self; | ||
93 | - let Fractional(n, d) = c.iter().rev().fold( Fractional(0, 1) | ||
94 | - , |acc, x| { | ||
95 | - let Fractional(an, ad) = acc + (*x).into(); | ||
96 | - Fractional(ad, an) | ||
97 | - }); | ||
98 | - Fractional(d, n) | 113 | + (&self).into_prec(None) |
99 | } | 114 | } |
100 | } | 115 | } |
1 | // | 1 | // |
2 | // This is an abstraction over a drawing environment. | 2 | // This is an abstraction over a drawing environment. |
3 | +// Future note: z-Buffer is described here: | ||
4 | +// https://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation/perspective-correct-interpolation-vertex-attributes | ||
3 | // | 5 | // |
4 | // Georg Hopp <georg@steffers.org> | 6 | // Georg Hopp <georg@steffers.org> |
5 | // | 7 | // |
@@ -69,10 +69,13 @@ impl Fractional { | @@ -69,10 +69,13 @@ impl Fractional { | ||
69 | Self(1, _n / _d) | 69 | Self(1, _n / _d) |
70 | } | 70 | } |
71 | } else { | 71 | } else { |
72 | - //Self(n / hcf(n, d), d / hcf(n, d)) | ||
73 | - let regular_reduced = self; | ||
74 | - let cont :Continuous = (®ular_reduced).into(); | ||
75 | - cont.into_prec(5) | 72 | + // Self(n / hcf(n, d), d / hcf(n, d)) |
73 | + // The above reduces prcisely but results in very large numerator | ||
74 | + // or denominator occasionally. The below is less precise but | ||
75 | + // keeps the numbers small… the bad point is, that it is not very | ||
76 | + // fast. | ||
77 | + let cont = Continuous::from_prec(&self, Some(5)); | ||
78 | + (&cont).into() | ||
76 | } | 79 | } |
77 | } | 80 | } |
78 | 81 | ||
@@ -171,10 +174,7 @@ impl Add for Fractional { | @@ -171,10 +174,7 @@ impl Add for Fractional { | ||
171 | type Output = Self; | 174 | type Output = Self; |
172 | 175 | ||
173 | fn add(self, other: Self) -> Self { | 176 | fn add(self, other: Self) -> Self { |
174 | - let Fractional(n1, d1) = self; | ||
175 | - let Fractional(n2, d2) = other; | ||
176 | - let n = n1 * (self.gcd(other) / d1) + n2 * (self.gcd(other) / d2); | ||
177 | - Self(n, self.gcd(other)).reduce() | 177 | + self.noreduce_add(other).reduce() |
178 | } | 178 | } |
179 | } | 179 | } |
180 | 180 | ||
@@ -191,7 +191,7 @@ impl Neg for Fractional { | @@ -191,7 +191,7 @@ impl Neg for Fractional { | ||
191 | 191 | ||
192 | fn neg(self) -> Self { | 192 | fn neg(self) -> Self { |
193 | let Fractional(n, d) = self; | 193 | let Fractional(n, d) = self; |
194 | - Self(-n, d).reduce() | 194 | + Self(-n, d) |
195 | } | 195 | } |
196 | } | 196 | } |
197 | 197 |
@@ -171,7 +171,7 @@ impl Trig for Fractional { | @@ -171,7 +171,7 @@ impl Trig for Fractional { | ||
171 | match d { | 171 | match d { |
172 | 0 => Fractional(0, 1), | 172 | 0 => Fractional(0, 1), |
173 | 90 => Fractional(1, 1), | 173 | 90 => Fractional(1, 1), |
174 | - _ => reduce(d, PRECISION, &f64::sin), | 174 | + _ => generate(d, PRECISION, &f64::sin), |
175 | } | 175 | } |
176 | } | 176 | } |
177 | 177 | ||
@@ -193,7 +193,7 @@ impl Trig for Fractional { | @@ -193,7 +193,7 @@ impl Trig for Fractional { | ||
193 | 45 => Fractional(1, 1), | 193 | 45 => Fractional(1, 1), |
194 | 90 => Fractional(1, 0), // although they are both inf and -inf. | 194 | 90 => Fractional(1, 0), // although they are both inf and -inf. |
195 | 135 => -Fractional(1, 1), | 195 | 135 => -Fractional(1, 1), |
196 | - _ => reduce(d, PRECISION, &f64::tan), | 196 | + _ => generate(d, PRECISION, &f64::tan), |
197 | } | 197 | } |
198 | } | 198 | } |
199 | 199 | ||
@@ -201,6 +201,22 @@ impl Trig for Fractional { | @@ -201,6 +201,22 @@ impl Trig for Fractional { | ||
201 | } | 201 | } |
202 | } | 202 | } |
203 | 203 | ||
204 | +// search for a fraction with a denominator less than MAX_DENOMINATOR that | ||
205 | +// provides the minimal PRECISION criteria. | ||
206 | +// !! With f = &f64::tan and d close to the inf boundarys of tan | ||
207 | +// we get very large numerators because the numerator becomes a | ||
208 | +// multiple of the denominator. | ||
209 | +fn generate(d :u32, p :i64, f :&dyn Fn(f64) -> f64) -> Fractional { | ||
210 | + // This is undefined behaviour for very large f64, but our f64 | ||
211 | + // is always between 0.0 and 1000000.0 which should be fine. | ||
212 | + let s = (f((d as f64).to_radians()) * p as f64).round() as i64; | ||
213 | + let Fractional(n, dn) = Fractional(s, p).reduce(); | ||
214 | + match dn.abs().cmp(&MAX_DENOMINATOR) { | ||
215 | + Ordering::Less => Fractional(n, dn), | ||
216 | + _ => generate(d, p + 1, f), | ||
217 | + } | ||
218 | +} | ||
219 | + | ||
204 | impl Trig for f64 { | 220 | impl Trig for f64 { |
205 | fn pi() -> Self { | 221 | fn pi() -> Self { |
206 | std::f64::consts::PI | 222 | std::f64::consts::PI |
@@ -262,19 +278,3 @@ impl Trig for f64 { | @@ -262,19 +278,3 @@ impl Trig for f64 { | ||
262 | TANTAB.to_vec() | 278 | TANTAB.to_vec() |
263 | } | 279 | } |
264 | } | 280 | } |
265 | - | ||
266 | -// search for a fraction with a denominator less than MAX_DENOMINATOR that | ||
267 | -// provides the minimal PRECISION criteria. | ||
268 | -// !! With f = &f64::tan and d close to the inf boundarys of tan | ||
269 | -// we get very large numerators because the numerator becomes a | ||
270 | -// multiple of the denominator. | ||
271 | -fn reduce(d :u32, p :i64, f :&dyn Fn(f64) -> f64) -> Fractional { | ||
272 | - // This is undefined behaviour for very large f64, but our f64 | ||
273 | - // is always between 0.0 and 1000000.0 which should be fine. | ||
274 | - let s = (f((d as f64).to_radians()) * p as f64).round() as i64; | ||
275 | - let Fractional(n, dn) = Fractional(s, p).reduce(); | ||
276 | - match dn.abs().cmp(&MAX_DENOMINATOR) { | ||
277 | - Ordering::Less => Fractional(n, dn), | ||
278 | - _ => reduce(d, p + 1, f), | ||
279 | - } | ||
280 | -} |
Please
register
or
login
to post a comment