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