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}