1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
//! Support for rescaling quantities.
use std::fmt::Debug;

mod decimal;
mod binary;
#[cfg(test)]
pub(crate) mod test;

pub use decimal::Decimal;
pub use binary::Binary;

/// Trait for an individual prefix.
pub trait Prefix: Debug + Clone + Copy + PartialEq {
  /// Get the base for this prefix's exponent.
  fn base(&self) -> i32;

  /// Get the exponent for this prefix.
  fn exponent(&self) -> i32;

  /// Get the multiplier for this prefix as a floating-point number.
  fn multiplier(&self) -> f64 {
    let base = self.base() as f64;
    let exp = self.exponent();
    base.powi(exp)
  }

  /// Rescale a value for this prefix.
  fn scale_value<T: Into<f64>>(&self, value: T) -> f64 {
    let x: f64 = value.into();
    x / self.multiplier()
  }

  /// Get the label for this prefix.
  fn label(&self) -> &'static str;
}

/// Trait for a collection of (related) prefixes.
pub trait PrefixFamily {
  type Prefix: Prefix + 'static;

  fn unit_prefix() -> Self::Prefix;

  /// Get all prefixes for this scheme.  The prefixes must be in sorted order.
  fn all_prefixes() -> &'static [&'static Self::Prefix];

  /// Auto-scale a value.
  fn autoscale(val: f64) -> (f64, Self::Prefix) {
    if !val.is_finite() || !val.is_normal() {
      // non-finite values just get displayed, as does ~0
      return (val, Self::unit_prefix())
    }

    let pfxs = Self::all_prefixes();
    let mut iter = pfxs.iter();
    // always have at least one
    let mut cur = iter.next().unwrap();
    while let Some(next) = iter.next() {
      // check fit w.r.t. next
      if next.scale_value(val).abs() < 1.0 {
        // next is too small, 'cur' is what we want
        break;
      } else {
        // save and let's try the next value
        cur = next;
      }
    }

    (cur.scale_value(val), **cur)
  }
}

/// A scale
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Scale<F: PrefixFamily> {
  /// Auto-scale to the best-fitting prefix.
  Auto,
  /// Display the number in its native scale.
  Native,
  /// Use a specific fixed prefix.
  Fixed(F::Prefix),
}

impl From<Decimal> for Scale<Decimal> {
  fn from(p: Decimal) -> Scale<Decimal> {
    Scale::Fixed(p)
  }
}

impl From<Binary> for Scale<Binary> {
  fn from(p: Binary) -> Scale<Binary> {
    Scale::Fixed(p)
  }
}