1use super::{Number, Unit, UnitSet};
2use crate::output::{Format, Formatted};
3use std::fmt::{self, Display};
4use std::ops::{Div, Mul, Neg};
5
6#[derive(Clone)]
9pub struct Numeric {
10 pub value: Number,
12 pub unit: UnitSet,
14}
15
16impl fmt::Debug for Numeric {
17 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18 write!(f, "{:?}; {:?}", self.value, self.unit)
19 }
20}
21
22impl Numeric {
23 pub fn new<V: Into<Number>, U: Into<UnitSet>>(value: V, unit: U) -> Self {
28 Self {
29 value: value.into(),
30 unit: unit.into(),
31 }
32 }
33 pub fn scalar(value: impl Into<Number>) -> Self {
35 Self {
36 value: value.into(),
37 unit: UnitSet::scalar(),
38 }
39 }
40 pub(crate) fn percentage(value: impl Into<Number>) -> Self {
43 Self::new(value.into() * 100, Unit::Percent)
44 }
45
46 pub fn as_unit(&self, unit: Unit) -> Option<Number> {
56 self.unit
57 .scale_to_unit(&unit)
58 .map(|scale| &self.value * &Number::from(scale))
59 }
60 pub fn as_unitset(&self, unit: &UnitSet) -> Option<Number> {
70 self.unit
71 .scale_to(unit)
72 .map(|scale| &self.value * &Number::from(scale))
73 }
74
75 pub fn as_unit_def(&self, unit: Unit) -> Option<Number> {
80 if self.is_no_unit() {
81 Some(self.value.clone())
82 } else {
83 self.as_unit(unit)
84 }
85 }
86
87 pub fn is_no_unit(&self) -> bool {
89 self.unit.is_none()
90 }
91
92 pub fn format(&self, format: Format) -> Formatted<Self> {
94 Formatted {
95 value: self,
96 format,
97 }
98 }
99}
100
101impl PartialEq for Numeric {
102 fn eq(&self, other: &Self) -> bool {
103 self.partial_cmp(other) == Some(std::cmp::Ordering::Equal)
104 }
105}
106impl Eq for Numeric {}
107
108impl PartialOrd for Numeric {
109 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
110 if self.unit == other.unit {
111 self.value.partial_cmp(&other.value)
112 } else if self.is_no_unit() || other.is_no_unit() {
113 match self.value.partial_cmp(&other.value) {
114 Some(std::cmp::Ordering::Equal) => None,
115 other => other,
116 }
117 } else if let Some(scaled) = other.as_unitset(&self.unit) {
118 self.value.partial_cmp(&scaled)
119 } else {
120 None
121 }
122 }
123}
124
125impl Neg for &Numeric {
126 type Output = Numeric;
127 fn neg(self) -> Self::Output {
128 Numeric {
129 value: -&self.value,
130 unit: self.unit.clone(),
131 }
132 }
133}
134
135impl Div for &Numeric {
136 type Output = Numeric;
137 fn div(self, rhs: Self) -> Self::Output {
138 Numeric {
139 value: &self.value / &rhs.value,
140 unit: &self.unit / &rhs.unit,
141 }
142 .simplify()
143 }
144}
145impl Numeric {
146 fn simplify(mut self) -> Self {
147 let scale = self.unit.simplify();
148 self.value = (f64::from(self.value) * scale).into();
149 self
150 }
151}
152
153impl Mul for &Numeric {
154 type Output = Numeric;
155 fn mul(self, rhs: Self) -> Self::Output {
156 Numeric {
157 value: &self.value * &rhs.value,
158 unit: &self.unit * &rhs.unit,
159 }
160 .simplify()
161 }
162}
163
164impl Display for Formatted<'_, Numeric> {
165 fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
166 let t = self.value.clone().simplify();
167 t.value.format(self.format).fmt(out)?;
168 if !t.value.is_finite() && t.unit.is_pos() {
169 out.write_str(" * 1")?;
170 }
171 t.unit.fmt(out)
172 }
173}