datalogic_rs/value/
number.rs1use std::cmp::Ordering;
7use std::fmt;
8
9#[derive(Debug, Clone, Copy)]
14pub enum NumberValue {
15 Integer(i64),
17
18 Float(f64),
20}
21
22impl NumberValue {
23 pub fn from_i64(value: i64) -> Self {
25 NumberValue::Integer(value)
26 }
27
28 pub fn from_f64(value: f64) -> Self {
30 if value.fract() == 0.0 && value >= i64::MIN as f64 && value <= i64::MAX as f64 {
32 NumberValue::Integer(value as i64)
33 } else {
34 NumberValue::Float(value)
35 }
36 }
37
38 pub fn is_integer(&self) -> bool {
40 matches!(self, NumberValue::Integer(_))
41 }
42
43 pub fn is_float(&self) -> bool {
45 matches!(self, NumberValue::Float(_))
46 }
47
48 pub fn as_i64(&self) -> Option<i64> {
50 match *self {
51 NumberValue::Integer(i) => Some(i),
52 NumberValue::Float(f) => {
53 if f.fract() == 0.0 && f >= i64::MIN as f64 && f <= i64::MAX as f64 {
54 Some(f as i64)
55 } else {
56 None
57 }
58 }
59 }
60 }
61
62 pub fn as_f64(&self) -> f64 {
64 match *self {
65 NumberValue::Integer(i) => i as f64,
66 NumberValue::Float(f) => f,
67 }
68 }
69
70 pub fn add(&self, other: &NumberValue) -> NumberValue {
72 match (*self, *other) {
73 (NumberValue::Integer(a), NumberValue::Integer(b)) => {
74 match a.checked_add(b) {
76 Some(result) => NumberValue::Integer(result),
77 None => NumberValue::Float(a as f64 + b as f64),
78 }
79 }
80 _ => NumberValue::from_f64(self.as_f64() + other.as_f64()),
81 }
82 }
83
84 pub fn subtract(&self, other: &NumberValue) -> NumberValue {
86 match (*self, *other) {
87 (NumberValue::Integer(a), NumberValue::Integer(b)) => {
88 match a.checked_sub(b) {
90 Some(result) => NumberValue::Integer(result),
91 None => NumberValue::Float(a as f64 - b as f64),
92 }
93 }
94 _ => NumberValue::from_f64(self.as_f64() - other.as_f64()),
95 }
96 }
97
98 pub fn multiply(&self, other: &NumberValue) -> NumberValue {
100 match (*self, *other) {
101 (NumberValue::Integer(a), NumberValue::Integer(b)) => {
102 match a.checked_mul(b) {
104 Some(result) => NumberValue::Integer(result),
105 None => NumberValue::Float(a as f64 * b as f64),
106 }
107 }
108 _ => NumberValue::from_f64(self.as_f64() * other.as_f64()),
109 }
110 }
111
112 pub fn divide(&self, other: &NumberValue) -> Option<NumberValue> {
114 let divisor = other.as_f64();
115 if divisor == 0.0 {
116 return None;
117 }
118
119 match (*self, *other) {
120 (NumberValue::Integer(a), NumberValue::Integer(b)) => {
121 if a % b == 0 {
122 Some(NumberValue::Integer(a / b))
123 } else {
124 Some(NumberValue::Float(a as f64 / b as f64))
125 }
126 }
127 _ => Some(NumberValue::from_f64(self.as_f64() / divisor)),
128 }
129 }
130
131 pub fn modulo(&self, other: &NumberValue) -> Option<NumberValue> {
133 let divisor = other.as_f64();
134 if divisor == 0.0 {
135 return None;
136 }
137
138 match (*self, *other) {
139 (NumberValue::Integer(a), NumberValue::Integer(b)) => Some(NumberValue::Integer(a % b)),
140 _ => Some(NumberValue::from_f64(self.as_f64() % divisor)),
141 }
142 }
143}
144
145impl PartialEq for NumberValue {
146 fn eq(&self, other: &Self) -> bool {
147 match (*self, *other) {
148 (NumberValue::Integer(a), NumberValue::Integer(b)) => a == b,
149 (NumberValue::Float(a), NumberValue::Float(b)) => a == b,
150 (NumberValue::Integer(a), NumberValue::Float(b)) => (a as f64) == b,
151 (NumberValue::Float(a), NumberValue::Integer(b)) => a == (b as f64),
152 }
153 }
154}
155
156impl Eq for NumberValue {}
157
158impl PartialOrd for NumberValue {
159 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
160 match (*self, *other) {
161 (NumberValue::Integer(a), NumberValue::Integer(b)) => a.partial_cmp(&b),
162 (NumberValue::Float(a), NumberValue::Float(b)) => a.partial_cmp(&b),
163 (NumberValue::Integer(a), NumberValue::Float(b)) => (a as f64).partial_cmp(&b),
164 (NumberValue::Float(a), NumberValue::Integer(b)) => a.partial_cmp(&(b as f64)),
165 }
166 }
167}
168
169impl fmt::Display for NumberValue {
170 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171 match *self {
172 NumberValue::Integer(i) => write!(f, "{}", i),
173 NumberValue::Float(fl) => write!(f, "{}", fl),
174 }
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181
182 #[test]
183 fn test_number_creation() {
184 let int = NumberValue::from_i64(42);
185 let float = NumberValue::from_f64(3.14);
186 let int_from_float = NumberValue::from_f64(42.0);
187
188 assert!(int.is_integer());
189 assert!(float.is_float());
190 assert!(int_from_float.is_integer());
191
192 assert_eq!(int.as_i64(), Some(42));
193 assert_eq!(float.as_i64(), None);
194 assert_eq!(int_from_float.as_i64(), Some(42));
195
196 assert_eq!(int.as_f64(), 42.0);
197 assert_eq!(float.as_f64(), 3.14);
198 }
199
200 #[test]
201 fn test_number_operations() {
202 let a = NumberValue::from_i64(5);
203 let b = NumberValue::from_i64(3);
204 let c = NumberValue::from_f64(2.5);
205
206 assert_eq!(a.add(&b), NumberValue::from_i64(8));
207 assert_eq!(a.subtract(&b), NumberValue::from_i64(2));
208 assert_eq!(a.multiply(&b), NumberValue::from_i64(15));
209 assert_eq!(a.divide(&b).unwrap(), NumberValue::from_f64(5.0 / 3.0));
210
211 assert_eq!(a.add(&c), NumberValue::from_f64(7.5));
212 assert_eq!(a.subtract(&c), NumberValue::from_f64(2.5));
213 assert_eq!(a.multiply(&c), NumberValue::from_f64(12.5));
214 assert_eq!(a.divide(&c).unwrap(), NumberValue::from_f64(2.0));
215 }
216
217 #[test]
218 fn test_number_comparison() {
219 let a = NumberValue::from_i64(5);
220 let b = NumberValue::from_i64(3);
221 let c = NumberValue::from_f64(5.0);
222 let d = NumberValue::from_f64(3.5);
223
224 assert!(a > b);
225 assert!(a == c);
226 assert!(a > d);
227 assert!(d > b);
228 }
229}