ckey/math.rs
1// Copyright (C) 2024 Christian Mauduit <ufoot@ufoot.org>
2
3use crate::key::CKey;
4use crate::key::DATA_U64_SIZE;
5use std::num::Wrapping;
6
7impl std::ops::Add<CKey> for CKey {
8 type Output = Self;
9
10 /// Add another key.
11 ///
12 /// If the result is outside key space, will loop back,
13 /// as in wrapping mode. So the result is always within key space.
14 ///
15 /// # Examples
16 /// ```
17 /// use ckey::CKey;
18 ///
19 /// let k1 = CKey::from(0.6f64);
20 /// let k2 = CKey::from(0.8f64);
21 /// let k_sum = k1 + k2;
22 /// assert_eq!("0.400000000", String::from(k_sum));
23 /// ```
24 fn add(self, other: CKey) -> CKey {
25 let mut old_carry = 0;
26 let mut ret: CKey = CKey::default();
27 for i in 0..DATA_U64_SIZE {
28 let j = DATA_U64_SIZE - i - 1;
29 let (v, carry) = match self.data[j].checked_add(other.data[j]) {
30 Some(v1) => match v1.checked_add(old_carry) {
31 Some(v2) => (v2, 0),
32 None => ((Wrapping(v1) + Wrapping(old_carry)).0, 1),
33 },
34 None => (
35 (Wrapping(self.data[j]) + Wrapping(other.data[j]) + Wrapping(old_carry)).0,
36 1,
37 ),
38 };
39 ret.data[j] = v;
40 old_carry = carry;
41 }
42 ret
43 }
44}
45
46impl std::ops::Sub<CKey> for CKey {
47 type Output = Self;
48
49 /// Sub another key.
50 ///
51 /// If the result is outside key space, will loop back,
52 /// as in wrapping mode. So the result is always within key space.
53 ///
54 /// # Examples
55 /// ```
56 /// use ckey::CKey;
57 ///
58 /// let k1 = CKey::from(0.5f64);
59 /// let k2 = CKey::from(0.9f64);
60 /// let k_diff = k1 - k2;
61 /// assert_eq!("0.600000000", String::from(k_diff));
62 /// ```
63 fn sub(self, other: CKey) -> CKey {
64 let mut old_carry = 0;
65 let mut ret: CKey = CKey::default();
66 for i in 0..DATA_U64_SIZE {
67 let j = DATA_U64_SIZE - i - 1;
68 let (v, carry) = match self.data[j].checked_sub(other.data[j]) {
69 Some(v1) => match v1.checked_sub(old_carry) {
70 Some(v2) => (v2, 0),
71 None => ((Wrapping(v1) - Wrapping(old_carry)).0, 1),
72 },
73 None => (
74 (Wrapping(self.data[j]) - Wrapping(other.data[j]) - Wrapping(old_carry)).0,
75 1,
76 ),
77 };
78 ret.data[j] = v;
79 old_carry = carry;
80 }
81 ret
82 }
83}
84
85impl std::ops::Add<f32> for CKey {
86 type Output = Self;
87
88 /// Add an f32.
89 ///
90 /// The value is first converted to a key, considering key space
91 /// goes from 0 to 1.
92 ///
93 /// # Examples
94 /// ```
95 /// use ckey::CKey;
96 ///
97 /// let k = CKey::from(0.3f32);
98 /// // due to rounding errors, not exactly 0.7
99 /// assert_eq!("0.700000018", String::from(k + 0.4f32));
100 /// ```
101 fn add(self, delta: f32) -> CKey {
102 let other = CKey::from(delta);
103 self + other
104 }
105}
106
107impl std::ops::Sub<f32> for CKey {
108 type Output = Self;
109
110 /// Sub an f32.
111 ///
112 /// The value is first converted to a key, considering key space
113 /// goes from 0 to 1.
114 ///
115 /// # Examples
116 /// ```
117 /// use ckey::CKey;
118 ///
119 /// let k = CKey::from(0.3f32);
120 /// // due to rounding errors, not exactly 0.9
121 /// assert_eq!("0.900000006", String::from(k - 0.4f32));
122 /// ```
123 fn sub(self, delta: f32) -> CKey {
124 let other = CKey::from(delta);
125 self - other
126 }
127}
128
129impl std::ops::Add<f64> for CKey {
130 type Output = Self;
131
132 /// Add an f64.
133 ///
134 /// The value is first converted to a key, considering key space
135 /// goes from 0 to 1.
136 ///
137 /// # Examples
138 /// ```
139 /// use ckey::CKey;
140 ///
141 /// let k = CKey::from(0.3f64);
142 /// assert_eq!("0.700000000", String::from(k + 0.4f64));
143 /// ```
144 fn add(self, delta: f64) -> CKey {
145 let other = CKey::from(delta);
146 self + other
147 }
148}
149
150impl std::ops::Sub<f64> for CKey {
151 type Output = Self;
152
153 /// Sub an f64.
154 ///
155 /// The value is first converted to a key, considering key space
156 /// goes from 0 to 1.
157 ///
158 /// # Examples
159 /// ```
160 /// use ckey::CKey;
161 ///
162 /// let k = CKey::from(0.3f64);
163 /// assert_eq!("0.900000000", String::from(k - 0.4f64));
164 /// ```
165 fn sub(self, delta: f64) -> CKey {
166 let other = CKey::from(delta);
167 self - other
168 }
169}
170
171impl std::ops::Add<u8> for CKey {
172 type Output = Self;
173
174 /// Add a u8.
175 ///
176 /// The value is first converted to a key, considering key space
177 /// goes from 0 to 2^8.
178 ///
179 /// # Examples
180 /// ```
181 /// use ckey::CKey;
182 ///
183 /// let k = CKey::from(0.1f64);
184 /// assert_eq!("0.490625000", String::from(k + 100u8))
185 /// ```
186 fn add(self, delta: u8) -> CKey {
187 let other = CKey::from(delta);
188 self + other
189 }
190}
191
192impl std::ops::Sub<u8> for CKey {
193 type Output = Self;
194
195 /// Sub a u8.
196 ///
197 /// The value is first converted to a key, considering key space
198 /// goes from 0 to 2^8.
199 ///
200 /// # Examples
201 /// ```
202 /// use ckey::CKey;
203 ///
204 /// let k = CKey::from(0.1f64);
205 /// assert_eq!("0.709375000", String::from(k - 100u8))
206 /// ```
207 fn sub(self, delta: u8) -> CKey {
208 let other = CKey::from(delta);
209 self - other
210 }
211}
212
213impl std::ops::Add<i8> for CKey {
214 type Output = Self;
215
216 /// Add a i8.
217 ///
218 /// The value is first converted to a key, considering key space
219 /// goes from 0 to 2^8.
220 ///
221 /// # Examples
222 /// ```
223 /// use ckey::CKey;
224 ///
225 /// let k = CKey::from(0.1f64);
226 /// assert_eq!("0.490625000", String::from(k + 100i8))
227 /// ```
228 fn add(self, delta: i8) -> CKey {
229 let other = CKey::from(delta);
230 self + other
231 }
232}
233
234impl std::ops::Sub<i8> for CKey {
235 type Output = Self;
236
237 /// Sub a i8.
238 ///
239 /// The value is first converted to a key, considering key space
240 /// goes from 0 to 2^8.
241 ///
242 /// # Examples
243 /// ```
244 /// use ckey::CKey;
245 ///
246 /// let k = CKey::from(0.1f64);
247 /// assert_eq!("0.709375000", String::from(k - 100i8))
248 /// ```
249 fn sub(self, delta: i8) -> CKey {
250 let other = CKey::from(delta);
251 self - other
252 }
253}
254
255impl std::ops::Add<u16> for CKey {
256 type Output = Self;
257
258 /// Add a u16.
259 ///
260 /// The value is first converted to a key, considering key space
261 /// goes from 0 to 2^16.
262 ///
263 /// # Examples
264 /// ```
265 /// use ckey::CKey;
266 ///
267 /// let k = CKey::from(0.1f64);
268 /// assert_eq!("0.252587891", String::from(k + 10_000u16))
269 /// ```
270 fn add(self, delta: u16) -> CKey {
271 let other = CKey::from(delta);
272 self + other
273 }
274}
275
276impl std::ops::Sub<u16> for CKey {
277 type Output = Self;
278
279 /// Sub a u16.
280 ///
281 /// The value is first converted to a key, considering key space
282 /// goes from 0 to 2^16.
283 ///
284 /// # Examples
285 /// ```
286 /// use ckey::CKey;
287 ///
288 /// let k = CKey::from(0.1f64);
289 /// assert_eq!("0.947412109", String::from(k - 10_000u16))
290 /// ```
291 fn sub(self, delta: u16) -> CKey {
292 let other = CKey::from(delta);
293 self - other
294 }
295}
296
297impl std::ops::Add<i16> for CKey {
298 type Output = Self;
299
300 /// Add a i16.
301 ///
302 /// The value is first converted to a key, considering key space
303 /// goes from 0 to 2^16.
304 ///
305 /// # Examples
306 /// ```
307 /// use ckey::CKey;
308 ///
309 /// let k = CKey::from(0.1f64);
310 /// assert_eq!("0.252587891", String::from(k + 10_000i16))
311 /// ```
312 fn add(self, delta: i16) -> CKey {
313 let other = CKey::from(delta);
314 self + other
315 }
316}
317
318impl std::ops::Sub<i16> for CKey {
319 type Output = Self;
320
321 /// Sub a i16.
322 ///
323 /// The value is first converted to a key, considering key space
324 /// goes from 0 to 2^16.
325 ///
326 /// # Examples
327 /// ```
328 /// use ckey::CKey;
329 ///
330 /// let k = CKey::from(0.1f64);
331 /// assert_eq!("0.947412109", String::from(k - 10_000i16))
332 /// ```
333 fn sub(self, delta: i16) -> CKey {
334 let other = CKey::from(delta);
335 self - other
336 }
337}
338
339impl std::ops::Add<u32> for CKey {
340 type Output = Self;
341
342 /// Add a u32.
343 ///
344 /// The value is first converted to a key, considering key space
345 /// goes from 0 to 2^32.
346 ///
347 /// # Examples
348 /// ```
349 /// use ckey::CKey;
350 ///
351 /// let k = CKey::from(0.1f64);
352 /// assert_eq!("0.100232831", String::from(k + 1_000_000u32))
353 /// ```
354 fn add(self, delta: u32) -> CKey {
355 let other = CKey::from(delta);
356 self + other
357 }
358}
359
360impl std::ops::Sub<u32> for CKey {
361 type Output = Self;
362
363 /// Sub a u32.
364 ///
365 /// The value is first converted to a key, considering key space
366 /// goes from 0 to 2^32.
367 ///
368 /// # Examples
369 /// ```
370 /// use ckey::CKey;
371 ///
372 /// let k = CKey::from(0.1f64);
373 /// assert_eq!("0.099767169", String::from(k - 1_000_000u32))
374 /// ```
375 fn sub(self, delta: u32) -> CKey {
376 let other = CKey::from(delta);
377 self - other
378 }
379}
380
381impl std::ops::Add<i32> for CKey {
382 type Output = Self;
383
384 /// Add a i32.
385 ///
386 /// The value is first converted to a key, considering key space
387 /// goes from 0 to 2^32.
388 ///
389 /// # Examples
390 /// ```
391 /// use ckey::CKey;
392 ///
393 /// let k = CKey::from(0.1f64);
394 /// assert_eq!("0.100232831", String::from(k + 1_000_000i32))
395 /// ```
396 fn add(self, delta: i32) -> CKey {
397 let other = CKey::from(delta);
398 self + other
399 }
400}
401
402impl std::ops::Sub<i32> for CKey {
403 type Output = Self;
404
405 /// Sub a i32.
406 ///
407 /// The value is first converted to a key, considering key space
408 /// goes from 0 to 2^32.
409 ///
410 /// # Examples
411 /// ```
412 /// use ckey::CKey;
413 ///
414 /// let k = CKey::from(0.1f64);
415 /// assert_eq!("0.099767169", String::from(k - 1_000_000i32))
416 /// ```
417 fn sub(self, delta: i32) -> CKey {
418 let other = CKey::from(delta);
419 self - other
420 }
421}
422
423impl std::ops::Add<u64> for CKey {
424 type Output = Self;
425
426 /// Add a u64.
427 ///
428 /// The value is first converted to a key, considering key space
429 /// goes from 0 to 2^64.
430 ///
431 /// # Examples
432 /// ```
433 /// use ckey::CKey;
434 ///
435 /// let k = CKey::from(0.1f64);
436 /// assert_eq!("0.100542101", String::from(k + 10_000_000_000_000_000u64))
437 /// ```
438 fn add(self, delta: u64) -> CKey {
439 let other = CKey::from(delta);
440 self + other
441 }
442}
443
444impl std::ops::Sub<u64> for CKey {
445 type Output = Self;
446
447 /// Sub a u64.
448 ///
449 /// The value is first converted to a key, considering key space
450 /// goes from 0 to 2^64.
451 ///
452 /// # Examples
453 /// ```
454 /// use ckey::CKey;
455 ///
456 /// let k = CKey::from(0.1f64);
457 /// assert_eq!("0.099457899", String::from(k - 10_000_000_000_000_000u64))
458 /// ```
459 fn sub(self, delta: u64) -> CKey {
460 let other = CKey::from(delta);
461 self - other
462 }
463}
464
465impl std::ops::Add<i64> for CKey {
466 type Output = Self;
467
468 /// Add a i64.
469 ///
470 /// The value is first converted to a key, considering key space
471 /// goes from 0 to 2^64.
472 ///
473 /// # Examples
474 /// ```
475 /// use ckey::CKey;
476 ///
477 /// let k = CKey::from(0.1f64);
478 /// assert_eq!("0.100542101", String::from(k + 10_000_000_000_000_000i64))
479 /// ```
480 fn add(self, delta: i64) -> CKey {
481 let other = CKey::from(delta);
482 self + other
483 }
484}
485
486impl std::ops::Sub<i64> for CKey {
487 type Output = Self;
488
489 /// Sub a i64.
490 ///
491 /// The value is first converted to a key, considering key space
492 /// goes from 0 to 2^64.
493 ///
494 /// # Examples
495 /// ```
496 /// use ckey::CKey;
497 ///
498 /// let k = CKey::from(0.1f64);
499 /// assert_eq!("0.099457899", String::from(k - 10_000_000_000_000_000i64))
500 /// ```
501 fn sub(self, delta: i64) -> CKey {
502 let other = CKey::from(delta);
503 self - other
504 }
505}
506
507#[cfg(test)]
508mod tests {
509 use super::CKey;
510
511 #[test]
512 fn test_ckey_add() {
513 assert_eq!(
514 "0.300000000",
515 format!("{}", CKey::from(0.1f64) + CKey::from(0.2f64))
516 );
517 assert_eq!(
518 "0.000000000",
519 format!("{}", CKey::from(0.5f64) + CKey::from(0.5f64))
520 );
521 assert_eq!(
522 "0.100000000",
523 format!("{}", CKey::from(0.8f64) + CKey::from(0.3f64))
524 );
525 assert_eq!(
526 "0.300000000",
527 format!("{}", CKey::from(1.1f64) + CKey::from(0.2f64))
528 );
529 assert_eq!(
530 "0.400000000",
531 format!("{}", CKey::from(-0.1f64) + CKey::from(0.5f64))
532 );
533 }
534
535 #[test]
536 fn test_ckey_sub() {
537 assert_eq!(
538 "0.300000000",
539 format!("{}", CKey::from(0.4f64) - CKey::from(0.1f64))
540 );
541 assert_eq!(
542 "0.000000000",
543 format!("{}", CKey::from(0.9f64) - CKey::from(0.9f64))
544 );
545 assert_eq!(
546 "0.900000000",
547 format!("{}", CKey::from(0.2f64) - CKey::from(0.3f64))
548 );
549 assert_eq!(
550 "0.800000000",
551 format!("{}", CKey::from(1.1f64) - CKey::from(1.3f64))
552 );
553 assert_eq!(
554 "0.900000000",
555 format!("{}", CKey::from(0.2f64) - CKey::from(-0.7f64))
556 );
557 }
558
559 #[test]
560 fn test_ckey_add_f32() {
561 assert_eq!("0.300000004", format!("{}", CKey::from(0.1f32) + 0.2f32));
562 assert_eq!("0.000000000", format!("{}", CKey::from(0.5f32) + 0.5f32));
563 assert_eq!("0.100000024", format!("{}", CKey::from(0.8f32) + 0.3f32));
564 assert_eq!("0.300000027", format!("{}", CKey::from(1.1f32) + 0.2f32));
565 assert_eq!("0.399999976", format!("{}", CKey::from(-0.1f32) + 0.5f32));
566 }
567
568 #[test]
569 fn test_ckey_sub_f32() {
570 assert_eq!("0.300000004", format!("{}", CKey::from(0.4f32) - 0.1f32));
571 assert_eq!("0.000000000", format!("{}", CKey::from(0.9f32) - 0.9f32));
572 assert_eq!("0.899999991", format!("{}", CKey::from(0.2f32) - 0.3f32));
573 assert_eq!("0.800000072", format!("{}", CKey::from(1.1f32) - 1.3f32));
574 assert_eq!("0.899999991", format!("{}", CKey::from(0.2f32) - -0.7f32));
575 }
576
577 #[test]
578 fn test_ckey_add_f64() {
579 assert_eq!("0.300000000", format!("{}", CKey::from(0.1f64) + 0.2f64));
580 assert_eq!("0.000000000", format!("{}", CKey::from(0.5f64) + 0.5f64));
581 assert_eq!("0.100000000", format!("{}", CKey::from(0.8f64) + 0.3f64));
582 assert_eq!("0.300000000", format!("{}", CKey::from(1.1f64) + 0.2f64));
583 assert_eq!("0.400000000", format!("{}", CKey::from(-0.1f64) + 0.5f64));
584 }
585
586 #[test]
587 fn test_ckey_sub_f64() {
588 assert_eq!("0.300000000", format!("{}", CKey::from(0.4f64) - 0.1f64));
589 assert_eq!("0.000000000", format!("{}", CKey::from(0.9f64) - 0.9f64));
590 assert_eq!("0.900000000", format!("{}", CKey::from(0.2f64) - 0.3f64));
591 assert_eq!("0.800000000", format!("{}", CKey::from(1.1f64) - 1.3f64));
592 assert_eq!("0.900000000", format!("{}", CKey::from(0.2f64) - -0.7f64));
593 }
594
595 #[test]
596 fn test_ckey_add_sub_ui8() {
597 let mut k = CKey::zero();
598
599 // u8/i8
600 k = k + 10u8;
601 assert_eq!("0.039062500", format!("{}", k));
602 k = k - 20u8;
603 assert_eq!("0.960937500", format!("{}", k));
604 k = k + (-5i8);
605 assert_eq!("0.941406250", format!("{}", k));
606 k = k - (-15i8);
607 assert_eq!(CKey::zero(), k);
608
609 // u16/i16
610 k = k + 10_000u16;
611 assert_eq!("0.152587891", format!("{}", k));
612 k = k - 20_000u16;
613 assert_eq!("0.847412109", format!("{}", k));
614 k = k + (-5_000i16);
615 assert_eq!("0.771118164", format!("{}", k));
616 k = k - (-15_000i16);
617 assert_eq!(CKey::zero(), k);
618
619 // u32/i32
620 k = k + 100_000_000u32;
621 assert_eq!("0.023283064", format!("{}", k));
622 k = k - 200_000_000u32;
623 assert_eq!("0.976716936", format!("{}", k));
624 k = k + (-50_000_000i32);
625 assert_eq!("0.965075403", format!("{}", k));
626 k = k - (-150_000_000i32);
627 assert_eq!(CKey::zero(), k);
628
629 // u64/i64
630 k = k + 1_000_000_000_000_000_000u64;
631 assert_eq!("0.054210109", format!("{}", k));
632 k = k - 2_000_000_000_000_000_000u64;
633 assert_eq!("0.945789891", format!("{}", k));
634 k = k + (-500_000_000_000_000_000i64);
635 assert_eq!("0.918684837", format!("{}", k));
636 k = k - (-1_500_000_000_000_000_000i64);
637 assert_eq!(CKey::zero(), k);
638 }
639}