Browse Source

try to optimize fractional code

master
Georg Hopp 6 years ago
parent
commit
c57ce571b8
Signed by: ghopp GPG Key ID: 4C5D226768784538
  1. 67
      fractional/src/continuous.rs
  2. 2
      fractional/src/easel.rs
  3. 16
      fractional/src/fractional.rs
  4. 36
      fractional/src/trigonometry.rs

67
fractional/src/continuous.rs

@ -39,20 +39,55 @@ impl Continuous {
v[0] = an;
// The convergence criteria „an_1 == 2 * a0“ is not good for
// very small x thus I decided to break the iteration at constant
// time. Which is the 10 below.
// time. Which is the 5 below.
if v.len() > 1 {
inner(&mut v[1..], x, a0, mn_1, dn_1, an_1);
}
}
let mut v :Vec<i64> = vec!(0; 10);
let mut v :Vec<i64> = vec!(0; 5);
inner(&mut v, x, a0, 0, 1, a0);
Continuous(v)
}
pub fn into_prec(&self, prec :usize) -> Fractional {
// general continous fraction form of a fractional...
pub fn from_prec(f :&Fractional, prec :Option<usize>) -> Self {
fn inner(v :&mut Vec<i64>, f :Fractional, prec :Option<usize>) {
let mut process = |prec :Option<usize>| {
let Fractional(n, d) = f;
let a = n / d;
let Fractional(_n, _d) = f.noreduce_sub(a.into());
v.push(a);
match _n {
1 => v.push(_d),
0 => {},
_ => inner(v, Fractional(_d, _n), prec),
}
};
match prec {
Some(0) => {},
None => process(None),
Some(p) => process(Some(p - 1)),
}
}
let mut v = match prec {
None => Vec::with_capacity(100),
Some(p) => Vec::with_capacity(p + 1),
};
inner(&mut v, *f, prec);
Continuous(v)
}
pub fn into_prec(&self, prec :Option<usize>) -> Fractional {
let Continuous(c) = self;
let p = if prec <= c.len() { prec } else { c.len() };
let p = match prec {
Some(p) => if p <= c.len() { p } else { c.len() },
None => c.len(),
};
let to_frac = |acc :Fractional, x :&i64| {
let Fractional(an, ad) = acc.noreduce_add((*x).into());
@ -68,33 +103,13 @@ impl Continuous {
}
impl From<&Fractional> for Continuous {
// general continous fraction form of a fractional...
fn from(x :&Fractional) -> Self {
fn inner(mut v :Vec<i64>, f :Fractional) -> Vec<i64> {
let Fractional(n, d) = f;
let a = n / d;
let Fractional(_n, _d) = f.noreduce_sub(a.into());
v.push(a);
match _n {
1 => { v.push(_d); v },
0 => v,
_ => inner(v, Fractional(_d, _n)),
}
}
Continuous(inner(Vec::new(), *x))
Self::from_prec(x, None)
}
}
impl Into<Fractional> for &Continuous {
fn into(self) -> Fractional {
let Continuous(c) = self;
let Fractional(n, d) = c.iter().rev().fold( Fractional(0, 1)
, |acc, x| {
let Fractional(an, ad) = acc + (*x).into();
Fractional(ad, an)
});
Fractional(d, n)
(&self).into_prec(None)
}
}

2
fractional/src/easel.rs

@ -1,5 +1,7 @@
//
// This is an abstraction over a drawing environment.
// Future note: z-Buffer is described here:
// https://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation/perspective-correct-interpolation-vertex-attributes
//
// Georg Hopp <georg@steffers.org>
//

16
fractional/src/fractional.rs

@ -70,9 +70,12 @@ impl Fractional {
}
} else {
// Self(n / hcf(n, d), d / hcf(n, d))
let regular_reduced = self;
let cont :Continuous = (&regular_reduced).into();
cont.into_prec(5)
// The above reduces prcisely but results in very large numerator
// or denominator occasionally. The below is less precise but
// keeps the numbers small… the bad point is, that it is not very
// fast.
let cont = Continuous::from_prec(&self, Some(5));
(&cont).into()
}
}
@ -171,10 +174,7 @@ 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()
self.noreduce_add(other).reduce()
}
}
@ -191,7 +191,7 @@ impl Neg for Fractional {
fn neg(self) -> Self {
let Fractional(n, d) = self;
Self(-n, d).reduce()
Self(-n, d)
}
}

36
fractional/src/trigonometry.rs

@ -171,7 +171,7 @@ impl Trig for Fractional {
match d {
0 => Fractional(0, 1),
90 => Fractional(1, 1),
_ => reduce(d, PRECISION, &f64::sin),
_ => generate(d, PRECISION, &f64::sin),
}
}
@ -193,7 +193,7 @@ impl Trig for Fractional {
45 => Fractional(1, 1),
90 => Fractional(1, 0), // although they are both inf and -inf.
135 => -Fractional(1, 1),
_ => reduce(d, PRECISION, &f64::tan),
_ => generate(d, PRECISION, &f64::tan),
}
}
@ -201,6 +201,22 @@ impl Trig for Fractional {
}
}
// search for a fraction with a denominator less than MAX_DENOMINATOR that
// provides the minimal PRECISION criteria.
// !! With f = &f64::tan and d close to the inf boundarys of tan
// we get very large numerators because the numerator becomes a
// multiple of the denominator.
fn generate(d :u32, p :i64, f :&dyn Fn(f64) -> f64) -> Fractional {
// This is undefined behaviour for very large f64, but our f64
// is always between 0.0 and 1000000.0 which should be fine.
let s = (f((d as f64).to_radians()) * p as f64).round() as i64;
let Fractional(n, dn) = Fractional(s, p).reduce();
match dn.abs().cmp(&MAX_DENOMINATOR) {
Ordering::Less => Fractional(n, dn),
_ => generate(d, p + 1, f),
}
}
impl Trig for f64 {
fn pi() -> Self {
std::f64::consts::PI
@ -262,19 +278,3 @@ impl Trig for f64 {
TANTAB.to_vec()
}
}
// search for a fraction with a denominator less than MAX_DENOMINATOR that
// provides the minimal PRECISION criteria.
// !! With f = &f64::tan and d close to the inf boundarys of tan
// we get very large numerators because the numerator becomes a
// multiple of the denominator.
fn reduce(d :u32, p :i64, f :&dyn Fn(f64) -> f64) -> Fractional {
// This is undefined behaviour for very large f64, but our f64
// is always between 0.0 and 1000000.0 which should be fine.
let s = (f((d as f64).to_radians()) * p as f64).round() as i64;
let Fractional(n, dn) = Fractional(s, p).reduce();
match dn.abs().cmp(&MAX_DENOMINATOR) {
Ordering::Less => Fractional(n, dn),
_ => reduce(d, p + 1, f),
}
}
Loading…
Cancel
Save