Skip to main content

fast_posit/posit/quire/
ops.rs

1#![allow(clippy::unused_unit)]
2
3use super::*;
4
5impl<
6  const N: u32,
7  const ES: u32,
8  Int: crate::Int,
9  const SIZE: usize,
10> core::ops::AddAssign<Posit<N, ES, Int>> for Quire<N, ES, SIZE> {
11  /// Standard: "[**qAddP**](https://posithub.org/docs/posit_standard-2.pdf#subsection.5.11)".
12  ///
13  /// # Example
14  ///
15  /// ```
16  /// # use fast_posit::*;
17  /// let mut quire = q16::from(p16::ONE);
18  /// quire += p16::round_from(0.42);
19  /// assert_eq!(p16::round_from(1.42), (&quire).round_into())
20  /// ```
21  fn add_assign(&mut self, rhs: Posit<N, ES, Int>) {
22    if rhs == Posit::ZERO {
23      ()
24    } else if crate::utl::unlikely(rhs == Posit::NAR) || crate::utl::unlikely(self.is_nar()) {
25      self.set_nar()
26    } else {
27      // SAFETY: `rhs` is not 0 or NaR
28      let decoded = unsafe { rhs.decode_regular() };
29      // SAFETY: `decoded` comes from `Posit::decode_regular`
30      unsafe { self.add_posit_kernel(decoded) }
31    }
32  }
33}
34
35impl<
36  const N: u32,
37  const ES: u32,
38  Int: crate::Int,
39  const SIZE: usize,
40> core::ops::AddAssign<&Posit<N, ES, Int>> for Quire<N, ES, SIZE> {
41  /// Standard: "[**qAddP**](https://posithub.org/docs/posit_standard-2.pdf#subsection.5.11)".
42  ///
43  /// # Example
44  ///
45  /// ```
46  /// # use fast_posit::*;
47  /// let mut quire = q16::from(p16::ONE);
48  /// quire += p16::round_from(0.42);
49  /// assert_eq!(p16::round_from(1.42), (&quire).round_into())
50  /// ```
51  #[inline]
52  fn add_assign(&mut self, rhs: &Posit<N, ES, Int>) {
53    *self += *rhs
54  }
55}
56
57impl<
58  const N: u32,
59  const ES: u32,
60  Int: crate::Int,
61  const SIZE: usize,
62> core::ops::SubAssign<Posit<N, ES, Int>> for Quire<N, ES, SIZE> {
63  /// Standard: "[**qSubP**](https://posithub.org/docs/posit_standard-2.pdf#subsection.5.11)".
64  ///
65  /// # Example
66  ///
67  /// ```
68  /// # use fast_posit::*;
69  /// let mut quire = q16::from(p16::ONE);
70  /// quire -= p16::round_from(0.42);
71  /// assert_eq!(p16::round_from(0.58), (&quire).round_into())
72  /// ```
73  #[inline]
74  fn sub_assign(&mut self, rhs: Posit<N, ES, Int>) {
75    *self += -rhs
76  }
77}
78
79impl<
80  const N: u32,
81  const ES: u32,
82  Int: crate::Int,
83  const SIZE: usize,
84> core::ops::SubAssign<&Posit<N, ES, Int>> for Quire<N, ES, SIZE> {
85  /// Standard: "[**qSubP**](https://posithub.org/docs/posit_standard-2.pdf#subsection.5.11)".
86  ///
87  /// # Example
88  ///
89  /// ```
90  /// # use fast_posit::*;
91  /// let mut quire = q16::from(p16::ONE);
92  /// quire -= p16::round_from(0.42);
93  /// assert_eq!(p16::round_from(0.58), (&quire).round_into())
94  /// ```
95  #[inline]
96  fn sub_assign(&mut self, rhs: &Posit<N, ES, Int>) {
97    *self += -*rhs
98  }
99}
100
101impl<
102  const N: u32,
103  const ES: u32,
104  const SIZE: usize,
105> Quire<N, ES, SIZE> {
106  /// Add the product of two posits to the quire, exactly, without rounding.
107  ///
108  /// Standard: "[**qMulAdd**](https://posithub.org/docs/posit_standard-2.pdf#subsection.5.11)".
109  pub fn add_prod<Int: crate::Int>(&mut self, a: Posit<N, ES, Int>, b: Posit<N, ES, Int>) {
110    if crate::utl::unlikely(a == Posit::NAR) || crate::utl::unlikely(b == Posit::NAR) {
111      self.set_nar()
112    } else if a == Posit::ZERO || b == Posit::ZERO || crate::utl::unlikely(self.is_nar()) {
113      ()
114    } else {
115      // SAFETY: neither `a` nor `b` are 0 or NaR
116      let a = unsafe { a.decode_regular() };
117      let b = unsafe { b.decode_regular() };
118      // SAFETY: `a` and `b` come from `Posit::decode_regular`
119      unsafe { self.add_posit_prod_kernel(a, b) }
120    }
121  }
122
123  /// Subtract the product of two posits from the quire, exactly, without rounding.
124  ///
125  /// Standard: "[**qMulSub**](https://posithub.org/docs/posit_standard-2.pdf#subsection.5.11)".
126  #[inline]
127  pub fn sub_prod<Int: crate::Int>(&mut self, a: Posit<N, ES, Int>, b: Posit<N, ES, Int>) {
128    self.add_prod(-a, b)
129  }
130}
131
132impl<
133  const N: u32,
134  const ES: u32,
135  const SIZE: usize,
136> core::ops::AddAssign<&Quire<N, ES, SIZE>> for Quire<N, ES, SIZE> {
137  /// Standard: "[**qAddQ**](https://posithub.org/docs/posit_standard-2.pdf#subsection.5.11)".
138  fn add_assign(&mut self, rhs: &Quire<N, ES, SIZE>) {
139    if crate::utl::unlikely(self.is_nar()) {
140      ()
141    } else if crate::utl::unlikely(rhs.is_nar()) {
142      self.set_nar()
143    } else {
144      // TODO replace `accumulate_slice` with `accumulate` if ever `min_generic_const_args` is
145      // stabilised!
146      // SAFETY: `Self::SIZE` == `quire::SIZE`.
147      unsafe { self.accumulate_slice(rhs.as_u64_array(), 0) }
148    }
149  }
150}
151
152/*impl<
153  const N: u32,
154  const ES: u32,
155  const SIZE: usize,
156> core::ops::SubAssign<&Quire<N, ES, SIZE>> for Quire<N, ES, SIZE> {
157  /// Standard: "[**qSubQ**](https://posithub.org/docs/posit_standard-2.pdf#subsection.5.11)".
158  fn sub_assign(&mut self, rhs: &Quire<N, ES, SIZE>) {
159    todo!()
160  }
161}*/
162
163impl<
164  const N: u32,
165  const ES: u32,
166  const SIZE: usize,
167> core::ops::Neg for Quire<N, ES, SIZE> {
168  type Output = Self;
169
170  fn neg(mut self) -> Self::Output {
171    // Two's complement negation is bitwise negating and adding 1.
172    let mut carry = true;
173    for i in self.as_u64_array_mut() {
174      *i = (!*i).wrapping_add(u64::from(carry));
175      carry &= *i == 0;
176    }
177    self
178  }
179}
180
181impl<
182  const N: u32,
183  const ES: u32,
184  const SIZE: usize,
185> Quire<N, ES, SIZE> {
186  /// Return `-self` if the quire value is negative, and `self` otherwise.
187  ///
188  /// Standard: "[**qAbs**](https://posithub.org/docs/posit_standard-2.pdf#subsection.5.11)".
189  pub fn abs(self) -> Self {
190    let is_negative = (self.as_u64_array()[Self::LEN_U64 - 1] as i64) < 0;
191    if is_negative {-self} else {self}
192  }
193}
194
195#[cfg(test)]
196mod tests {
197  use super::*;
198  use malachite::rational::Rational;
199  use proptest::prelude::*;
200
201  /// `Quire::from(posit) += posit`
202  mod posit_posit {
203    // TODO these tests are basic: they test for two posits a and b whether summing them on the quire
204    // yields the correct result. But more tests are needed: summing vectors of n posits, not just 2.
205    use super::*;
206
207    macro_rules! test_exhaustive {
208      ($name:ident, $posit:ty, $quire:ty) => {
209        #[test]
210        fn $name() {
211          for a in <$posit>::cases_exhaustive_all() {
212            for b in <$posit>::cases_exhaustive_all() {
213              let posit = a + b;
214              let mut quire = <$quire>::from(a);
215              quire += b;
216              assert!(super::rational::try_is_correct_rounded(Rational::try_from(quire), posit))
217            }
218          }
219        }
220      };
221    }
222
223    macro_rules! test_proptest {
224      ($name:ident, $posit:ty, $quire:ty) => {
225        proptest!{
226          #![proptest_config(ProptestConfig::with_cases(crate::PROPTEST_CASES))]
227          #[test]
228          fn $name(
229            a in <$posit>::cases_proptest_all(),
230            b in <$posit>::cases_proptest_all(),
231          ) {
232            let posit = a + b;
233            let mut quire = <$quire>::from(a);
234            quire += b;
235            assert!(super::rational::try_is_correct_rounded(Rational::try_from(quire), posit))
236          }
237        }
238      };
239    }
240
241    test_exhaustive!{posit_10_0_exhaustive, Posit<10, 0, i16>, Quire<10, 0, 128>}
242    test_exhaustive!{posit_10_1_exhaustive, Posit<10, 1, i16>, Quire<10, 1, 128>}
243    test_exhaustive!{posit_10_2_exhaustive, Posit<10, 2, i16>, Quire<10, 2, 128>}
244    test_exhaustive!{posit_10_3_exhaustive, Posit<10, 3, i16>, Quire<10, 3, 128>}
245    test_exhaustive!{posit_8_0_exhaustive, Posit<8, 0, i8>, Quire<8, 0, 128>}
246
247    test_exhaustive!{p8_exhaustive, crate::p8, crate::q8}
248    test_proptest!{p16_proptest, crate::p16, crate::q16}
249    test_proptest!{p32_proptest, crate::p32, crate::q32}
250    test_proptest!{p64_proptest, crate::p64, crate::q64}
251
252    test_exhaustive!{posit_3_0_exhaustive, Posit<3, 0, i8>, Quire<3, 0, 128>}
253    test_exhaustive!{posit_4_0_exhaustive, Posit<4, 0, i8>, Quire<4, 0, 128>}
254    test_exhaustive!{posit_4_1_exhaustive, Posit<4, 1, i8>, Quire<4, 1, 128>}
255  }
256
257  /// `quire += posit`
258  mod quire_posit {
259    use super::*;
260
261    /// Manual test for overflowing the quire sum limit on the positive side
262    #[test]
263    fn q8_overflow_positive() {
264      use crate::{p8, q8};
265      let mut quire = q8::MAX;
266      assert!(!quire.is_nar());
267      quire -= p8::ONE;
268      assert!(!quire.is_nar());
269      quire += p8::ONE;
270      assert!(!quire.is_nar());
271      quire += p8::ONE;
272      assert!(quire.is_nar());
273    }
274
275    /// Manual test for overflowing the quire sum limit on the negative side
276    #[test]
277    fn q8_overflow_negative() {
278      use crate::{p8, q8};
279      let mut quire = q8::MIN;
280      assert!(!quire.is_nar());
281      quire += p8::ONE;
282      assert!(!quire.is_nar());
283      quire -= p8::ONE;
284      assert!(!quire.is_nar());
285      quire -= p8::ONE;
286      assert!(quire.is_nar());
287    }
288
289    macro_rules! test_proptest {
290      ($name:ident, $posit:ty, $quire:ty) => {
291        proptest!{
292          #![proptest_config(ProptestConfig::with_cases(crate::PROPTEST_CASES))]
293          #[test]
294          fn $name(
295            q in <$quire>::cases_proptest_all(),
296            p in <$posit>::cases_proptest_all(),
297          ) {
298            let mut sum = q.clone();
299            sum += p;
300            match (Rational::try_from(q), Rational::try_from(p)) {
301              (Ok(q), Ok(p)) => assert!(super::rational::quire_is_correct_rounded(q + p, sum)),
302              _ => assert!(sum.is_nar()),
303            }
304          }
305        }
306      };
307    }
308
309    test_proptest!{posit_10_0_proptest, Posit<10, 0, i16>, Quire<10, 0, 128>}
310    test_proptest!{posit_10_1_proptest, Posit<10, 1, i16>, Quire<10, 1, 128>}
311    test_proptest!{posit_10_2_proptest, Posit<10, 2, i16>, Quire<10, 2, 128>}
312    test_proptest!{posit_10_3_proptest, Posit<10, 3, i16>, Quire<10, 3, 128>}
313    test_proptest!{posit_8_0_proptest, Posit<8, 0, i8>, Quire<8, 0, 128>}
314
315    test_proptest!{p8_proptest, crate::p8, crate::q8}
316    test_proptest!{p16_proptest, crate::p16, crate::q16}
317    test_proptest!{p32_proptest, crate::p32, crate::q32}
318    test_proptest!{p64_proptest, crate::p64, crate::q64}
319
320    test_proptest!{posit_3_0_proptest, Posit<3, 0, i8>, Quire<3, 0, 128>}
321    test_proptest!{posit_4_0_proptest, Posit<4, 0, i8>, Quire<4, 0, 128>}
322    test_proptest!{posit_4_1_proptest, Posit<4, 1, i8>, Quire<4, 1, 128>}
323  }
324
325  /// `quire += quire`
326  mod quire_quire {
327    use super::*;
328
329    #[test]
330    fn q32_overflow_positive() {
331      use crate::RoundFrom;
332      let mut quire = crate::q32::MAX;
333      assert!(!quire.is_nar());
334      quire += &crate::q32::from(crate::p32::round_from(1e-9));
335      assert!(quire.is_nar())
336    }
337
338    #[test]
339    fn q32_overflow_negative() {
340      use crate::RoundFrom;
341      let mut quire = crate::q32::MIN;
342      assert!(!quire.is_nar());
343      quire += &crate::q32::from(crate::p32::round_from(-1e-9));
344      assert!(quire.is_nar())
345    }
346
347    macro_rules! test_proptest {
348      ($name:ident, $quire:ty) => {
349        proptest!{
350          #![proptest_config(ProptestConfig::with_cases(crate::PROPTEST_CASES))]
351          #[test]
352          fn $name(
353            a in <$quire>::cases_proptest_all(),
354            b in <$quire>::cases_proptest_all(),
355          ) {
356            let mut sum = a.clone();
357            sum += &b;
358            match (Rational::try_from(a), Rational::try_from(b)) {
359              (Ok(a), Ok(b)) => assert!(super::rational::quire_is_correct_rounded(a + b, sum)),
360              _ => assert!(sum.is_nar()),
361            }
362          }
363        }
364      };
365    }
366
367    test_proptest!{posit_10_0_proptest, Quire<10, 0, 128>}
368    test_proptest!{posit_10_1_proptest, Quire<10, 1, 128>}
369    test_proptest!{posit_10_2_proptest, Quire<10, 2, 128>}
370    test_proptest!{posit_10_3_proptest, Quire<10, 3, 128>}
371    test_proptest!{posit_8_0_proptest, Quire<8, 0, 128>}
372
373    test_proptest!{p8_proptest, crate::q8}
374    test_proptest!{p16_proptest, crate::q16}
375    test_proptest!{p32_proptest, crate::q32}
376    test_proptest!{p64_proptest, crate::q64}
377
378    test_proptest!{posit_3_0_proptest, Quire<3, 0, 128>}
379    test_proptest!{posit_4_0_proptest, Quire<4, 0, 128>}
380    test_proptest!{posit_4_1_proptest, Quire<4, 1, 128>}
381  }
382
383  /// `quire += posit * posit`
384  mod quire_posit_posit {
385    use super::*;
386
387    macro_rules! test_proptest {
388      ($name:ident, $posit:ty, $quire:ty) => {
389        proptest!{
390          #![proptest_config(ProptestConfig::with_cases(crate::PROPTEST_CASES))]
391          #[test]
392          fn $name(
393            q in <$quire>::cases_proptest_all(),
394            a in <$posit>::cases_proptest_all(),
395            b in <$posit>::cases_proptest_all(),
396          ) {
397            let mut result = q.clone();
398            result.add_prod(a, b);
399            match (Rational::try_from(q), Rational::try_from(a), Rational::try_from(b)) {
400              (Ok(q), Ok(a), Ok(b)) => assert!(super::rational::quire_is_correct_rounded(q + a * b, result)),
401              _ => assert!(result.is_nar()),
402            }
403          }
404        }
405      };
406    }
407
408    test_proptest!{posit_10_0_proptest, Posit<10, 0, i16>, Quire<10, 0, 128>}
409    test_proptest!{posit_10_1_proptest, Posit<10, 1, i16>, Quire<10, 1, 128>}
410    test_proptest!{posit_10_2_proptest, Posit<10, 2, i16>, Quire<10, 2, 128>}
411    test_proptest!{posit_10_3_proptest, Posit<10, 3, i16>, Quire<10, 3, 128>}
412    test_proptest!{posit_8_0_proptest, Posit<8, 0, i8>, Quire<8, 0, 128>}
413
414    test_proptest!{p8_proptest, crate::p8, crate::q8}
415    test_proptest!{p16_proptest, crate::p16, crate::q16}
416    test_proptest!{p32_proptest, crate::p32, crate::q32}
417    // test_proptest!{p64_proptest, crate::p64, crate::q64}
418
419    test_proptest!{posit_3_0_proptest, Posit<3, 0, i8>, Quire<3, 0, 128>}
420    test_proptest!{posit_4_0_proptest, Posit<4, 0, i8>, Quire<4, 0, 128>}
421    test_proptest!{posit_4_1_proptest, Posit<4, 1, i8>, Quire<4, 1, 128>}
422  }
423
424  /// Dot products
425  mod quire_dot_product {
426    use super::*;
427
428    macro_rules! test_proptest {
429      ($name:ident, $posit:ty, $quire:ty) => {
430        proptest!{
431          #![proptest_config(ProptestConfig::with_cases(crate::PROPTEST_CASES / 50_000))]
432          #[test]
433          fn $name(
434            pairs in proptest::collection::vec(
435              (<$posit>::cases_proptest(), <$posit>::cases_proptest()),
436              50_000,
437            ),
438          ) {
439            let mut quire = <$quire>::ZERO;
440            let mut exact = Rational::from(0);
441            for (a, b) in pairs {
442              quire.add_prod(a, b);
443              exact += Rational::try_from(a).unwrap() * Rational::try_from(b).unwrap();
444              assert_eq!(Rational::try_from(quire.clone()), Ok(exact.clone()))
445            }
446          }
447        }
448      };
449    }
450
451    test_proptest!{posit_10_0_proptest, Posit<10, 0, i16>, Quire<10, 0, 128>}
452    test_proptest!{posit_10_1_proptest, Posit<10, 1, i16>, Quire<10, 1, 128>}
453    test_proptest!{posit_10_2_proptest, Posit<10, 2, i16>, Quire<10, 2, 128>}
454    test_proptest!{posit_10_3_proptest, Posit<10, 3, i16>, Quire<10, 3, 128>}
455    test_proptest!{posit_8_0_proptest, Posit<8, 0, i8>, Quire<8, 0, 128>}
456
457    test_proptest!{p8_proptest, crate::p8, crate::q8}
458    test_proptest!{p16_proptest, crate::p16, crate::q16}
459    test_proptest!{p32_proptest, crate::p32, crate::q32}
460    // test_proptest!{p64_proptest, crate::p64, crate::q64}
461
462    test_proptest!{posit_3_0_proptest, Posit<3, 0, i8>, Quire<3, 0, 128>}
463    test_proptest!{posit_4_0_proptest, Posit<4, 0, i8>, Quire<4, 0, 128>}
464    test_proptest!{posit_4_1_proptest, Posit<4, 1, i8>, Quire<4, 1, 128>}
465  }
466
467  /// -quire, quire.abs()
468  mod quire_neg {
469    use super::*;
470
471    macro_rules! test_proptest {
472      ($name:ident, $posit:ty, $quire:ty) => {
473        proptest!{
474          #![proptest_config(ProptestConfig::with_cases(crate::PROPTEST_CASES))]
475          #[test]
476          fn $name(quire in <$quire>::cases_proptest_all()) {
477            use malachite::base::num::arithmetic::traits::Abs;
478            assert_eq!(
479              Rational::try_from(-quire.clone()),
480              Rational::try_from(quire.clone()).map(|x| -x),
481            );
482            assert_eq!(
483              Rational::try_from(quire.clone().abs()),
484              Rational::try_from(quire.clone()).map(|x| x.abs()),
485            );
486          }
487        }
488      };
489    }
490
491    test_proptest!{posit_10_0_proptest, Posit<10, 0, i16>, Quire<10, 0, 128>}
492    test_proptest!{posit_10_1_proptest, Posit<10, 1, i16>, Quire<10, 1, 128>}
493    test_proptest!{posit_10_2_proptest, Posit<10, 2, i16>, Quire<10, 2, 128>}
494    test_proptest!{posit_10_3_proptest, Posit<10, 3, i16>, Quire<10, 3, 128>}
495    test_proptest!{posit_8_0_proptest, Posit<8, 0, i8>, Quire<8, 0, 128>}
496
497    test_proptest!{p8_proptest, crate::p8, crate::q8}
498    test_proptest!{p16_proptest, crate::p16, crate::q16}
499    test_proptest!{p32_proptest, crate::p32, crate::q32}
500    test_proptest!{p64_proptest, crate::p64, crate::q64}
501
502    test_proptest!{posit_3_0_proptest, Posit<3, 0, i8>, Quire<3, 0, 128>}
503    test_proptest!{posit_4_0_proptest, Posit<4, 0, i8>, Quire<4, 0, 128>}
504    test_proptest!{posit_4_1_proptest, Posit<4, 1, i8>, Quire<4, 1, 128>}
505  }
506}