arnaucube
arnaucube/phantom-zonePublic Self-hosted repository
phantom-zone/src/bool/mp_api.rs
src/bool/mp_api.rsRaw
  1use std::{cell::RefCell, sync::OnceLock};
  2
  3use crate::{
  4    backend::{ModularOpsU64, ModulusPowerOf2},
  5    ntt::NttBackendU64,
  6    random::{DefaultSecureRng, NewWithSeed},
  7    utils::{Global, WithLocal},
  8};
  9
 10use super::{evaluator::InteractiveMultiPartyCrs, keys::*, parameters::*, ClientKey};
 11
 12pub(crate) type BoolEvaluator = super::evaluator::BoolEvaluator<
 13    Vec<Vec<u64>>,
 14    NttBackendU64,
 15    ModularOpsU64<CiphertextModulus<u64>>,
 16    ModulusPowerOf2<CiphertextModulus<u64>>,
 17    ShoupServerKeyEvaluationDomain<Vec<Vec<u64>>>,
 18>;
 19
 20thread_local! {
 21    static BOOL_EVALUATOR: RefCell<Option<BoolEvaluator>> = RefCell::new(None);
 22
 23}
 24static BOOL_SERVER_KEY: OnceLock<ShoupServerKeyEvaluationDomain<Vec<Vec<u64>>>> = OnceLock::new();
 25
 26static MULTI_PARTY_CRS: OnceLock<InteractiveMultiPartyCrs<[u8; 32]>> = OnceLock::new();
 27
 28pub enum ParameterSelector {
 29    InteractiveLTE2Party,
 30    InteractiveLTE4Party,
 31    InteractiveLTE8Party,
 32}
 33
 34/// Select Interactive multi-party parameter variant
 35pub fn set_parameter_set(select: ParameterSelector) {
 36    match select {
 37        ParameterSelector::InteractiveLTE2Party => {
 38            BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(I_2P_LB_SR)));
 39        }
 40        ParameterSelector::InteractiveLTE4Party => {
 41            BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(I_4P)));
 42        }
 43        ParameterSelector::InteractiveLTE8Party => {
 44            BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(I_8P)));
 45        }
 46        _ => {
 47            panic!("Paramerter not supported")
 48        }
 49    }
 50}
 51
 52/// Set application specific interactive multi-party common reference string
 53pub fn set_common_reference_seed(seed: [u8; 32]) {
 54    assert!(
 55        MULTI_PARTY_CRS
 56            .set(InteractiveMultiPartyCrs { seed: seed })
 57            .is_ok(),
 58        "Attempted to set MP SEED twice."
 59    )
 60}
 61
 62/// Generate client key for interactive multi-party protocol
 63pub fn gen_client_key() -> ClientKey {
 64    BoolEvaluator::with_local(|e| e.client_key())
 65}
 66
 67/// Generate client's share for collective public key, i.e round 1 share, in
 68/// round 1 of the 2 round protocol
 69pub fn interactive_multi_party_round1_share(
 70    ck: &ClientKey,
 71) -> CommonReferenceSeededCollectivePublicKeyShare<Vec<u64>, [u8; 32], BoolParameters<u64>> {
 72    BoolEvaluator::with_local(|e| {
 73        let pk_share = e.multi_party_public_key_share(InteractiveMultiPartyCrs::global(), ck);
 74        pk_share
 75    })
 76}
 77
 78/// Generate clients share for collective server key, i.e. round 2, of the
 79/// protocol
 80pub fn gen_mp_keys_phase2<R, ModOp>(
 81    ck: &ClientKey,
 82    user_id: usize,
 83    total_users: usize,
 84    pk: &PublicKey<Vec<Vec<u64>>, R, ModOp>,
 85) -> CommonReferenceSeededInteractiveMultiPartyServerKeyShare<
 86    Vec<Vec<u64>>,
 87    BoolParameters<u64>,
 88    InteractiveMultiPartyCrs<[u8; 32]>,
 89> {
 90    BoolEvaluator::with_local_mut(|e| {
 91        let server_key_share = e.gen_interactive_multi_party_server_key_share(
 92            user_id,
 93            total_users,
 94            InteractiveMultiPartyCrs::global(),
 95            pk.key(),
 96            ck,
 97        );
 98        server_key_share
 99    })
100}
101
102/// Aggregate public key shares from all parties.
103///
104/// Public key shares are generated per client in round 1. Aggregation of public
105/// key shares marks the end of round 1.
106pub fn aggregate_public_key_shares(
107    shares: &[CommonReferenceSeededCollectivePublicKeyShare<
108        Vec<u64>,
109        [u8; 32],
110        BoolParameters<u64>,
111    >],
112) -> PublicKey<Vec<Vec<u64>>, DefaultSecureRng, ModularOpsU64<CiphertextModulus<u64>>> {
113    PublicKey::from(shares)
114}
115
116/// Aggregate server key shares
117pub fn aggregate_server_key_shares(
118    shares: &[CommonReferenceSeededInteractiveMultiPartyServerKeyShare<
119        Vec<Vec<u64>>,
120        BoolParameters<u64>,
121        InteractiveMultiPartyCrs<[u8; 32]>,
122    >],
123) -> SeededInteractiveMultiPartyServerKey<
124    Vec<Vec<u64>>,
125    InteractiveMultiPartyCrs<[u8; 32]>,
126    BoolParameters<u64>,
127> {
128    BoolEvaluator::with_local(|e| e.aggregate_interactive_multi_party_server_key_shares(shares))
129}
130
131impl
132    SeededInteractiveMultiPartyServerKey<
133        Vec<Vec<u64>>,
134        InteractiveMultiPartyCrs<<DefaultSecureRng as NewWithSeed>::Seed>,
135        BoolParameters<u64>,
136    >
137{
138    /// Sets the server key as a global reference for circuit evaluation
139    pub fn set_server_key(&self) {
140        assert!(
141            BOOL_SERVER_KEY
142                .set(ShoupServerKeyEvaluationDomain::from(
143                    ServerKeyEvaluationDomain::<_, _, DefaultSecureRng, NttBackendU64>::from(self),
144                ))
145                .is_ok(),
146            "Attempted to set server key twice."
147        );
148    }
149}
150
151//  MULTIPARTY CRS  //
152impl Global for InteractiveMultiPartyCrs<[u8; 32]> {
153    fn global() -> &'static Self {
154        MULTI_PARTY_CRS
155            .get()
156            .expect("Multi Party Common Reference String not set")
157    }
158}
159
160// BOOL EVALUATOR //
161impl WithLocal for BoolEvaluator {
162    fn with_local<F, R>(func: F) -> R
163    where
164        F: Fn(&Self) -> R,
165    {
166        BOOL_EVALUATOR.with_borrow(|s| func(s.as_ref().expect("Parameters not set")))
167    }
168
169    fn with_local_mut<F, R>(func: F) -> R
170    where
171        F: Fn(&mut Self) -> R,
172    {
173        BOOL_EVALUATOR.with_borrow_mut(|s| func(s.as_mut().expect("Parameters not set")))
174    }
175
176    fn with_local_mut_mut<F, R>(func: &mut F) -> R
177    where
178        F: FnMut(&mut Self) -> R,
179    {
180        BOOL_EVALUATOR.with_borrow_mut(|s| func(s.as_mut().expect("Parameters not set")))
181    }
182}
183
184pub(crate) type RuntimeServerKey = ShoupServerKeyEvaluationDomain<Vec<Vec<u64>>>;
185impl Global for RuntimeServerKey {
186    fn global() -> &'static Self {
187        BOOL_SERVER_KEY.get().expect("Server key not set!")
188    }
189}
190
191mod impl_enc_dec {
192    use crate::{
193        bool::evaluator::BoolEncoding,
194        multi_party::{
195            multi_party_aggregate_decryption_shares_and_decrypt, multi_party_decryption_share,
196        },
197        pbs::{sample_extract, PbsInfo},
198        rgsw::public_key_encrypt_rlwe,
199        utils::TryConvertFrom1,
200        Encryptor, Matrix, MatrixEntity, MultiPartyDecryptor, RowEntity,
201    };
202    use itertools::Itertools;
203    use num_traits::{ToPrimitive, Zero};
204
205    use super::*;
206
207    type Mat = Vec<Vec<u64>>;
208
209    impl<Rng, ModOp> Encryptor<[bool], Vec<Mat>> for PublicKey<Mat, Rng, ModOp> {
210        fn encrypt(&self, m: &[bool]) -> Vec<Mat> {
211            BoolEvaluator::with_local(|e| {
212                DefaultSecureRng::with_local_mut(|rng| {
213                    let parameters = e.parameters();
214                    let ring_size = parameters.rlwe_n().0;
215
216                    let rlwe_count = ((m.len() as f64 / ring_size as f64).ceil())
217                        .to_usize()
218                        .unwrap();
219
220                    // encrypt `m` into ceil(len(m)/N) RLWE ciphertexts
221                    let rlwes = (0..rlwe_count)
222                        .map(|index| {
223                            let mut message = vec![<Mat as Matrix>::MatElement::zero(); ring_size];
224                            m[(index * ring_size)..std::cmp::min(m.len(), (index + 1) * ring_size)]
225                                .iter()
226                                .enumerate()
227                                .for_each(|(i, v)| {
228                                    if *v {
229                                        message[i] = parameters.rlwe_q().true_el()
230                                    } else {
231                                        message[i] = parameters.rlwe_q().false_el()
232                                    }
233                                });
234
235                            // encrypt message
236                            let mut rlwe_out =
237                                <Mat as MatrixEntity>::zeros(2, parameters.rlwe_n().0);
238
239                            public_key_encrypt_rlwe::<_, _, _, _, i32, _>(
240                                &mut rlwe_out,
241                                self.key(),
242                                &message,
243                                e.pbs_info().modop_rlweq(),
244                                e.pbs_info().nttop_rlweq(),
245                                rng,
246                            );
247
248                            rlwe_out
249                        })
250                        .collect_vec();
251                    rlwes
252                })
253            })
254        }
255    }
256
257    impl<Rng, ModOp> Encryptor<bool, <Mat as Matrix>::R> for PublicKey<Mat, Rng, ModOp> {
258        fn encrypt(&self, m: &bool) -> <Mat as Matrix>::R {
259            let m = vec![*m];
260            let rlwe = &self.encrypt(m.as_slice())[0];
261            BoolEvaluator::with_local(|e| {
262                let mut lwe = <Mat as Matrix>::R::zeros(e.parameters().rlwe_n().0 + 1);
263                sample_extract(&mut lwe, rlwe, e.pbs_info().modop_rlweq(), 0);
264                lwe
265            })
266        }
267    }
268
269    impl<K> MultiPartyDecryptor<bool, <Mat as Matrix>::R> for K
270    where
271        K: InteractiveMultiPartyClientKey,
272        <Mat as Matrix>::R:
273            TryConvertFrom1<[K::Element], CiphertextModulus<<Mat as Matrix>::MatElement>>,
274    {
275        type DecryptionShare = <Mat as Matrix>::MatElement;
276
277        fn gen_decryption_share(&self, c: &<Mat as Matrix>::R) -> Self::DecryptionShare {
278            BoolEvaluator::with_local(|e| {
279                DefaultSecureRng::with_local_mut(|rng| {
280                    multi_party_decryption_share(
281                        c,
282                        self.sk_rlwe().as_slice(),
283                        e.pbs_info().modop_rlweq(),
284                        rng,
285                    )
286                })
287            })
288        }
289
290        fn aggregate_decryption_shares(
291            &self,
292            c: &<Mat as Matrix>::R,
293            shares: &[Self::DecryptionShare],
294        ) -> bool {
295            BoolEvaluator::with_local(|e| {
296                let noisy_m = multi_party_aggregate_decryption_shares_and_decrypt(
297                    c,
298                    shares,
299                    e.pbs_info().modop_rlweq(),
300                );
301
302                e.pbs_info().rlwe_q().decode(noisy_m)
303            })
304        }
305    }
306}
307
308#[cfg(test)]
309mod tests {
310    use itertools::Itertools;
311    use rand::{thread_rng, Rng, RngCore};
312
313    use crate::{
314        backend::Modulus,
315        bool::{
316            evaluator::BoolEncoding,
317            keys::tests::{ideal_sk_rlwe, measure_noise_lwe},
318            BooleanGates,
319        },
320        lwe::decrypt_lwe,
321        utils::tests::Stats,
322        Encoder, Encryptor, MultiPartyDecryptor, SampleExtractor,
323    };
324
325    use super::*;
326
327    #[test]
328    fn multi_party_bool_gates() {
329        set_parameter_set(ParameterSelector::InteractiveLTE8Party);
330        let mut seed = [0u8; 32];
331        thread_rng().fill_bytes(&mut seed);
332        set_common_reference_seed(seed);
333
334        let parties = 8;
335        let cks = (0..parties).map(|_| gen_client_key()).collect_vec();
336
337        // round 1
338        let pk_shares = cks
339            .iter()
340            .map(|k| interactive_multi_party_round1_share(k))
341            .collect_vec();
342
343        // collective pk
344        let pk = aggregate_public_key_shares(&pk_shares);
345
346        // round 2
347        let server_key_shares = cks
348            .iter()
349            .enumerate()
350            .map(|(user_id, k)| gen_mp_keys_phase2(k, user_id, parties, &pk))
351            .collect_vec();
352
353        // server key
354        let server_key = aggregate_server_key_shares(&server_key_shares);
355        server_key.set_server_key();
356
357        let mut m0 = false;
358        let mut m1 = true;
359
360        let mut ct0 = pk.encrypt(&m0);
361        let mut ct1 = pk.encrypt(&m1);
362
363        let ideal_sk_rlwe = ideal_sk_rlwe(&cks);
364        let parameters = BoolEvaluator::with_local(|e| e.parameters().clone());
365        let rlwe_modop = parameters.default_rlwe_modop();
366
367        let mut stats = Stats::new();
368
369        for _ in 0..1000 {
370            let now = std::time::Instant::now();
371            let ct_out =
372                BoolEvaluator::with_local_mut(|e| e.nand(&ct0, &ct1, RuntimeServerKey::global()));
373            println!("Time: {:?}", now.elapsed());
374
375            let m_expected = !(m0 && m1);
376
377            let decryption_shares = cks
378                .iter()
379                .map(|k| k.gen_decryption_share(&ct_out))
380                .collect_vec();
381            let m_out = cks[0].aggregate_decryption_shares(&ct_out, &decryption_shares);
382
383            assert!(m_out == m_expected, "Expected {m_expected}, got {m_out}");
384
385            {
386                let noise = measure_noise_lwe(
387                    &ct_out,
388                    parameters.rlwe_q().encode(m_expected),
389                    &ideal_sk_rlwe,
390                    &rlwe_modop,
391                );
392                stats.add_sample(parameters.rlwe_q().map_element_to_i64(&noise));
393            }
394
395            m1 = m0;
396            m0 = m_expected;
397
398            ct1 = ct0;
399            ct0 = ct_out;
400        }
401
402        for _ in 0..1000 {
403            let ct_out =
404                BoolEvaluator::with_local_mut(|e| e.xnor(&ct0, &ct1, RuntimeServerKey::global()));
405
406            let m_expected = !(m0 ^ m1);
407
408            let decryption_shares = cks
409                .iter()
410                .map(|k| k.gen_decryption_share(&ct_out))
411                .collect_vec();
412            let m_out = cks[0].aggregate_decryption_shares(&ct_out, &decryption_shares);
413
414            assert!(m_out == m_expected, "Expected {m_expected}, got {m_out}");
415
416            {
417                let noise = measure_noise_lwe(
418                    &ct_out,
419                    parameters.rlwe_q().encode(m_expected),
420                    &ideal_sk_rlwe,
421                    &rlwe_modop,
422                );
423                stats.add_sample(parameters.rlwe_q().map_element_to_i64(&noise));
424            }
425
426            m1 = m0;
427            m0 = m_expected;
428
429            ct1 = ct0;
430            ct0 = ct_out;
431        }
432
433        println!("Noise std_dev log2: {}", stats.std_dev().abs().log2());
434    }
435
436    #[test]
437    fn batched_fhe_u8s_extract_works() {
438        set_parameter_set(ParameterSelector::InteractiveLTE2Party);
439        let mut seed = [0u8; 32];
440        thread_rng().fill_bytes(&mut seed);
441        set_common_reference_seed(seed);
442
443        let parties = 2;
444        let cks = (0..parties).map(|_| gen_client_key()).collect_vec();
445
446        // round 1
447        let pk_shares = cks
448            .iter()
449            .map(|k| interactive_multi_party_round1_share(k))
450            .collect_vec();
451
452        // collective pk
453        let pk = aggregate_public_key_shares(&pk_shares);
454
455        let parameters = BoolEvaluator::with_local(|e| e.parameters().clone());
456
457        let batch_size = parameters.rlwe_n().0 * 3 + 123;
458        let m = (0..batch_size)
459            .map(|_| thread_rng().gen::<u8>())
460            .collect_vec();
461
462        let seeded_ct = pk.encrypt(m.as_slice());
463
464        let m_back = (0..batch_size)
465            .map(|i| {
466                let ct = seeded_ct.extract_at(i);
467                cks[0].aggregate_decryption_shares(
468                    &ct,
469                    &cks.iter()
470                        .map(|k| k.gen_decryption_share(&ct))
471                        .collect_vec(),
472                )
473            })
474            .collect_vec();
475
476        assert_eq!(m, m_back);
477    }
478
479    mod sp_api {
480        use num_traits::ToPrimitive;
481
482        use crate::{
483            bool::impl_bool_frontend::FheBool, pbs::PbsInfo, rgsw::seeded_secret_key_encrypt_rlwe,
484            Decryptor,
485        };
486
487        use super::*;
488
489        pub(crate) fn set_single_party_parameter_sets(parameter: BoolParameters<u64>) {
490            BOOL_EVALUATOR.with_borrow_mut(|e| *e = Some(BoolEvaluator::new(parameter)));
491        }
492
493        // SERVER KEY EVAL (/SHOUP) DOMAIN //
494        impl SeededSinglePartyServerKey<Vec<Vec<u64>>, BoolParameters<u64>, [u8; 32]> {
495            pub fn set_server_key(&self) {
496                assert!(
497                    BOOL_SERVER_KEY
498                        .set(
499                            ShoupServerKeyEvaluationDomain::from(ServerKeyEvaluationDomain::<
500                                _,
501                                _,
502                                DefaultSecureRng,
503                                NttBackendU64,
504                            >::from(
505                                self
506                            ),)
507                        )
508                        .is_ok(),
509                    "Attempted to set server key twice."
510                );
511            }
512        }
513
514        pub(crate) fn gen_keys() -> (
515            ClientKey,
516            SeededSinglePartyServerKey<Vec<Vec<u64>>, BoolParameters<u64>, [u8; 32]>,
517        ) {
518            super::BoolEvaluator::with_local_mut(|e| {
519                let ck = e.client_key();
520                let sk = e.single_party_server_key(&ck);
521
522                (ck, sk)
523            })
524        }
525
526        impl<K: SinglePartyClientKey<Element = i32>> Encryptor<bool, Vec<u64>> for K {
527            fn encrypt(&self, m: &bool) -> Vec<u64> {
528                BoolEvaluator::with_local(|e| e.sk_encrypt(*m, self))
529            }
530        }
531
532        impl<K: SinglePartyClientKey<Element = i32>> Decryptor<bool, Vec<u64>> for K {
533            fn decrypt(&self, c: &Vec<u64>) -> bool {
534                BoolEvaluator::with_local(|e| e.sk_decrypt(c, self))
535            }
536        }
537
538        impl<K: SinglePartyClientKey<Element = i32>, C> Encryptor<bool, FheBool<C>> for K
539        where
540            K: Encryptor<bool, C>,
541        {
542            fn encrypt(&self, m: &bool) -> FheBool<C> {
543                FheBool {
544                    data: self.encrypt(m),
545                }
546            }
547        }
548
549        impl<K: SinglePartyClientKey<Element = i32>, C> Decryptor<bool, FheBool<C>> for K
550        where
551            K: Decryptor<bool, C>,
552        {
553            fn decrypt(&self, c: &FheBool<C>) -> bool {
554                self.decrypt(c.data())
555            }
556        }
557
558        impl<K> Encryptor<[bool], (Vec<Vec<u64>>, [u8; 32])> for K
559        where
560            K: SinglePartyClientKey<Element = i32>,
561        {
562            fn encrypt(&self, m: &[bool]) -> (Vec<Vec<u64>>, [u8; 32]) {
563                BoolEvaluator::with_local(|e| {
564                    DefaultSecureRng::with_local_mut(|rng| {
565                        let parameters = e.parameters();
566                        let ring_size = parameters.rlwe_n().0;
567
568                        let rlwe_count = ((m.len() as f64 / ring_size as f64).ceil())
569                            .to_usize()
570                            .unwrap();
571
572                        let mut seed = <DefaultSecureRng as NewWithSeed>::Seed::default();
573                        rng.fill_bytes(&mut seed);
574                        let mut prng = DefaultSecureRng::new_seeded(seed);
575
576                        let sk_u = self.sk_rlwe();
577
578                        // encrypt `m` into ceil(len(m)/N) RLWE ciphertexts
579                        let rlwes = (0..rlwe_count)
580                            .map(|index| {
581                                let mut message = vec![0; ring_size];
582                                m[(index * ring_size)
583                                    ..std::cmp::min(m.len(), (index + 1) * ring_size)]
584                                    .iter()
585                                    .enumerate()
586                                    .for_each(|(i, v)| {
587                                        if *v {
588                                            message[i] = parameters.rlwe_q().true_el()
589                                        } else {
590                                            message[i] = parameters.rlwe_q().false_el()
591                                        }
592                                    });
593
594                                // encrypt message
595                                let mut rlwe_out = vec![0u64; parameters.rlwe_n().0];
596                                seeded_secret_key_encrypt_rlwe(
597                                    &message,
598                                    &mut rlwe_out,
599                                    &sk_u,
600                                    e.pbs_info().modop_rlweq(),
601                                    e.pbs_info().nttop_rlweq(),
602                                    &mut prng,
603                                    rng,
604                                );
605
606                                rlwe_out
607                            })
608                            .collect_vec();
609
610                        (rlwes, seed)
611                    })
612                })
613            }
614        }
615
616        #[test]
617        #[cfg(feature = "interactive_mp")]
618        fn all_uint8_apis() {
619            use num_traits::Euclid;
620
621            use crate::{div_zero_error_flag, FheBool};
622
623            set_single_party_parameter_sets(SP_TEST_BOOL_PARAMS);
624
625            let (ck, sk) = gen_keys();
626            sk.set_server_key();
627
628            for i in 0..=255 {
629                for j in 0..=255 {
630                    let m0 = i;
631                    let m1 = j;
632                    let c0 = ck.encrypt(&m0);
633                    let c1 = ck.encrypt(&m1);
634
635                    assert!(ck.decrypt(&c0) == m0);
636                    assert!(ck.decrypt(&c1) == m1);
637
638                    // Arithmetic
639                    {
640                        {
641                            // Add
642                            let c_add = &c0 + &c1;
643                            let m0_plus_m1 = ck.decrypt(&c_add);
644                            assert_eq!(
645                                m0_plus_m1,
646                                m0.wrapping_add(m1),
647                                "Expected {} but got {m0_plus_m1} for
648                        {i}+{j}",
649                                m0.wrapping_add(m1)
650                            );
651                        }
652                        {
653                            // Sub
654                            let c_sub = &c0 - &c1;
655                            let m0_sub_m1 = ck.decrypt(&c_sub);
656                            assert_eq!(
657                                m0_sub_m1,
658                                m0.wrapping_sub(m1),
659                                "Expected {} but got {m0_sub_m1} for
660                        {i}-{j}",
661                                m0.wrapping_sub(m1)
662                            );
663                        }
664
665                        {
666                            // Mul
667                            let c_m0m1 = &c0 * &c1;
668                            let m0m1 = ck.decrypt(&c_m0m1);
669                            assert_eq!(
670                                m0m1,
671                                m0.wrapping_mul(m1),
672                                "Expected {} but got {m0m1} for {i}x{j}",
673                                m0.wrapping_mul(m1)
674                            );
675                        }
676
677                        // Div & Rem
678                        {
679                            let (c_quotient, c_rem) = c0.div_rem(&c1);
680                            let m_quotient = ck.decrypt(&c_quotient);
681                            let m_remainder = ck.decrypt(&c_rem);
682                            if j != 0 {
683                                let (q, r) = i.div_rem_euclid(&j);
684                                assert_eq!(
685                                    m_quotient, q,
686                                    "Expected {} but got {m_quotient} for
687                    {i}/{j}",
688                                    q
689                                );
690                                assert_eq!(
691                                    m_remainder, r,
692                                    "Expected {} but got {m_remainder} for
693                    {i}%{j}",
694                                    r
695                                );
696                            } else {
697                                assert_eq!(
698                                    m_quotient, 255,
699                                    "Expected 255 but got {m_quotient}. Case
700                    div by zero"
701                                );
702                                assert_eq!(
703                                    m_remainder, i,
704                                    "Expected {i} but got {m_remainder}. Case
705                    div by zero"
706                                );
707
708                                let div_by_zero = ck.decrypt(&div_zero_error_flag().unwrap());
709                                assert_eq!(
710                                    div_by_zero, true,
711                                    "Expected true but got {div_by_zero}"
712                                );
713                            }
714                        }
715                    }
716
717                    // // Comparisons
718                    {
719                        {
720                            let c_eq = c0.eq(&c1);
721                            let is_eq = ck.decrypt(&c_eq);
722                            assert_eq!(
723                                is_eq,
724                                i == j,
725                                "Expected {} but got {is_eq} for {i}=={j}",
726                                i == j
727                            );
728                        }
729
730                        {
731                            let c_gt = c0.gt(&c1);
732                            let is_gt = ck.decrypt(&c_gt);
733                            assert_eq!(
734                                is_gt,
735                                i > j,
736                                "Expected {} but got {is_gt} for {i}>{j}",
737                                i > j
738                            );
739                        }
740
741                        {
742                            let c_lt = c0.lt(&c1);
743                            let is_lt = ck.decrypt(&c_lt);
744                            assert_eq!(
745                                is_lt,
746                                i < j,
747                                "Expected {} but got {is_lt} for {i}<{j}",
748                                i < j
749                            );
750                        }
751
752                        {
753                            let c_ge = c0.ge(&c1);
754                            let is_ge = ck.decrypt(&c_ge);
755                            assert_eq!(
756                                is_ge,
757                                i >= j,
758                                "Expected {} but got {is_ge} for {i}>={j}",
759                                i >= j
760                            );
761                        }
762
763                        {
764                            let c_le = c0.le(&c1);
765                            let is_le = ck.decrypt(&c_le);
766                            assert_eq!(
767                                is_le,
768                                i <= j,
769                                "Expected {} but got {is_le} for {i}<={j}",
770                                i <= j
771                            );
772                        }
773                    }
774
775                    // mux
776                    {
777                        let selector = thread_rng().gen_bool(0.5);
778                        let selector_enc: FheBool = ck.encrypt(&selector);
779                        let mux_out = ck.decrypt(&c0.mux(&c1, &selector_enc));
780                        let want_mux_out = if selector { m0 } else { m1 };
781                        assert_eq!(mux_out, want_mux_out);
782                    }
783                }
784            }
785        }
786
787        #[test]
788        #[cfg(feature = "interactive_mp")]
789        fn all_bool_apis() {
790            use crate::FheBool;
791
792            set_single_party_parameter_sets(SP_TEST_BOOL_PARAMS);
793
794            let (ck, sk) = gen_keys();
795            sk.set_server_key();
796
797            for _ in 0..100 {
798                let a = thread_rng().gen_bool(0.5);
799                let b = thread_rng().gen_bool(0.5);
800
801                let c_a: FheBool = ck.encrypt(&a);
802                let c_b: FheBool = ck.encrypt(&b);
803
804                let c_out = &c_a & &c_b;
805                let out = ck.decrypt(&c_out);
806                assert_eq!(out, a & b, "Expected {} but got {out}", a & b);
807
808                let c_out = &c_a | &c_b;
809                let out = ck.decrypt(&c_out);
810                assert_eq!(out, a | b, "Expected {} but got {out}", a | b);
811
812                let c_out = &c_a ^ &c_b;
813                let out = ck.decrypt(&c_out);
814                assert_eq!(out, a ^ b, "Expected {} but got {out}", a ^ b);
815
816                let c_out = !(&c_a);
817                let out = ck.decrypt(&c_out);
818                assert_eq!(out, !a, "Expected {} but got {out}", !a);
819            }
820        }
821    }
822}