Commit c57ce571b8d55cdb1658b0b5a4f662cc6aca149b

Authored by Georg Hopp
1 parent b06ee13b

try to optimize fractional code

@@ -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 = (&regular_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