1use crate::{
2 buffer::Buffer,
3 div_const::ConstLargeDivisor,
4 error::panic_divide_by_invalid_modulo,
5 gcd,
6 helper_macros::debug_assert_zero,
7 memory::MemoryAllocation,
8 primitive::{locate_top_word_plus_one, lowest_dword},
9 shift::{shl_in_place, shr_in_place},
10 Sign,
11};
12
13use core::ops::{Deref, Div, DivAssign};
14
15use super::{
16 add::negate_in_place,
17 repr::{Reduced, ReducedDword, ReducedLarge, ReducedRepr, ReducedWord},
18};
19use num_modular::Reducer;
20
21impl<'a> Reduced<'a> {
22 #[inline]
38 pub fn inv(&self) -> Option<Reduced<'a>> {
39 match self.repr() {
40 ReducedRepr::Single(raw, ring) => ring
41 .0
42 .inv(raw.0)
43 .map(|v| Reduced::from_single(ReducedWord(v), ring)),
44 ReducedRepr::Double(raw, ring) => ring
45 .0
46 .inv(raw.0)
47 .map(|v| Reduced::from_double(ReducedDword(v), ring)),
48 ReducedRepr::Large(raw, ring) => {
49 inv_large(ring, raw.clone()).map(|v| Reduced::from_large(v, ring))
50 }
51 }
52 }
53}
54
55fn inv_large(ring: &ConstLargeDivisor, mut raw: ReducedLarge) -> Option<ReducedLarge> {
56 let mut modulus = Buffer::from(ring.normalized_divisor.deref());
58 debug_assert_zero!(shr_in_place(&mut modulus, ring.shift));
59
60 debug_assert_zero!(shr_in_place(&mut raw.0, ring.shift));
62 let raw_len = locate_top_word_plus_one(&raw.0);
63
64 let (is_g_one, b_sign) = match raw_len {
66 0 => return None,
67 1 => {
68 let (g, _, b_sign) = gcd::gcd_ext_word(&mut modulus, *raw.0.first().unwrap());
69 (g == 1, b_sign)
70 }
71 2 => {
72 let (g, _, b_sign) = gcd::gcd_ext_dword(&mut modulus, lowest_dword(&raw.0));
73 (g == 1, b_sign)
74 }
75 _ => {
76 let mut allocation =
77 MemoryAllocation::new(gcd::memory_requirement_ext_exact(modulus.len(), raw_len));
78 let (g_len, b_len, b_sign) = gcd::gcd_ext_in_place(
79 &mut modulus,
80 &mut raw.0[..raw_len],
81 &mut allocation.memory(),
82 );
83 modulus[b_len..].fill(0);
84
85 (g_len == 1 && *raw.0.first().unwrap() == 1, b_sign)
87 }
88 };
89 if !is_g_one {
90 return None;
91 }
92
93 shl_in_place(&mut modulus, ring.shift);
95 let mut inv = ReducedLarge(modulus.into_boxed_slice());
96 debug_assert!(inv.is_valid(ring));
97 if b_sign == Sign::Negative {
98 negate_in_place(ring, &mut inv);
99 }
100 Some(inv)
101}
102
103impl<'a> Div<Reduced<'a>> for Reduced<'a> {
104 type Output = Reduced<'a>;
105
106 #[inline]
107 fn div(self, rhs: Reduced<'a>) -> Reduced<'a> {
108 (&self).div(&rhs)
109 }
110}
111
112impl<'a> Div<&Reduced<'a>> for Reduced<'a> {
113 type Output = Reduced<'a>;
114
115 #[inline]
116 fn div(self, rhs: &Reduced<'a>) -> Reduced<'a> {
117 (&self).div(rhs)
118 }
119}
120
121impl<'a> Div<Reduced<'a>> for &Reduced<'a> {
122 type Output = Reduced<'a>;
123
124 #[inline]
125 fn div(self, rhs: Reduced<'a>) -> Reduced<'a> {
126 self.div(&rhs)
127 }
128}
129
130impl<'a> Div<&Reduced<'a>> for &Reduced<'a> {
131 type Output = Reduced<'a>;
132
133 #[inline]
134 fn div(self, rhs: &Reduced<'a>) -> Reduced<'a> {
135 #[allow(clippy::suspicious_arithmetic_impl)]
137 match rhs.inv() {
138 None => panic_divide_by_invalid_modulo(),
139 Some(inv_rhs) => self * inv_rhs,
140 }
141 }
142}
143
144impl<'a> DivAssign<Reduced<'a>> for Reduced<'a> {
145 #[inline]
146 fn div_assign(&mut self, rhs: Reduced<'a>) {
147 self.div_assign(&rhs)
148 }
149}
150
151impl<'a> DivAssign<&Reduced<'a>> for Reduced<'a> {
152 #[inline]
153 fn div_assign(&mut self, rhs: &Reduced<'a>) {
154 *self = (&*self).div(rhs)
155 }
156}