1use crate::integer::ciphertext::{IntegerRadixCiphertext, RadixCiphertext, SignedRadixCiphertext};
2use crate::integer::server_key::comparator::ZeroComparisonType;
3use crate::integer::{BooleanBlock, IntegerCiphertext, ServerKey};
4use crate::shortint::MessageModulus;
5use rayon::prelude::*;
6
7impl ServerKey {
8 pub fn unchecked_div_rem_parallelized<T>(&self, numerator: &T, divisor: &T) -> (T, T)
12 where
13 T: IntegerRadixCiphertext,
14 {
15 if T::IS_SIGNED {
16 let n = SignedRadixCiphertext::from_blocks(numerator.blocks().to_vec());
17 let d = SignedRadixCiphertext::from_blocks(divisor.blocks().to_vec());
18 let (q, r) = self.signed_unchecked_div_rem_parallelized(&n, &d);
19 let q = T::from_blocks(q.into_blocks());
20 let r = T::from_blocks(r.into_blocks());
21 (q, r)
22 } else {
23 let n = RadixCiphertext::from_blocks(numerator.blocks().to_vec());
24 let d = RadixCiphertext::from_blocks(divisor.blocks().to_vec());
25 let (q, r) = self.unsigned_unchecked_div_rem_parallelized(&n, &d);
26 let q = T::from_blocks(q.into_blocks());
27 let r = T::from_blocks(r.into_blocks());
28 (q, r)
29 }
30 }
31
32 pub fn unchecked_div_rem_floor_parallelized(
33 &self,
34 numerator: &SignedRadixCiphertext,
35 divisor: &SignedRadixCiphertext,
36 ) -> (SignedRadixCiphertext, SignedRadixCiphertext) {
37 let (quotient, remainder) = self.unchecked_div_rem_parallelized(numerator, divisor);
38
39 let (remainder_is_not_zero, remainder_and_divisor_signs_disagrees) = rayon::join(
40 || self.unchecked_scalar_ne_parallelized(&remainder, 0),
41 || {
42 let sign_bit_pos = self.key.message_modulus.0.ilog2() - 1;
43 let compare_sign_bits = |x, y| {
44 let x_sign_bit = (x >> sign_bit_pos) & 1;
45 let y_sign_bit = (y >> sign_bit_pos) & 1;
46 u64::from(x_sign_bit != y_sign_bit)
47 };
48 let lut = self.key.generate_lookup_table_bivariate(compare_sign_bits);
49 self.key.unchecked_apply_lookup_table_bivariate(
50 remainder.blocks().last().unwrap(),
51 divisor.blocks().last().unwrap(),
52 &lut,
53 )
54 },
55 );
56
57 let mut condition = remainder_is_not_zero.0;
58 let mut remainder_plus_divisor = remainder.clone();
59 let mut quotient_minus_one = quotient.clone();
60 rayon::scope(|s| {
61 s.spawn(|_| {
62 self.key
63 .add_assign(&mut condition, &remainder_and_divisor_signs_disagrees);
64 });
65 s.spawn(|_| self.add_assign_parallelized(&mut remainder_plus_divisor, divisor));
66 s.spawn(|_| self.scalar_sub_assign_parallelized(&mut quotient_minus_one, 1));
67 });
68
69 let (quotient, remainder) = rayon::join(
70 || {
71 self.unchecked_programmable_if_then_else_parallelized(
72 &condition,
73 "ient_minus_one,
74 "ient,
75 |x| x == 2,
76 true,
77 )
78 },
79 || {
80 self.unchecked_programmable_if_then_else_parallelized(
81 &condition,
82 &remainder_plus_divisor,
83 &remainder,
84 |x| x == 2,
85 true,
86 )
87 },
88 );
89
90 (quotient, remainder)
91 }
92
93 fn unsigned_div_rem_block_by_block_2_2(
94 &self,
95 numerator: &RadixCiphertext,
96 divisor: &RadixCiphertext,
97 ) -> (RadixCiphertext, RadixCiphertext) {
98 let num_bits_in_block = self.message_modulus().0.ilog2() as usize;
99 assert!(
100 num_bits_in_block == 2 && self.carry_modulus().0 == 4,
101 "This algorithm only works for 2_2 parameters"
102 );
103
104 let num_blocks = numerator.blocks.len();
105
106 let mut remainder = numerator.clone();
107 let mut quotient_blocks = Vec::with_capacity(num_blocks);
108
109 let mut d1 = divisor.clone();
110
111 let (d2, d3) = rayon::join(
112 || {
113 let mut d2 = self.extend_radix_with_trivial_zero_blocks_msb(divisor, 1);
114 self.scalar_left_shift_assign_parallelized(&mut d2, 1);
115 d2
116 },
117 || {
118 self.extend_radix_with_trivial_zero_blocks_msb_assign(&mut d1, 1);
119 let mut d4 = self.blockshift(&d1, 1);
120 self.sub_assign_parallelized(&mut d4, &d1);
121 self.trim_radix_blocks_msb_assign(&mut d1, 1);
122 d4 },
124 );
125
126 let zero_out_if_not_1_lut = (
133 self.key.generate_lookup_table(|x| {
134 let block = x / 2;
135 let condition = (x & 1) == 1;
136
137 block * u64::from(condition)
138 }),
139 2u8,
140 );
141
142 let zero_out_if_not_2_lut = (
149 self.key.generate_lookup_table(|x| {
150 let block = x / 3;
151 let condition = (x % 3) == 2;
152
153 block * u64::from(condition)
154 }),
155 3u8,
156 );
157
158 let quotient_block_luts = [
160 self.key.generate_lookup_table(|cond| u64::from(cond == 2)),
163 self.key
166 .generate_lookup_table(|cond| u64::from(cond == 2) * 2),
167 self.key.generate_lookup_table(|cond| cond * 3),
170 ];
171
172 for block_index in (0..num_blocks).rev() {
173 let low1 = RadixCiphertext::from(d1.blocks[..num_blocks - block_index].to_vec());
174 let low2 = RadixCiphertext::from(d2.blocks[..num_blocks - block_index].to_vec());
175 let low3 = RadixCiphertext::from(d3.blocks[..num_blocks - block_index].to_vec());
176 let mut rem = RadixCiphertext::from(remainder.blocks[block_index..].to_vec());
177
178 let (mut sub_results, cmps) = rayon::join(
179 || {
180 [&low3, &low2, &low1]
181 .into_par_iter()
182 .map(|rhs| self.unsigned_overflowing_sub_parallelized(&rem, rhs))
183 .collect::<Vec<_>>()
184 },
185 || {
186 [
187 &d3.blocks[num_blocks - block_index..],
188 &d2.blocks[num_blocks - block_index..],
189 &d1.blocks[num_blocks - block_index..],
190 ]
191 .into_par_iter()
192 .map(|blocks| {
193 let mut b = BooleanBlock::new_unchecked(self.are_all_blocks_zero(blocks));
194 self.boolean_bitnot_assign(&mut b);
195 b
196 })
197 .collect::<Vec<_>>()
198 },
199 );
200
201 let (mut r1, mut o1) = sub_results.pop().unwrap();
202 let (mut r2, mut o2) = sub_results.pop().unwrap();
203 let (mut r3, mut o3) = sub_results.pop().unwrap();
204
205 [&mut o3, &mut o2, &mut o1]
206 .into_par_iter()
207 .zip(cmps.par_iter())
208 .for_each(|(ox, cmpx)| {
209 self.boolean_bitor_assign(ox, cmpx);
210 });
211
212 let c3 = self.boolean_bitnot(&o3).0;
218 let c2 = {
219 let mut c2 = self.boolean_bitnot(&o2).0;
220 self.key.unchecked_add_assign(&mut c2, &o3.0);
221 c2
222 };
223 let c1 = {
224 let mut c1 = self.boolean_bitnot(&o1).0;
225 self.key.unchecked_add_assign(&mut c1, &o2.0);
226 c1
227 };
228 let c0 = o1.0;
229
230 let (_, [q1, q2, q3]) = rayon::join(
231 || {
232 [&c3, &c2, &c1, &c0]
233 .into_par_iter()
234 .zip([&mut r3, &mut r2, &mut r1, &mut rem])
235 .zip([
236 &zero_out_if_not_1_lut,
237 &zero_out_if_not_2_lut,
238 &zero_out_if_not_2_lut,
239 &zero_out_if_not_1_lut,
240 ])
241 .for_each(|((cx, rx), (lut, factor))| {
242 rx.blocks.par_iter_mut().for_each(|block| {
244 self.key.unchecked_scalar_mul_assign(block, *factor);
245 self.key.unchecked_add_assign(block, cx);
246 self.key.apply_lookup_table_assign(block, lut);
247 });
248 });
249 },
250 || {
251 let mut qs = [c1.clone(), c2.clone(), c3.clone()];
252 qs.par_iter_mut()
253 .zip("ient_block_luts)
254 .for_each(|(qx, lut)| {
255 self.key.apply_lookup_table_assign(qx, lut);
256 });
257 qs
258 },
259 );
260
261 for rx in [&r3, &r2, &r1] {
263 self.unchecked_add_assign(&mut rem, rx);
264 }
265
266 let mut q = q1;
268 for qx in [q2, q3] {
269 self.key.unchecked_add_assign(&mut q, &qx);
270 }
271
272 rayon::join(
273 || {
274 rem.blocks.par_iter_mut().for_each(|block| {
275 self.key.message_extract_assign(block);
276 });
277 },
278 || {
279 self.key.message_extract_assign(&mut q);
280 },
281 );
282
283 remainder.blocks[block_index..].clone_from_slice(&rem.blocks);
284 quotient_blocks.push(q);
285 }
286
287 quotient_blocks.reverse();
288
289 (RadixCiphertext::from(quotient_blocks), remainder)
290 }
291
292 fn unsigned_unchecked_div_rem_parallelized(
293 &self,
294 numerator: &RadixCiphertext,
295 divisor: &RadixCiphertext,
296 ) -> (RadixCiphertext, RadixCiphertext) {
297 assert_eq!(
298 numerator.blocks.len(),
299 divisor.blocks.len(),
300 "numerator and divisor must have same number of blocks"
301 );
302
303 if self.message_modulus().0 == 4 && self.carry_modulus().0 == 4 {
304 return self.unsigned_div_rem_block_by_block_2_2(numerator, divisor);
305 }
306
307 assert_eq!(
323 numerator.blocks.len(),
324 divisor.blocks.len(),
325 "numerator and divisor must have same number of blocks \
326 numerator: {} blocks, divisor: {} blocks",
327 numerator.blocks.len(),
328 divisor.blocks.len(),
329 );
330 assert!(
331 self.key.message_modulus.0.is_power_of_two(),
332 "The message modulus ({}) needs to be a power of two",
333 self.key.message_modulus.0
334 );
335 assert!(
336 numerator.block_carries_are_empty(),
337 "The numerator must have its carries empty"
338 );
339 assert!(
340 divisor.block_carries_are_empty(),
341 "The numerator must have its carries empty"
342 );
343 assert!(numerator
344 .blocks()
345 .iter()
346 .all(|block| block.message_modulus == self.key.message_modulus
347 && block.carry_modulus == self.key.carry_modulus));
348 assert!(divisor
349 .blocks()
350 .iter()
351 .all(|block| block.message_modulus == self.key.message_modulus
352 && block.carry_modulus == self.key.carry_modulus));
353
354 let num_blocks = numerator.blocks.len();
355 let num_bits_in_message = self.key.message_modulus.0.ilog2() as u64;
356 let total_bits = num_bits_in_message * num_blocks as u64;
357
358 let mut quotient: RadixCiphertext = self.create_trivial_zero_radix(num_blocks);
359 let mut remainder1: RadixCiphertext = self.create_trivial_zero_radix(num_blocks);
360 let mut remainder2: RadixCiphertext = self.create_trivial_zero_radix(num_blocks);
361
362 let mut numerator_block_stack = numerator.blocks.clone();
363
364 let merge_overflow_flags_luts = (0..num_bits_in_message)
373 .map(|bit_position_in_block| {
374 self.key.generate_lookup_table_bivariate(|x, y| {
375 u64::from(x == 0 && y == 0) << bit_position_in_block
376 })
377 })
378 .collect::<Vec<_>>();
379
380 for i in (0..total_bits as usize).rev() {
381 let block_of_bit = i / num_bits_in_message as usize;
382 let pos_in_block = i % num_bits_in_message as usize;
383
384 let msb_bit_set = total_bits as usize - 1 - i;
387
388 let last_non_trivial_block = msb_bit_set / num_bits_in_message as usize;
389 let first_trivial_block = last_non_trivial_block + 1;
393
394 let mut interesting_remainder1 =
399 RadixCiphertext::from(remainder1.blocks[..=last_non_trivial_block].to_vec());
400 let mut interesting_remainder2 =
401 RadixCiphertext::from(remainder2.blocks[..=last_non_trivial_block].to_vec());
402 let mut interesting_divisor =
403 RadixCiphertext::from(divisor.blocks[..=last_non_trivial_block].to_vec());
404 let mut divisor_ms_blocks = RadixCiphertext::from(
405 divisor.blocks[((msb_bit_set + 1) / num_bits_in_message as usize)..].to_vec(),
406 );
407
408 let mut trim_last_interesting_divisor_bits = || {
415 if (msb_bit_set + 1).is_multiple_of(num_bits_in_message as usize) {
416 return;
417 }
418 let pos_in_block = msb_bit_set as u64 % num_bits_in_message;
424
425 let shift_amount = num_bits_in_message - (pos_in_block + 1);
430 let full_message_mask = self.key.message_modulus.0 - 1;
432 let shifted_mask = full_message_mask >> shift_amount;
434
435 let masking_lut = self.key.generate_lookup_table(|x| x & shifted_mask);
436 self.key.apply_lookup_table_assign(
437 interesting_divisor.blocks.last_mut().unwrap(),
438 &masking_lut,
439 );
440 };
441
442 let mut trim_first_divisor_ms_bits = || {
443 if divisor_ms_blocks.blocks.is_empty()
444 || (msb_bit_set + 1).is_multiple_of(num_bits_in_message as usize)
445 {
446 return;
447 }
448 let pos_in_block = msb_bit_set as u64 % num_bits_in_message;
456
457 let shift_amount = pos_in_block + 1;
463 let full_message_mask = self.key.message_modulus.0 - 1;
464 let shifted_mask = full_message_mask << shift_amount;
465 let shifted_mask = shifted_mask & full_message_mask;
468
469 let masking_lut = self.key.generate_lookup_table(|x| x & shifted_mask);
470 self.key.apply_lookup_table_assign(
471 divisor_ms_blocks.blocks.first_mut().unwrap(),
472 &masking_lut,
473 );
474 };
475
476 let mut left_shift_interesting_remainder1 = || {
486 let numerator_block = numerator_block_stack
487 .pop()
488 .expect("internal error: empty numerator block stack in div");
489 interesting_remainder1.blocks.insert(0, numerator_block);
491 self.unchecked_scalar_left_shift_assign_parallelized(
492 &mut interesting_remainder1,
493 1,
494 );
495
496 interesting_remainder1.blocks.rotate_left(1);
499 let numerator_block = interesting_remainder1.blocks.pop().unwrap();
501 if pos_in_block != 0 {
502 numerator_block_stack.push(numerator_block);
505 }
506 };
507
508 let mut left_shift_interesting_remainder2 = || {
509 self.unchecked_scalar_left_shift_assign_parallelized(
510 &mut interesting_remainder2,
511 1,
512 );
513 };
514
515 let tasks: [&mut (dyn FnMut() + Send + Sync); 4] = [
516 &mut trim_last_interesting_divisor_bits,
517 &mut trim_first_divisor_ms_bits,
518 &mut left_shift_interesting_remainder1,
519 &mut left_shift_interesting_remainder2,
520 ];
521 tasks.into_par_iter().for_each(|task| task());
522
523 let mut merged_interesting_remainder = interesting_remainder1;
528 self.unchecked_add_assign(&mut merged_interesting_remainder, &interesting_remainder2);
529
530 let do_overflowing_sub = || {
531 self.unchecked_unsigned_overflowing_sub_parallelized(
532 &merged_interesting_remainder,
533 &interesting_divisor,
534 )
535 };
536
537 let check_divisor_upper_blocks = || {
538 let trivial_blocks = &divisor_ms_blocks.blocks;
540 if trivial_blocks.is_empty() {
541 self.key.create_trivial(0)
542 } else {
543 let tmp = self
547 .compare_blocks_with_zero(trivial_blocks, ZeroComparisonType::Difference);
548 self.is_at_least_one_comparisons_block_true(tmp)
549 }
550 };
551
552 let create_clean_version_of_merged_remainder = || {
555 RadixCiphertext::from_blocks(
556 merged_interesting_remainder
557 .blocks
558 .par_iter()
559 .map(|b| self.key.message_extract(b))
560 .collect(),
561 )
562 };
563
564 let (
566 (mut new_remainder, subtraction_overflowed),
567 (at_least_one_upper_block_is_non_zero, mut cleaned_merged_interesting_remainder),
568 ) = rayon::join(do_overflowing_sub, || {
569 let (r1, r2) = rayon::join(
570 check_divisor_upper_blocks,
571 create_clean_version_of_merged_remainder,
572 );
573
574 (r1, r2)
575 });
576 drop(merged_interesting_remainder);
578
579 let overflow_sum = self.key.unchecked_add(
580 subtraction_overflowed.as_ref(),
581 &at_least_one_upper_block_is_non_zero,
582 );
583 let overflow_happened = |overflow_sum: u64| overflow_sum != 0;
585 let overflow_did_not_happen = |overflow_sum: u64| !overflow_happened(overflow_sum);
586
587 assert!(overflow_sum.degree.get() <= 2); let factor = MessageModulus(overflow_sum.degree.get() + 1);
591 let mut conditionally_zero_out_merged_interesting_remainder = || {
592 let zero_out_if_overflow_did_not_happen =
593 self.key.generate_lookup_table_bivariate_with_factor(
594 |block, overflow_sum| {
595 if overflow_did_not_happen(overflow_sum) {
596 0
597 } else {
598 block
599 }
600 },
601 factor,
602 );
603 cleaned_merged_interesting_remainder
604 .blocks_mut()
605 .par_iter_mut()
606 .for_each(|block| {
607 self.key.unchecked_apply_lookup_table_bivariate_assign(
608 block,
609 &overflow_sum,
610 &zero_out_if_overflow_did_not_happen,
611 );
612 });
613 };
614
615 let mut conditionally_zero_out_merged_new_remainder = || {
616 let zero_out_if_overflow_happened =
617 self.key.generate_lookup_table_bivariate_with_factor(
618 |block, overflow_sum| {
619 if overflow_happened(overflow_sum) {
620 0
621 } else {
622 block
623 }
624 },
625 factor,
626 );
627 new_remainder.blocks_mut().par_iter_mut().for_each(|block| {
628 self.key.unchecked_apply_lookup_table_bivariate_assign(
629 block,
630 &overflow_sum,
631 &zero_out_if_overflow_happened,
632 );
633 });
634 };
635
636 let mut set_quotient_bit = || {
637 let did_not_overflow = self.key.unchecked_apply_lookup_table_bivariate(
638 subtraction_overflowed.as_ref(),
639 &at_least_one_upper_block_is_non_zero,
640 &merge_overflow_flags_luts[pos_in_block],
641 );
642
643 self.key
644 .unchecked_add_assign(&mut quotient.blocks[block_of_bit], &did_not_overflow);
645 };
646
647 let tasks: [&mut (dyn FnMut() + Send + Sync); 3] = [
648 &mut conditionally_zero_out_merged_interesting_remainder,
649 &mut conditionally_zero_out_merged_new_remainder,
650 &mut set_quotient_bit,
651 ];
652 tasks.into_par_iter().for_each(|task| task());
653
654 assert_eq!(
655 remainder1.blocks[..first_trivial_block].len(),
656 cleaned_merged_interesting_remainder.blocks.len()
657 );
658 assert_eq!(
659 remainder2.blocks[..first_trivial_block].len(),
660 new_remainder.blocks.len()
661 );
662 remainder1.blocks[..first_trivial_block]
663 .iter_mut()
664 .zip(cleaned_merged_interesting_remainder.blocks.iter())
665 .for_each(|(remainder_block, new_value)| {
666 remainder_block.clone_from(new_value);
667 });
668 remainder2.blocks[..first_trivial_block]
669 .iter_mut()
670 .zip(new_remainder.blocks.iter())
671 .for_each(|(remainder_block, new_value)| {
672 remainder_block.clone_from(new_value);
673 });
674 }
675
676 rayon::join(
679 || {
680 remainder1
681 .blocks_mut()
682 .par_iter_mut()
683 .zip(remainder2.blocks.par_iter())
684 .for_each(|(r1_block, r2_block)| {
685 self.key.unchecked_add_assign(r1_block, r2_block);
686 self.key.message_extract_assign(r1_block);
687 });
688 },
689 || {
690 quotient.blocks_mut().par_iter_mut().for_each(|block| {
691 self.key.message_extract_assign(block);
692 });
693 },
694 );
695
696 (quotient, remainder1)
697 }
698
699 fn signed_unchecked_div_rem_parallelized(
700 &self,
701 numerator: &SignedRadixCiphertext,
702 divisor: &SignedRadixCiphertext,
703 ) -> (SignedRadixCiphertext, SignedRadixCiphertext) {
704 assert_eq!(
705 numerator.blocks.len(),
706 divisor.blocks.len(),
707 "numerator and divisor must have same length"
708 );
709 let (positive_numerator, positive_divisor) = rayon::join(
710 || {
711 let positive_numerator = self.unchecked_abs_parallelized(numerator);
712 RadixCiphertext::from_blocks(positive_numerator.into_blocks())
713 },
714 || {
715 let positive_divisor = self.unchecked_abs_parallelized(divisor);
716 RadixCiphertext::from_blocks(positive_divisor.into_blocks())
717 },
718 );
719
720 let ((quotient, remainder), sign_bits_are_different) = rayon::join(
721 || self.unsigned_unchecked_div_rem_parallelized(&positive_numerator, &positive_divisor),
722 || {
723 let sign_bit_pos = self.key.message_modulus.0.ilog2() - 1;
724 let compare_sign_bits = |x, y| {
725 let x_sign_bit = (x >> sign_bit_pos) & 1;
726 let y_sign_bit = (y >> sign_bit_pos) & 1;
727 u64::from(x_sign_bit != y_sign_bit)
728 };
729 let lut = self.key.generate_lookup_table_bivariate(compare_sign_bits);
730 self.key.unchecked_apply_lookup_table_bivariate(
731 numerator.blocks().last().unwrap(),
732 divisor.blocks().last().unwrap(),
733 &lut,
734 )
735 },
736 );
737
738 let (quotient, remainder) = rayon::join(
742 || {
743 let negated_quotient = self.neg_parallelized("ient);
744
745 let quotient = self.unchecked_programmable_if_then_else_parallelized(
746 &sign_bits_are_different,
747 &negated_quotient,
748 "ient,
749 |x| x == 1,
750 true,
751 );
752 SignedRadixCiphertext::from_blocks(quotient.into_blocks())
753 },
754 || {
755 let negated_remainder = self.neg_parallelized(&remainder);
756
757 let sign_block = numerator.blocks().last().unwrap();
758 let sign_bit_pos = self.key.message_modulus.0.ilog2() - 1;
759
760 let remainder = self.unchecked_programmable_if_then_else_parallelized(
761 sign_block,
762 &negated_remainder,
763 &remainder,
764 |sign_block| (sign_block >> sign_bit_pos) == 1,
765 true,
766 );
767 SignedRadixCiphertext::from_blocks(remainder.into_blocks())
768 },
769 );
770
771 (quotient, remainder)
772 }
773
774 pub fn div_rem_parallelized<T>(&self, numerator: &T, divisor: &T) -> (T, T)
814 where
815 T: IntegerRadixCiphertext,
816 {
817 let mut tmp_numerator;
818 let mut tmp_divisor;
819
820 let (numerator, divisor) = match (
821 numerator.block_carries_are_empty(),
822 divisor.block_carries_are_empty(),
823 ) {
824 (true, true) => (numerator, divisor),
825 (true, false) => {
826 tmp_divisor = divisor.clone();
827 self.full_propagate_parallelized(&mut tmp_divisor);
828 (numerator, &tmp_divisor)
829 }
830 (false, true) => {
831 tmp_numerator = numerator.clone();
832 self.full_propagate_parallelized(&mut tmp_numerator);
833 (&tmp_numerator, divisor)
834 }
835 (false, false) => {
836 tmp_divisor = divisor.clone();
837 tmp_numerator = numerator.clone();
838 rayon::join(
839 || self.full_propagate_parallelized(&mut tmp_numerator),
840 || self.full_propagate_parallelized(&mut tmp_divisor),
841 );
842 (&tmp_numerator, &tmp_divisor)
843 }
844 };
845
846 self.unchecked_div_rem_parallelized(numerator, divisor)
847 }
848
849 pub fn smart_div_rem_parallelized<T>(&self, numerator: &mut T, divisor: &mut T) -> (T, T)
850 where
851 T: IntegerRadixCiphertext,
852 {
853 rayon::join(
854 || {
855 if !numerator.block_carries_are_empty() {
856 self.full_propagate_parallelized(numerator);
857 }
858 },
859 || {
860 if !divisor.block_carries_are_empty() {
861 self.full_propagate_parallelized(divisor);
862 }
863 },
864 );
865 self.unchecked_div_rem_parallelized(numerator, divisor)
866 }
867
868 pub fn unchecked_div_assign_parallelized<T>(&self, numerator: &mut T, divisor: &T)
873 where
874 T: IntegerRadixCiphertext,
875 {
876 let (q, _r) = self.unchecked_div_rem_parallelized(numerator, divisor);
877 *numerator = q;
878 }
879
880 pub fn unchecked_div_parallelized<T>(&self, numerator: &T, divisor: &T) -> T
881 where
882 T: IntegerRadixCiphertext,
883 {
884 let (q, _r) = self.unchecked_div_rem_parallelized(numerator, divisor);
885 q
886 }
887
888 pub fn smart_div_assign_parallelized<T>(&self, numerator: &mut T, divisor: &mut T)
889 where
890 T: IntegerRadixCiphertext,
891 {
892 let (q, _r) = self.smart_div_rem_parallelized(numerator, divisor);
893 *numerator = q;
894 }
895
896 pub fn smart_div_parallelized<T>(&self, numerator: &mut T, divisor: &mut T) -> T
897 where
898 T: IntegerRadixCiphertext,
899 {
900 let (q, _r) = self.smart_div_rem_parallelized(numerator, divisor);
901 q
902 }
903
904 pub fn div_assign_parallelized<T>(&self, numerator: &mut T, divisor: &T)
905 where
906 T: IntegerRadixCiphertext,
907 {
908 let mut tmp_divisor;
909
910 let (numerator, divisor) = match (
911 numerator.block_carries_are_empty(),
912 divisor.block_carries_are_empty(),
913 ) {
914 (true, true) => (numerator, divisor),
915 (true, false) => {
916 tmp_divisor = divisor.clone();
917 self.full_propagate_parallelized(&mut tmp_divisor);
918 (numerator, &tmp_divisor)
919 }
920 (false, true) => {
921 self.full_propagate_parallelized(numerator);
922 (numerator, divisor)
923 }
924 (false, false) => {
925 tmp_divisor = divisor.clone();
926 rayon::join(
927 || self.full_propagate_parallelized(numerator),
928 || self.full_propagate_parallelized(&mut tmp_divisor),
929 );
930 (numerator, &tmp_divisor)
931 }
932 };
933
934 let (q, _r) = self.unchecked_div_rem_parallelized(numerator, divisor);
935 *numerator = q;
936 }
937
938 pub fn div_parallelized<T>(&self, numerator: &T, divisor: &T) -> T
968 where
969 T: IntegerRadixCiphertext,
970 {
971 let (q, _r) = self.div_rem_parallelized(numerator, divisor);
972 q
973 }
974
975 pub fn unchecked_rem_assign_parallelized<T>(&self, numerator: &mut T, divisor: &T)
980 where
981 T: IntegerRadixCiphertext,
982 {
983 let (_q, r) = self.unchecked_div_rem_parallelized(numerator, divisor);
984 *numerator = r;
985 }
986
987 pub fn unchecked_rem_parallelized<T>(&self, numerator: &T, divisor: &T) -> T
988 where
989 T: IntegerRadixCiphertext,
990 {
991 let (_q, r) = self.unchecked_div_rem_parallelized(numerator, divisor);
992 r
993 }
994
995 pub fn smart_rem_assign_parallelized<T>(&self, numerator: &mut T, divisor: &mut T)
996 where
997 T: IntegerRadixCiphertext,
998 {
999 let (_q, r) = self.smart_div_rem_parallelized(numerator, divisor);
1000 *numerator = r;
1001 }
1002
1003 pub fn smart_rem_parallelized<T>(&self, numerator: &mut T, divisor: &mut T) -> T
1004 where
1005 T: IntegerRadixCiphertext,
1006 {
1007 let (_q, r) = self.smart_div_rem_parallelized(numerator, divisor);
1008 r
1009 }
1010
1011 pub fn rem_assign_parallelized<T>(&self, numerator: &mut T, divisor: &T)
1012 where
1013 T: IntegerRadixCiphertext,
1014 {
1015 let mut tmp_divisor;
1016
1017 let (numerator, divisor) = match (
1018 numerator.block_carries_are_empty(),
1019 divisor.block_carries_are_empty(),
1020 ) {
1021 (true, true) => (numerator, divisor),
1022 (true, false) => {
1023 tmp_divisor = divisor.clone();
1024 self.full_propagate_parallelized(&mut tmp_divisor);
1025 (numerator, &tmp_divisor)
1026 }
1027 (false, true) => {
1028 self.full_propagate_parallelized(numerator);
1029 (numerator, divisor)
1030 }
1031 (false, false) => {
1032 tmp_divisor = divisor.clone();
1033 rayon::join(
1034 || self.full_propagate_parallelized(numerator),
1035 || self.full_propagate_parallelized(&mut tmp_divisor),
1036 );
1037 (numerator, &tmp_divisor)
1038 }
1039 };
1040
1041 let (_q, r) = self.unchecked_div_rem_parallelized(numerator, divisor);
1042 *numerator = r;
1043 }
1044
1045 pub fn rem_parallelized<T>(&self, numerator: &T, divisor: &T) -> T
1075 where
1076 T: IntegerRadixCiphertext,
1077 {
1078 let (_q, r) = self.div_rem_parallelized(numerator, divisor);
1079 r
1080 }
1081
1082 pub fn checked_div_rem_parallelized<T>(
1115 &self,
1116 numerator: &T,
1117 divisor: &T,
1118 ) -> (T, T, BooleanBlock)
1119 where
1120 T: IntegerRadixCiphertext,
1121 {
1122 let ((q, r), div_by_0) = rayon::join(
1123 || self.div_rem_parallelized(numerator, divisor),
1124 || self.are_all_blocks_zero(divisor.blocks()),
1125 );
1126
1127 (q, r, BooleanBlock::new_unchecked(div_by_0))
1128 }
1129
1130 pub fn checked_div_parallelized<T>(&self, numerator: &T, divisor: &T) -> (T, BooleanBlock)
1164 where
1165 T: IntegerRadixCiphertext,
1166 {
1167 let (q, div_by_0) = rayon::join(
1168 || self.div_parallelized(numerator, divisor),
1169 || self.are_all_blocks_zero(divisor.blocks()),
1170 );
1171
1172 (q, BooleanBlock::new_unchecked(div_by_0))
1173 }
1174
1175 pub fn checked_rem_parallelized<T>(&self, numerator: &T, divisor: &T) -> (T, BooleanBlock)
1209 where
1210 T: IntegerRadixCiphertext,
1211 {
1212 let (r, rem_by_0) = rayon::join(
1213 || self.rem_parallelized(numerator, divisor),
1214 || self.are_all_blocks_zero(divisor.blocks()),
1215 );
1216
1217 (r, BooleanBlock::new_unchecked(rem_by_0))
1218 }
1219}