Commit c57ce571b8d55cdb1658b0b5a4f662cc6aca149b

Authored by Georg Hopp
1 parent b06ee13b

try to optimize fractional code

... ... @@ -39,20 +39,55 @@ impl Continuous {
39 39 v[0] = an;
40 40 // The convergence criteria „an_1 == 2 * a0“ is not good for
41 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 43 if v.len() > 1 {
44 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 49 inner(&mut v, x, a0, 0, 1, a0);
50 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 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 92 let to_frac = |acc :Fractional, x :&i64| {
58 93 let Fractional(an, ad) = acc.noreduce_add((*x).into());
... ... @@ -68,33 +103,13 @@ impl Continuous {
68 103 }
69 104
70 105 impl From<&Fractional> for Continuous {
71   - // general continous fraction form of a fractional...
72 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 111 impl Into<Fractional> for &Continuous {
91 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 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 6 // Georg Hopp <georg@steffers.org>
5 7 //
... ...
... ... @@ -69,10 +69,13 @@ impl Fractional {
69 69 Self(1, _n / _d)
70 70 }
71 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 174 type Output = Self;
172 175
173 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 191
192 192 fn neg(self) -> Self {
193 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 171 match d {
172 172 0 => Fractional(0, 1),
173 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 193 45 => Fractional(1, 1),
194 194 90 => Fractional(1, 0), // although they are both inf and -inf.
195 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 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 220 impl Trig for f64 {
205 221 fn pi() -> Self {
206 222 std::f64::consts::PI
... ... @@ -262,19 +278,3 @@ impl Trig for f64 {
262 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