arnaucube
arnaucube/phantom-zonePublic Self-hosted repository
phantom-zone/src/bool/evaluator.rs
src/bool/evaluator.rsRaw
   1use std::{
   2    cell::{OnceCell, RefCell},
   3    collections::HashMap,
   4    fmt::{Debug, Display},
   5    marker::PhantomData,
   6    ops::Shr,
   7};
   8
   9use itertools::{izip, partition, Itertools};
  10use num_traits::{FromPrimitive, Num, One, PrimInt, ToPrimitive, WrappingSub, Zero};
  11
  12use crate::{
  13    backend::{ArithmeticOps, GetModulus, ModInit, ModularOpsU64, Modulus, VectorOps},
  14    bool::parameters::{MP_BOOL_PARAMS, SP_BOOL_PARAMS},
  15    decomposer::{Decomposer, DefaultDecomposer, NumInfo, RlweDecomposer},
  16    lwe::{decrypt_lwe, encrypt_lwe, lwe_key_switch, lwe_ksk_keygen, measure_noise_lwe, LweSecret},
  17    multi_party::public_key_share,
  18    ntt::{self, Ntt, NttBackendU64, NttInit},
  19    random::{
  20        DefaultSecureRng, NewWithSeed, RandomFillGaussianInModulus, RandomFillUniformInModulus,
  21        RandomGaussianElementInModulus,
  22    },
  23    rgsw::{
  24        decrypt_rlwe, galois_auto, galois_key_gen, generate_auto_map, public_key_encrypt_rgsw,
  25        rgsw_by_rgsw_inplace, rlwe_by_rgsw, secret_key_encrypt_rgsw, IsTrivial, RgswCiphertext,
  26        RlweCiphertext, RlweSecret,
  27    },
  28    utils::{
  29        fill_random_ternary_secret_with_hamming_weight, generate_prime, mod_exponent,
  30        TryConvertFrom1, WithLocal,
  31    },
  32    Matrix, MatrixEntity, MatrixMut, Row, RowEntity, RowMut, Secret,
  33};
  34
  35use super::parameters::{BoolParameters, CiphertextModulus};
  36
  37thread_local! {
  38    static BOOL_EVALUATOR: RefCell<BoolEvaluator<Vec<Vec<u64>>, NttBackendU64, ModularOpsU64<CiphertextModulus<u64>>,  ModularOpsU64<CiphertextModulus<u64>>>> = RefCell::new(BoolEvaluator::new(MP_BOOL_PARAMS));
  39}
  40
  41pub fn set_parameter_set(parameter: &BoolParameters<u64>) {
  42    BoolEvaluator::with_local_mut(|e| *e = BoolEvaluator::new(parameter.clone()))
  43}
  44
  45impl WithLocal
  46    for BoolEvaluator<
  47        Vec<Vec<u64>>,
  48        NttBackendU64,
  49        ModularOpsU64<CiphertextModulus<u64>>,
  50        ModularOpsU64<CiphertextModulus<u64>>,
  51    >
  52{
  53    fn with_local<F, R>(func: F) -> R
  54    where
  55        F: Fn(&Self) -> R,
  56    {
  57        BOOL_EVALUATOR.with_borrow(|s| func(s))
  58    }
  59
  60    fn with_local_mut<F, R>(func: F) -> R
  61    where
  62        F: Fn(&mut Self) -> R,
  63    {
  64        BOOL_EVALUATOR.with_borrow_mut(|s| func(s))
  65    }
  66}
  67
  68struct ScratchMemory<M>
  69where
  70    M: Matrix,
  71{
  72    lwe_vector: M::R,
  73    decomposition_matrix: M,
  74}
  75
  76impl<M: MatrixEntity> ScratchMemory<M>
  77where
  78    M::R: RowEntity,
  79{
  80    fn new(parameters: &BoolParameters<M::MatElement>) -> Self {
  81        // Vector to store LWE ciphertext with LWE dimesnion n
  82        let lwe_vector = M::R::zeros(parameters.lwe_n().0 + 1);
  83
  84        // Matrix to store decomposed polynomials
  85        // Max decompistion count + space for temporary RLWE
  86        let d = std::cmp::max(
  87            parameters.auto_decomposition_count().0,
  88            std::cmp::max(
  89                parameters.rlwe_rgsw_decomposition_count().0 .0,
  90                parameters.rlwe_rgsw_decomposition_count().1 .0,
  91            ),
  92        ) + 2;
  93        let decomposition_matrix = M::zeros(d, parameters.rlwe_n().0);
  94
  95        Self {
  96            lwe_vector,
  97            decomposition_matrix,
  98        }
  99    }
 100}
 101
 102// thread_local! {
 103//     pub(crate) static CLIENT_KEY: RefCell<ClientKey> =
 104// RefCell::new(ClientKey::random()); }
 105
 106trait BoolEncoding {
 107    type Element;
 108    fn true_el(&self) -> Self::Element;
 109    fn false_el(&self) -> Self::Element;
 110    fn qby4(&self) -> Self::Element;
 111    fn decode(&self, m: Self::Element) -> bool;
 112}
 113
 114impl<T> BoolEncoding for CiphertextModulus<T>
 115where
 116    CiphertextModulus<T>: Modulus<Element = T>,
 117    T: PrimInt,
 118{
 119    type Element = T;
 120
 121    fn qby4(&self) -> Self::Element {
 122        if self.is_native() {
 123            T::one() << (CiphertextModulus::<T>::_bits() - 2)
 124        } else {
 125            self.q().unwrap() >> 2
 126        }
 127    }
 128    /// Q/8
 129    fn true_el(&self) -> Self::Element {
 130        if self.is_native() {
 131            T::one() << (CiphertextModulus::<T>::_bits() - 3)
 132        } else {
 133            self.q().unwrap() >> 3
 134        }
 135    }
 136    /// -Q/8
 137    fn false_el(&self) -> Self::Element {
 138        self.largest_unsigned_value() - self.true_el() + T::one()
 139    }
 140    fn decode(&self, m: Self::Element) -> bool {
 141        let qby8 = self.true_el();
 142        let m = (((m + qby8).to_f64().unwrap() * 4.0f64) / self.q_as_f64().unwrap()).round()
 143            as usize
 144            % 4usize;
 145
 146        if m == 0 {
 147            return false;
 148        } else if m == 1 {
 149            return true;
 150        } else {
 151            panic!("Incorrect bool decryption. Got m={m} but expected m to be 0 or 1")
 152        }
 153    }
 154}
 155
 156trait PbsKey {
 157    type M: Matrix;
 158
 159    /// RGSW ciphertext of LWE secret elements
 160    fn rgsw_ct_lwe_si(&self, si: usize) -> &Self::M;
 161    /// Key for automorphism
 162    fn galois_key_for_auto(&self, k: isize) -> &Self::M;
 163    /// LWE ksk to key switch from RLWE secret to LWE secret
 164    fn lwe_ksk(&self) -> &Self::M;
 165}
 166
 167trait PbsInfo {
 168    type Element;
 169    type Modulus: Modulus<Element = Self::Element>;
 170    type NttOp: Ntt<Element = Self::Element>;
 171    type D: Decomposer<Element = Self::Element>;
 172
 173    // Although both types have same bounds, they can be different types. For ex,
 174    // type RlweModOp may only support native modulus, where LweModOp may only
 175    // support prime modulus, etc.
 176    type RlweModOp: VectorOps<Element = Self::Element> + ArithmeticOps<Element = Self::Element>;
 177    type LweModOp: VectorOps<Element = Self::Element> + ArithmeticOps<Element = Self::Element>;
 178
 179    fn rlwe_q(&self) -> &Self::Modulus;
 180    fn lwe_q(&self) -> &Self::Modulus;
 181    fn br_q(&self) -> usize;
 182    fn rlwe_n(&self) -> usize;
 183    fn lwe_n(&self) -> usize;
 184    /// Embedding fator for ring X^{q}+1 inside
 185    fn embedding_factor(&self) -> usize;
 186    /// generator g
 187    fn g(&self) -> isize;
 188    /// Decomposers
 189    fn lwe_decomposer(&self) -> &Self::D;
 190    fn rlwe_rgsw_decomposer(&self) -> &(Self::D, Self::D);
 191    fn auto_decomposer(&self) -> &Self::D;
 192
 193    /// Modulus operators
 194    fn modop_lweq(&self) -> &Self::LweModOp;
 195    fn modop_rlweq(&self) -> &Self::RlweModOp;
 196
 197    /// Ntt operators
 198    fn nttop_rlweq(&self) -> &Self::NttOp;
 199
 200    /// Maps a \in Z^*_{q} to discrete log k, with generator g (i.e. g^k =
 201    /// a). Returned vector is of size q that stores dlog of a at `vec[a]`.
 202    /// For any a, if k is s.t. a = g^{k}, then k is expressed as k. If k is s.t
 203    /// a = -g^{k}, then k is expressed as k=k+q/2
 204    fn g_k_dlog_map(&self) -> &[usize];
 205    fn rlwe_auto_map(&self, k: isize) -> &(Vec<usize>, Vec<bool>);
 206}
 207
 208#[derive(Clone)]
 209struct ClientKey {
 210    sk_rlwe: RlweSecret,
 211    sk_lwe: LweSecret,
 212}
 213
 214impl ClientKey {
 215    fn random() -> Self {
 216        let sk_rlwe = RlweSecret::random(0, 0);
 217        let sk_lwe = LweSecret::random(0, 0);
 218        Self { sk_rlwe, sk_lwe }
 219    }
 220}
 221
 222// impl WithLocal for ClientKey {
 223//     fn with_local<F, R>(func: F) -> R
 224//     where
 225//         F: Fn(&Self) -> R,
 226//     {
 227//         CLIENT_KEY.with_borrow(|client_key| func(client_key))
 228//     }
 229
 230//     fn with_local_mut<F, R>(func: F) -> R
 231//     where
 232//         F: Fn(&mut Self) -> R,
 233//     {
 234//         CLIENT_KEY.with_borrow_mut(|client_key| func(client_key))
 235//     }
 236// }
 237
 238// fn set_client_key(key: &ClientKey) {
 239//     ClientKey::with_local_mut(|k| *k = key.clone())
 240// }
 241
 242struct MultiPartyDecryptionShare<E> {
 243    share: E,
 244}
 245
 246struct CommonReferenceSeededCollectivePublicKeyShare<R, S, P> {
 247    share: R,
 248    cr_seed: S,
 249    parameters: P,
 250}
 251
 252struct PublicKey<M, R, O> {
 253    key: M,
 254    _phantom: PhantomData<(R, O)>,
 255}
 256
 257impl<
 258        M: MatrixMut + MatrixEntity,
 259        Rng: NewWithSeed + RandomFillUniformInModulus<[M::MatElement], CiphertextModulus<M::MatElement>>,
 260        ModOp: VectorOps<Element = M::MatElement> + ModInit<M = CiphertextModulus<M::MatElement>>,
 261    >
 262    From<
 263        &[CommonReferenceSeededCollectivePublicKeyShare<
 264            M::R,
 265            Rng::Seed,
 266            BoolParameters<M::MatElement>,
 267        >],
 268    > for PublicKey<M, Rng, ModOp>
 269where
 270    <M as Matrix>::R: RowMut,
 271    Rng::Seed: Copy + PartialEq,
 272    M::MatElement: PartialEq + Copy,
 273{
 274    fn from(
 275        value: &[CommonReferenceSeededCollectivePublicKeyShare<
 276            M::R,
 277            Rng::Seed,
 278            BoolParameters<M::MatElement>,
 279        >],
 280    ) -> Self {
 281        assert!(value.len() > 0);
 282
 283        let parameters = &value[0].parameters;
 284        let mut key = M::zeros(2, parameters.rlwe_n().0);
 285
 286        // sample A
 287        let seed = value[0].cr_seed;
 288        let mut main_rng = Rng::new_with_seed(seed);
 289        RandomFillUniformInModulus::random_fill(
 290            &mut main_rng,
 291            parameters.rlwe_q(),
 292            key.get_row_mut(0),
 293        );
 294
 295        // Sum all Bs
 296        let rlweq_modop = ModOp::new(parameters.rlwe_q().clone());
 297        value.iter().for_each(|share_i| {
 298            assert!(share_i.cr_seed == seed);
 299            assert!(&share_i.parameters == parameters);
 300
 301            rlweq_modop.elwise_add_mut(key.get_row_mut(1), share_i.share.as_ref());
 302        });
 303
 304        PublicKey {
 305            key,
 306            _phantom: PhantomData,
 307        }
 308    }
 309}
 310
 311struct CommonReferenceSeededMultiPartyServerKeyShare<M: Matrix, P, S> {
 312    rgsw_cts: Vec<M>,
 313    auto_keys: HashMap<isize, M>,
 314    lwe_ksk: M::R,
 315    /// Common reference seed
 316    cr_seed: S,
 317    parameters: P,
 318}
 319struct SeededMultiPartyServerKey<M: Matrix, S, P> {
 320    rgsw_cts: Vec<M>,
 321    auto_keys: HashMap<isize, M>,
 322    lwe_ksk: M::R,
 323    cr_seed: S,
 324    parameters: P,
 325}
 326
 327/// Seeded single party server key
 328struct SeededServerKey<M: Matrix, P, S> {
 329    /// Rgsw cts of LWE secret elements
 330    pub(crate) rgsw_cts: Vec<M>,
 331    /// Auto keys
 332    pub(crate) auto_keys: HashMap<isize, M>,
 333    /// LWE ksk to key switching LWE ciphertext from RLWE secret to LWE secret
 334    pub(crate) lwe_ksk: M::R,
 335    /// Parameters
 336    pub(crate) parameters: P,
 337    /// Main seed
 338    pub(crate) seed: S,
 339}
 340
 341impl<M: Matrix, S> SeededServerKey<M, BoolParameters<M::MatElement>, S> {
 342    pub(crate) fn from_raw(
 343        auto_keys: HashMap<isize, M>,
 344        rgsw_cts: Vec<M>,
 345        lwe_ksk: M::R,
 346        parameters: BoolParameters<M::MatElement>,
 347        seed: S,
 348    ) -> Self {
 349        // sanity checks
 350        auto_keys.iter().for_each(|v| {
 351            assert!(
 352                v.1.dimension()
 353                    == (
 354                        parameters.auto_decomposition_count().0,
 355                        parameters.rlwe_n().0
 356                    )
 357            )
 358        });
 359
 360        let (part_a_d, part_b_d) = parameters.rlwe_rgsw_decomposition_count();
 361        rgsw_cts.iter().for_each(|v| {
 362            assert!(v.dimension() == (part_a_d.0 * 2 + part_b_d.0, parameters.rlwe_n().0))
 363        });
 364        assert!(
 365            lwe_ksk.as_ref().len()
 366                == (parameters.lwe_decomposition_count().0 * parameters.rlwe_n().0)
 367        );
 368
 369        SeededServerKey {
 370            rgsw_cts,
 371            auto_keys,
 372            lwe_ksk,
 373            parameters,
 374            seed,
 375        }
 376    }
 377}
 378
 379/// Server key in evaluation domain
 380struct ServerKeyEvaluationDomain<M, R, N> {
 381    /// Rgsw cts of LWE secret elements
 382    rgsw_cts: Vec<M>,
 383    /// Galois keys
 384    galois_keys: HashMap<isize, M>,
 385    /// LWE ksk to key switching LWE ciphertext from RLWE secret to LWE secret
 386    lwe_ksk: M,
 387    _phanton: PhantomData<(R, N)>,
 388}
 389
 390impl<
 391        M: MatrixMut + MatrixEntity,
 392        R: RandomFillUniformInModulus<[M::MatElement], CiphertextModulus<M::MatElement>> + NewWithSeed,
 393        N: NttInit<CiphertextModulus<M::MatElement>> + Ntt<Element = M::MatElement>,
 394    > From<&SeededServerKey<M, BoolParameters<M::MatElement>, R::Seed>>
 395    for ServerKeyEvaluationDomain<M, R, N>
 396where
 397    <M as Matrix>::R: RowMut,
 398    M::MatElement: Copy,
 399    R::Seed: Clone,
 400{
 401    fn from(value: &SeededServerKey<M, BoolParameters<M::MatElement>, R::Seed>) -> Self {
 402        let mut main_prng = R::new_with_seed(value.seed.clone());
 403        let parameters = &value.parameters;
 404        let g = parameters.g() as isize;
 405        let ring_size = value.parameters.rlwe_n().0;
 406        let lwe_n = value.parameters.lwe_n().0;
 407        let rlwe_q = value.parameters.rlwe_q();
 408        let lwq_q = value.parameters.lwe_q();
 409
 410        let nttop = N::new(rlwe_q, ring_size);
 411
 412        // galois keys
 413        let mut auto_keys = HashMap::new();
 414        let auto_decomp_count = parameters.auto_decomposition_count().0;
 415        for i in [g, -g] {
 416            let seeded_auto_key = value.auto_keys.get(&i).unwrap();
 417            assert!(seeded_auto_key.dimension() == (auto_decomp_count, ring_size));
 418
 419            let mut data = M::zeros(auto_decomp_count * 2, ring_size);
 420
 421            // sample RLWE'_A(-s(X^k))
 422            data.iter_rows_mut().take(auto_decomp_count).for_each(|ri| {
 423                RandomFillUniformInModulus::random_fill(&mut main_prng, &rlwe_q, ri.as_mut())
 424            });
 425
 426            // copy over RLWE'B_(-s(X^k))
 427            izip!(
 428                data.iter_rows_mut().skip(auto_decomp_count),
 429                seeded_auto_key.iter_rows()
 430            )
 431            .for_each(|(to_ri, from_ri)| to_ri.as_mut().copy_from_slice(from_ri.as_ref()));
 432
 433            // Send to Evaluation domain
 434            data.iter_rows_mut()
 435                .for_each(|ri| nttop.forward(ri.as_mut()));
 436
 437            auto_keys.insert(i, data);
 438        }
 439
 440        // RGSW ciphertexts
 441        let (rlrg_a_decomp, rlrg_b_decomp) = parameters.rlwe_rgsw_decomposition_count();
 442        let rgsw_cts = value
 443            .rgsw_cts
 444            .iter()
 445            .map(|seeded_rgsw_si| {
 446                assert!(
 447                    seeded_rgsw_si.dimension()
 448                        == (rlrg_a_decomp.0 * 2 + rlrg_b_decomp.0, ring_size)
 449                );
 450
 451                let mut data = M::zeros(rlrg_a_decomp.0 * 2 + rlrg_b_decomp.0 * 2, ring_size);
 452
 453                // copy over RLWE'(-sm)
 454                izip!(
 455                    data.iter_rows_mut().take(rlrg_a_decomp.0 * 2),
 456                    seeded_rgsw_si.iter_rows().take(rlrg_a_decomp.0 * 2)
 457                )
 458                .for_each(|(to_ri, from_ri)| to_ri.as_mut().copy_from_slice(from_ri.as_ref()));
 459
 460                // sample RLWE'_A(m)
 461                data.iter_rows_mut()
 462                    .skip(rlrg_a_decomp.0 * 2)
 463                    .take(rlrg_b_decomp.0)
 464                    .for_each(|ri| {
 465                        RandomFillUniformInModulus::random_fill(
 466                            &mut main_prng,
 467                            &rlwe_q,
 468                            ri.as_mut(),
 469                        )
 470                    });
 471
 472                // copy over RLWE'_B(m)
 473                izip!(
 474                    data.iter_rows_mut()
 475                        .skip(rlrg_a_decomp.0 * 2 + rlrg_b_decomp.0),
 476                    seeded_rgsw_si.iter_rows().skip(rlrg_a_decomp.0 * 2)
 477                )
 478                .for_each(|(to_ri, from_ri)| to_ri.as_mut().copy_from_slice(from_ri.as_ref()));
 479
 480                // send polynomials to evaluation domain
 481                data.iter_rows_mut()
 482                    .for_each(|ri| nttop.forward(ri.as_mut()));
 483
 484                data
 485            })
 486            .collect_vec();
 487
 488        // LWE ksk
 489        let lwe_ksk = {
 490            let d = parameters.lwe_decomposition_count().0;
 491            assert!(value.lwe_ksk.as_ref().len() == d * ring_size);
 492
 493            let mut data = M::zeros(d * ring_size, lwe_n + 1);
 494            izip!(data.iter_rows_mut(), value.lwe_ksk.as_ref().iter()).for_each(|(lwe_i, bi)| {
 495                RandomFillUniformInModulus::random_fill(
 496                    &mut main_prng,
 497                    &lwq_q,
 498                    &mut lwe_i.as_mut()[1..],
 499                );
 500                lwe_i.as_mut()[0] = *bi;
 501            });
 502
 503            data
 504        };
 505
 506        ServerKeyEvaluationDomain {
 507            rgsw_cts,
 508            galois_keys: auto_keys,
 509            lwe_ksk,
 510            _phanton: PhantomData,
 511        }
 512    }
 513}
 514
 515impl<
 516        M: MatrixMut + MatrixEntity,
 517        Rng: NewWithSeed,
 518        N: NttInit<CiphertextModulus<M::MatElement>> + Ntt<Element = M::MatElement>,
 519    > From<&SeededMultiPartyServerKey<M, Rng::Seed, BoolParameters<M::MatElement>>>
 520    for ServerKeyEvaluationDomain<M, Rng, N>
 521where
 522    <M as Matrix>::R: RowMut,
 523    Rng::Seed: Copy,
 524    Rng: RandomFillUniformInModulus<[M::MatElement], CiphertextModulus<M::MatElement>>,
 525    M::MatElement: Copy,
 526{
 527    fn from(
 528        value: &SeededMultiPartyServerKey<M, Rng::Seed, BoolParameters<M::MatElement>>,
 529    ) -> Self {
 530        let g = value.parameters.g() as isize;
 531        let rlwe_n = value.parameters.rlwe_n().0;
 532        let lwe_n = value.parameters.lwe_n().0;
 533        let rlwe_q = value.parameters.rlwe_q();
 534        let lwe_q = value.parameters.lwe_q();
 535
 536        let mut main_prng = Rng::new_with_seed(value.cr_seed);
 537
 538        let rlwe_nttop = N::new(rlwe_q, rlwe_n);
 539
 540        // auto keys
 541        let mut auto_keys = HashMap::new();
 542        let auto_d_count = value.parameters.auto_decomposition_count().0;
 543        for i in [g, -g] {
 544            let mut key = M::zeros(auto_d_count * 2, rlwe_n);
 545
 546            // sample a
 547            key.iter_rows_mut().take(auto_d_count).for_each(|ri| {
 548                RandomFillUniformInModulus::random_fill(&mut main_prng, &rlwe_q, ri.as_mut())
 549            });
 550
 551            let key_part_b = value.auto_keys.get(&i).unwrap();
 552            assert!(key_part_b.dimension() == (auto_d_count, rlwe_n));
 553            izip!(
 554                key.iter_rows_mut().skip(auto_d_count),
 555                key_part_b.iter_rows()
 556            )
 557            .for_each(|(to_ri, from_ri)| {
 558                to_ri.as_mut().copy_from_slice(from_ri.as_ref());
 559            });
 560
 561            // send to evaluation domain
 562            key.iter_rows_mut()
 563                .for_each(|ri| rlwe_nttop.forward(ri.as_mut()));
 564
 565            auto_keys.insert(i, key);
 566        }
 567
 568        // rgsw cts
 569        let (rlrg_d_a, rlrg_d_b) = value.parameters.rlwe_rgsw_decomposition_count();
 570        let rgsw_ct_out = rlrg_d_a.0 * 2 + rlrg_d_b.0 * 2;
 571        let rgsw_cts = value
 572            .rgsw_cts
 573            .iter()
 574            .map(|ct_i_in| {
 575                assert!(ct_i_in.dimension() == (rgsw_ct_out, rlwe_n));
 576                let mut eval_ct_i_out = M::zeros(rgsw_ct_out, rlwe_n);
 577
 578                izip!(eval_ct_i_out.iter_rows_mut(), ct_i_in.iter_rows()).for_each(
 579                    |(to_ri, from_ri)| {
 580                        to_ri.as_mut().copy_from_slice(from_ri.as_ref());
 581                        rlwe_nttop.forward(to_ri.as_mut());
 582                    },
 583                );
 584
 585                eval_ct_i_out
 586            })
 587            .collect_vec();
 588
 589        // lwe ksk
 590        let d_lwe = value.parameters.lwe_decomposition_count().0;
 591        let mut lwe_ksk = M::zeros(rlwe_n * d_lwe, lwe_n + 1);
 592        izip!(lwe_ksk.iter_rows_mut(), value.lwe_ksk.as_ref().iter()).for_each(|(lwe_i, bi)| {
 593            RandomFillUniformInModulus::random_fill(
 594                &mut main_prng,
 595                &lwe_q,
 596                &mut lwe_i.as_mut()[1..],
 597            );
 598            lwe_i.as_mut()[0] = *bi;
 599        });
 600
 601        ServerKeyEvaluationDomain {
 602            rgsw_cts,
 603            galois_keys: auto_keys,
 604            lwe_ksk,
 605            _phanton: PhantomData,
 606        }
 607    }
 608}
 609
 610//FIXME(Jay): Figure out a way for BoolEvaluator to have access to ServerKey
 611// via a pointer and implement PbsKey for BoolEvaluator instead of ServerKey
 612// directly
 613impl<M: Matrix, R, N> PbsKey for ServerKeyEvaluationDomain<M, R, N> {
 614    type M = M;
 615    fn galois_key_for_auto(&self, k: isize) -> &Self::M {
 616        self.galois_keys.get(&k).unwrap()
 617    }
 618    fn rgsw_ct_lwe_si(&self, si: usize) -> &Self::M {
 619        &self.rgsw_cts[si]
 620    }
 621
 622    fn lwe_ksk(&self) -> &Self::M {
 623        &self.lwe_ksk
 624    }
 625}
 626
 627struct BoolPbsInfo<M: Matrix, Ntt, RlweModOp, LweModOp> {
 628    auto_decomposer: DefaultDecomposer<M::MatElement>,
 629    rlwe_rgsw_decomposer: (
 630        DefaultDecomposer<M::MatElement>,
 631        DefaultDecomposer<M::MatElement>,
 632    ),
 633    lwe_decomposer: DefaultDecomposer<M::MatElement>,
 634    g_k_dlog_map: Vec<usize>,
 635    rlwe_nttop: Ntt,
 636    rlwe_modop: RlweModOp,
 637    lwe_modop: LweModOp,
 638    embedding_factor: usize,
 639    nand_test_vec: M::R,
 640    rlwe_qby4: M::MatElement,
 641    rlwe_auto_maps: Vec<(Vec<usize>, Vec<bool>)>,
 642    parameters: BoolParameters<M::MatElement>,
 643}
 644
 645impl<M: Matrix, NttOp, RlweModOp, LweModOp> PbsInfo for BoolPbsInfo<M, NttOp, RlweModOp, LweModOp>
 646where
 647    M::MatElement: PrimInt + WrappingSub + NumInfo + Debug + FromPrimitive,
 648    RlweModOp: ArithmeticOps<Element = M::MatElement> + VectorOps<Element = M::MatElement>,
 649    LweModOp: ArithmeticOps<Element = M::MatElement> + VectorOps<Element = M::MatElement>,
 650    NttOp: Ntt<Element = M::MatElement>,
 651{
 652    type Modulus = CiphertextModulus<M::MatElement>;
 653    type Element = M::MatElement;
 654    type D = DefaultDecomposer<M::MatElement>;
 655    type RlweModOp = RlweModOp;
 656    type LweModOp = LweModOp;
 657    type NttOp = NttOp;
 658    fn rlwe_auto_map(&self, k: isize) -> &(Vec<usize>, Vec<bool>) {
 659        let g = self.parameters.g() as isize;
 660        if k == g {
 661            &self.rlwe_auto_maps[0]
 662        } else if k == -g {
 663            &self.rlwe_auto_maps[1]
 664        } else {
 665            panic!("RLWE auto map only supports k in [-g, g], but got k={k}");
 666        }
 667    }
 668    fn br_q(&self) -> usize {
 669        *self.parameters.br_q()
 670    }
 671    fn lwe_decomposer(&self) -> &Self::D {
 672        &self.lwe_decomposer
 673    }
 674    fn rlwe_rgsw_decomposer(&self) -> &(Self::D, Self::D) {
 675        &self.rlwe_rgsw_decomposer
 676    }
 677    fn auto_decomposer(&self) -> &Self::D {
 678        &self.auto_decomposer
 679    }
 680    fn embedding_factor(&self) -> usize {
 681        self.embedding_factor
 682    }
 683    fn g(&self) -> isize {
 684        self.parameters.g() as isize
 685    }
 686    fn g_k_dlog_map(&self) -> &[usize] {
 687        &self.g_k_dlog_map
 688    }
 689    fn lwe_n(&self) -> usize {
 690        self.parameters.lwe_n().0
 691    }
 692    fn lwe_q(&self) -> &Self::Modulus {
 693        self.parameters.lwe_q()
 694    }
 695    fn rlwe_n(&self) -> usize {
 696        self.parameters.rlwe_n().0
 697    }
 698    fn rlwe_q(&self) -> &Self::Modulus {
 699        self.parameters.rlwe_q()
 700    }
 701    fn modop_lweq(&self) -> &Self::LweModOp {
 702        &self.lwe_modop
 703    }
 704    fn modop_rlweq(&self) -> &Self::RlweModOp {
 705        &self.rlwe_modop
 706    }
 707    fn nttop_rlweq(&self) -> &Self::NttOp {
 708        &self.rlwe_nttop
 709    }
 710}
 711
 712struct BoolEvaluator<M, Ntt, RlweModOp, LweModOp>
 713where
 714    M: Matrix,
 715{
 716    pbs_info: BoolPbsInfo<M, Ntt, RlweModOp, LweModOp>,
 717    scratch_memory: ScratchMemory<M>,
 718    _phantom: PhantomData<M>,
 719}
 720
 721impl<M: Matrix, NttOp, RlweModOp, LweModOp> BoolEvaluator<M, NttOp, RlweModOp, LweModOp> {}
 722
 723impl<M: Matrix, NttOp, RlweModOp, LweModOp> BoolEvaluator<M, NttOp, RlweModOp, LweModOp>
 724where
 725    M: MatrixEntity + MatrixMut,
 726    M::MatElement: PrimInt + Debug + Display + NumInfo + FromPrimitive + WrappingSub,
 727    NttOp: Ntt<Element = M::MatElement>,
 728    RlweModOp: ArithmeticOps<Element = M::MatElement>
 729        + VectorOps<Element = M::MatElement>
 730        + GetModulus<Element = M::MatElement, M = CiphertextModulus<M::MatElement>>,
 731    LweModOp: ArithmeticOps<Element = M::MatElement>
 732        + VectorOps<Element = M::MatElement>
 733        + GetModulus<Element = M::MatElement, M = CiphertextModulus<M::MatElement>>,
 734    M::R: TryConvertFrom1<[i32], CiphertextModulus<M::MatElement>> + RowEntity + Debug,
 735    <M as Matrix>::R: RowMut,
 736    DefaultSecureRng: RandomFillGaussianInModulus<[M::MatElement], CiphertextModulus<M::MatElement>>
 737        + RandomFillUniformInModulus<[M::MatElement], CiphertextModulus<M::MatElement>>
 738        + RandomGaussianElementInModulus<M::MatElement, CiphertextModulus<M::MatElement>>
 739        + NewWithSeed,
 740{
 741    fn new(parameters: BoolParameters<M::MatElement>) -> Self
 742    where
 743        RlweModOp: ModInit<M = CiphertextModulus<M::MatElement>>,
 744        LweModOp: ModInit<M = CiphertextModulus<M::MatElement>>,
 745        NttOp: NttInit<CiphertextModulus<M::MatElement>>,
 746    {
 747        //TODO(Jay): Run sanity checks for modulus values in parameters
 748
 749        // generatr dlog map s.t. g^{k} % q = a, for all a \in Z*_{q}
 750        let g = parameters.g();
 751        let q = *parameters.br_q();
 752        let mut g_k_dlog_map = vec![0usize; q];
 753        for i in 0..q / 2 {
 754            let v = mod_exponent(g as u64, i as u64, q as u64) as usize;
 755            // g^i
 756            g_k_dlog_map[v] = i;
 757            // -(g^i)
 758            g_k_dlog_map[q - v] = i + (q / 2);
 759        }
 760
 761        let embedding_factor = (2 * parameters.rlwe_n().0) / q;
 762
 763        let rlwe_nttop = NttOp::new(parameters.rlwe_q(), parameters.rlwe_n().0);
 764        let rlwe_modop = RlweModOp::new(*parameters.rlwe_q());
 765        let lwe_modop = LweModOp::new(*parameters.lwe_q());
 766
 767        // set test vectors
 768        let q = *parameters.br_q();
 769        let qby2 = q >> 1;
 770        let qby8 = q >> 3;
 771        let mut nand_test_vec = M::R::zeros(qby2);
 772        // Q/8 (Q: rlwe_q)
 773        let true_m_el = parameters.rlwe_q().true_el();
 774        // -Q/8
 775        let false_m_el = parameters.rlwe_q().false_el();
 776        for i in 0..qby2 {
 777            if i < (3 * qby8) {
 778                nand_test_vec.as_mut()[i] = true_m_el;
 779            } else {
 780                nand_test_vec.as_mut()[i] = false_m_el;
 781            }
 782        }
 783
 784        // v(X) -> v(X^{-g})
 785        let (auto_map_index, auto_map_sign) = generate_auto_map(qby2, -(g as isize));
 786        let mut nand_test_vec_autog = M::R::zeros(qby2);
 787        izip!(
 788            nand_test_vec.as_ref().iter(),
 789            auto_map_index.iter(),
 790            auto_map_sign.iter()
 791        )
 792        .for_each(|(v, to_index, to_sign)| {
 793            if !to_sign {
 794                // negate
 795                nand_test_vec_autog.as_mut()[*to_index] = rlwe_modop.neg(v);
 796            } else {
 797                nand_test_vec_autog.as_mut()[*to_index] = *v;
 798            }
 799        });
 800
 801        // auto map indices and sign
 802        let mut rlwe_auto_maps = vec![];
 803        let ring_size = parameters.rlwe_n().0;
 804        let g = parameters.g() as isize;
 805        for i in [g, -g] {
 806            rlwe_auto_maps.push(generate_auto_map(ring_size, i))
 807        }
 808
 809        let rlwe_qby4 = parameters.rlwe_q().qby4();
 810
 811        let scratch_memory = ScratchMemory::new(&parameters);
 812
 813        let pbs_info = BoolPbsInfo {
 814            auto_decomposer: parameters.auto_decomposer(),
 815            lwe_decomposer: parameters.lwe_decomposer(),
 816            rlwe_rgsw_decomposer: parameters.rlwe_rgsw_decomposer(),
 817            g_k_dlog_map,
 818            embedding_factor,
 819            lwe_modop,
 820            rlwe_modop,
 821            rlwe_nttop,
 822            nand_test_vec: nand_test_vec_autog,
 823            rlwe_qby4,
 824            rlwe_auto_maps,
 825            parameters: parameters,
 826        };
 827
 828        BoolEvaluator {
 829            pbs_info,
 830            scratch_memory,
 831            _phantom: PhantomData,
 832        }
 833    }
 834
 835    fn client_key(&self) -> ClientKey {
 836        let sk_lwe = LweSecret::random(
 837            self.pbs_info.parameters.lwe_n().0 >> 1,
 838            self.pbs_info.parameters.lwe_n().0,
 839        );
 840        let sk_rlwe = RlweSecret::random(
 841            self.pbs_info.parameters.rlwe_n().0 >> 1,
 842            self.pbs_info.parameters.rlwe_n().0,
 843        );
 844        ClientKey { sk_rlwe, sk_lwe }
 845    }
 846
 847    fn server_key(
 848        &self,
 849        client_key: &ClientKey,
 850    ) -> SeededServerKey<M, BoolParameters<M::MatElement>, [u8; 32]> {
 851        DefaultSecureRng::with_local_mut(|rng| {
 852            let mut main_seed = [0u8; 32];
 853            rng.fill_bytes(&mut main_seed);
 854
 855            let mut main_prng = DefaultSecureRng::new_seeded(main_seed);
 856
 857            let rlwe_n = self.pbs_info.parameters.rlwe_n().0;
 858            let sk_rlwe = &client_key.sk_rlwe;
 859            let sk_lwe = &client_key.sk_lwe;
 860
 861            // generate auto keys -g, g
 862            let mut auto_keys = HashMap::new();
 863            let auto_gadget = self.pbs_info.auto_decomposer.gadget_vector();
 864            let g = self.pbs_info.parameters.g() as isize;
 865            for i in [g, -g] {
 866                let mut gk = M::zeros(self.pbs_info.auto_decomposer.decomposition_count(), rlwe_n);
 867                galois_key_gen(
 868                    &mut gk,
 869                    sk_rlwe.values(),
 870                    i,
 871                    &auto_gadget,
 872                    &self.pbs_info.rlwe_modop,
 873                    &self.pbs_info.rlwe_nttop,
 874                    &mut main_prng,
 875                    rng,
 876                );
 877                auto_keys.insert(i, gk);
 878            }
 879
 880            // generate rgsw ciphertexts RGSW(si) where si is i^th LWE secret element
 881            let ring_size = self.pbs_info.parameters.rlwe_n().0;
 882            let rlwe_q = self.pbs_info.parameters.rlwe_q();
 883            let (rlrg_d_a, rlrg_d_b) = (
 884                self.pbs_info.rlwe_rgsw_decomposer.0.decomposition_count(),
 885                self.pbs_info.rlwe_rgsw_decomposer.1.decomposition_count(),
 886            );
 887            let rlrg_gadget_a = self.pbs_info.rlwe_rgsw_decomposer.0.gadget_vector();
 888            let rlrg_gadget_b = self.pbs_info.rlwe_rgsw_decomposer.1.gadget_vector();
 889            let rgsw_cts = sk_lwe
 890                .values()
 891                .iter()
 892                .map(|si| {
 893                    // X^{si}; assume |emebedding_factor * si| < N
 894                    let mut m = M::R::zeros(ring_size);
 895                    let si = (self.pbs_info.embedding_factor as i32) * si;
 896                    // dbg!(si);
 897                    if si < 0 {
 898                        // X^{-i} = X^{2N - i} = -X^{N-i}
 899                        m.as_mut()[ring_size - (si.abs() as usize)] = rlwe_q.neg_one();
 900                    } else {
 901                        // X^{i}
 902                        m.as_mut()[si.abs() as usize] = M::MatElement::one();
 903                    }
 904
 905                    let mut rgsw_si = M::zeros(rlrg_d_a * 2 + rlrg_d_b, ring_size);
 906                    secret_key_encrypt_rgsw(
 907                        &mut rgsw_si,
 908                        m.as_ref(),
 909                        &rlrg_gadget_a,
 910                        &rlrg_gadget_b,
 911                        sk_rlwe.values(),
 912                        &self.pbs_info.rlwe_modop,
 913                        &self.pbs_info.rlwe_nttop,
 914                        &mut main_prng,
 915                        rng,
 916                    );
 917
 918                    rgsw_si
 919                })
 920                .collect_vec();
 921
 922            // LWE KSK from RLWE secret s -> LWE secret z
 923            let d_lwe_gadget = self.pbs_info.lwe_decomposer.gadget_vector();
 924            let mut lwe_ksk =
 925                M::R::zeros(self.pbs_info.lwe_decomposer.decomposition_count() * ring_size);
 926            lwe_ksk_keygen(
 927                &sk_rlwe.values(),
 928                &sk_lwe.values(),
 929                &mut lwe_ksk,
 930                &d_lwe_gadget,
 931                &self.pbs_info.lwe_modop,
 932                &mut main_prng,
 933                rng,
 934            );
 935
 936            SeededServerKey::from_raw(
 937                auto_keys,
 938                rgsw_cts,
 939                lwe_ksk,
 940                self.pbs_info.parameters.clone(),
 941                main_seed,
 942            )
 943        })
 944    }
 945
 946    fn multi_party_server_key_share(
 947        &self,
 948        cr_seed: [u8; 32],
 949        collective_pk: &M,
 950        client_key: &ClientKey,
 951    ) -> CommonReferenceSeededMultiPartyServerKeyShare<M, BoolParameters<M::MatElement>, [u8; 32]>
 952    {
 953        DefaultSecureRng::with_local_mut(|rng| {
 954            let mut main_prng = DefaultSecureRng::new_seeded(cr_seed);
 955
 956            let sk_rlwe = &client_key.sk_rlwe;
 957            let sk_lwe = &client_key.sk_lwe;
 958
 959            let g = self.pbs_info.parameters.g() as isize;
 960            let ring_size = self.pbs_info.parameters.rlwe_n().0;
 961            let rlwe_q = self.pbs_info.parameters.rlwe_q();
 962            let lwe_q = self.pbs_info.parameters.lwe_q();
 963
 964            let rlweq_modop = &self.pbs_info.rlwe_modop;
 965            let rlweq_nttop = &self.pbs_info.rlwe_nttop;
 966
 967            // sanity check
 968            assert!(sk_rlwe.values().len() == ring_size);
 969            assert!(sk_lwe.values().len() == self.pbs_info.parameters.lwe_n().0);
 970
 971            // auto keys
 972            let mut auto_keys = HashMap::new();
 973            let auto_gadget = self.pbs_info.auto_decomposer.gadget_vector();
 974            for i in [g, -g] {
 975                let mut ksk_out = M::zeros(
 976                    self.pbs_info.auto_decomposer.decomposition_count(),
 977                    ring_size,
 978                );
 979                galois_key_gen(
 980                    &mut ksk_out,
 981                    sk_rlwe.values(),
 982                    i,
 983                    &auto_gadget,
 984                    rlweq_modop,
 985                    rlweq_nttop,
 986                    &mut main_prng,
 987                    rng,
 988                );
 989                auto_keys.insert(i, ksk_out);
 990            }
 991
 992            // rgsw ciphertexts of lwe secret elements
 993            let rgsw_rgsw_decomposer = self
 994                .pbs_info
 995                .parameters
 996                .rgsw_rgsw_decomposer::<DefaultDecomposer<M::MatElement>>();
 997            let (rgrg_d_a, rgrg_d_b) = (
 998                rgsw_rgsw_decomposer.0.decomposition_count(),
 999                rgsw_rgsw_decomposer.1.decomposition_count(),
1000            );
1001            let (rgrg_gadget_a, rgrg_gadget_b) = (
1002                rgsw_rgsw_decomposer.0.gadget_vector(),
1003                rgsw_rgsw_decomposer.1.gadget_vector(),
1004            );
1005            let rgsw_cts = sk_lwe
1006                .values()
1007                .iter()
1008                .map(|si| {
1009                    let mut m = M::R::zeros(ring_size);
1010                    //TODO(Jay): It will be nice to have a function that returns polynomial
1011                    // (monomial infact!) corresponding to secret element embedded in ring X^{2N+1}.
1012                    // Save lots of mistakes where one forgest to emebed si in bigger ring.
1013                    let si = *si * (self.pbs_info.embedding_factor as i32);
1014                    if si < 0 {
1015                        // X^{-si} = X^{2N-si} = -X^{N-si}, assuming abs(si) < N
1016                        // (which it is given si is secret element)
1017                        m.as_mut()[ring_size - (si.abs() as usize)] = rlwe_q.neg_one();
1018                    } else {
1019                        m.as_mut()[si as usize] = M::MatElement::one();
1020                    }
1021
1022                    // public key RGSW encryption has no part that can be seeded, unlike secret key
1023                    // RGSW encryption where RLWE'_A(m) is seeded
1024                    let mut out_rgsw = M::zeros(rgrg_d_a * 2 + rgrg_d_b * 2, ring_size);
1025                    public_key_encrypt_rgsw(
1026                        &mut out_rgsw,
1027                        &m.as_ref(),
1028                        collective_pk,
1029                        &rgrg_gadget_a,
1030                        &rgrg_gadget_b,
1031                        rlweq_modop,
1032                        rlweq_nttop,
1033                        rng,
1034                    );
1035
1036                    out_rgsw
1037                })
1038                .collect_vec();
1039
1040            // LWE ksk
1041            let mut lwe_ksk =
1042                M::R::zeros(self.pbs_info.lwe_decomposer.decomposition_count() * ring_size);
1043            let lwe_modop = &self.pbs_info.lwe_modop;
1044            let d_lwe_gadget_vec = self.pbs_info.lwe_decomposer.gadget_vector();
1045            lwe_ksk_keygen(
1046                sk_rlwe.values(),
1047                sk_lwe.values(),
1048                &mut lwe_ksk,
1049                &d_lwe_gadget_vec,
1050                lwe_modop,
1051                &mut main_prng,
1052                rng,
1053            );
1054
1055            CommonReferenceSeededMultiPartyServerKeyShare {
1056                auto_keys,
1057                rgsw_cts,
1058                lwe_ksk,
1059                cr_seed,
1060                parameters: self.pbs_info.parameters.clone(),
1061            }
1062        })
1063    }
1064
1065    fn multi_party_public_key_share(
1066        &self,
1067        cr_seed: [u8; 32],
1068        client_key: &ClientKey,
1069    ) -> CommonReferenceSeededCollectivePublicKeyShare<
1070        <M as Matrix>::R,
1071        [u8; 32],
1072        BoolParameters<<M as Matrix>::MatElement>,
1073    > {
1074        DefaultSecureRng::with_local_mut(|rng| {
1075            let mut share_out = M::R::zeros(self.pbs_info.parameters.rlwe_n().0);
1076            let modop = &self.pbs_info.rlwe_modop;
1077            let nttop = &self.pbs_info.rlwe_nttop;
1078            let mut main_prng = DefaultSecureRng::new_seeded(cr_seed);
1079            public_key_share(
1080                &mut share_out,
1081                client_key.sk_rlwe.values(),
1082                modop,
1083                nttop,
1084                &mut main_prng,
1085                rng,
1086            );
1087
1088            CommonReferenceSeededCollectivePublicKeyShare {
1089                share: share_out,
1090                cr_seed: cr_seed,
1091                parameters: self.pbs_info.parameters.clone(),
1092            }
1093        })
1094    }
1095
1096    fn multi_party_decryption_share(
1097        &self,
1098        lwe_ct: &M::R,
1099        client_key: &ClientKey,
1100    ) -> MultiPartyDecryptionShare<<M as Matrix>::MatElement> {
1101        assert!(lwe_ct.as_ref().len() == self.pbs_info.parameters.rlwe_n().0 + 1);
1102        let modop = &self.pbs_info.rlwe_modop;
1103        let mut neg_s = M::R::try_convert_from(
1104            client_key.sk_rlwe.values(),
1105            &self.pbs_info.parameters.rlwe_q(),
1106        );
1107        modop.elwise_neg_mut(neg_s.as_mut());
1108
1109        let mut neg_sa = M::MatElement::zero();
1110        izip!(lwe_ct.as_ref().iter().skip(1), neg_s.as_ref().iter()).for_each(|(ai, nsi)| {
1111            neg_sa = modop.add(&neg_sa, &modop.mul(ai, nsi));
1112        });
1113
1114        let e = DefaultSecureRng::with_local_mut(|rng| {
1115            let mut e =
1116                RandomGaussianElementInModulus::random(rng, self.pbs_info.parameters.rlwe_q());
1117            e
1118        });
1119        let share = modop.add(&neg_sa, &e);
1120
1121        MultiPartyDecryptionShare { share }
1122    }
1123
1124    pub(crate) fn multi_party_decrypt(
1125        &self,
1126        shares: &[MultiPartyDecryptionShare<M::MatElement>],
1127        lwe_ct: &M::R,
1128    ) -> bool {
1129        let modop = &self.pbs_info.rlwe_modop;
1130        let mut sum_a = M::MatElement::zero();
1131        shares
1132            .iter()
1133            .for_each(|share_i| sum_a = modop.add(&sum_a, &share_i.share));
1134
1135        let encoded_m = modop.add(&lwe_ct.as_ref()[0], &sum_a);
1136        self.pbs_info.parameters.rlwe_q().decode(encoded_m)
1137    }
1138
1139    /// First encrypt as RLWE(m) with m as constant polynomial and extract it as
1140    /// LWE ciphertext
1141    pub(crate) fn pk_encrypt(&self, pk: &M, m: bool) -> M::R {
1142        DefaultSecureRng::with_local_mut(|rng| {
1143            let modop = &self.pbs_info.rlwe_modop;
1144            let nttop = &self.pbs_info.rlwe_nttop;
1145
1146            // RLWE(0)
1147            // sample ephemeral key u
1148            let ring_size = self.pbs_info.parameters.rlwe_n().0;
1149            let mut u = vec![0i32; ring_size];
1150            fill_random_ternary_secret_with_hamming_weight(u.as_mut(), ring_size >> 1, rng);
1151            let mut u = M::R::try_convert_from(&u, &self.pbs_info.parameters.rlwe_q());
1152            nttop.forward(u.as_mut());
1153
1154            let mut ua = M::R::zeros(ring_size);
1155            ua.as_mut().copy_from_slice(pk.get_row_slice(0));
1156            let mut ub = M::R::zeros(ring_size);
1157            ub.as_mut().copy_from_slice(pk.get_row_slice(1));
1158
1159            // a*u
1160            nttop.forward(ua.as_mut());
1161            modop.elwise_mul_mut(ua.as_mut(), u.as_ref());
1162            nttop.backward(ua.as_mut());
1163
1164            // b*u
1165            nttop.forward(ub.as_mut());
1166            modop.elwise_mul_mut(ub.as_mut(), u.as_ref());
1167            nttop.backward(ub.as_mut());
1168
1169            let mut rlwe = M::zeros(2, ring_size);
1170            // sample error
1171            rlwe.iter_rows_mut().for_each(|ri| {
1172                RandomFillGaussianInModulus::random_fill(
1173                    rng,
1174                    &self.pbs_info.parameters.rlwe_q(),
1175                    ri.as_mut(),
1176                );
1177            });
1178
1179            // a*u + e0
1180            modop.elwise_add_mut(rlwe.get_row_mut(0), ua.as_ref());
1181            // b*u + e1
1182            modop.elwise_add_mut(rlwe.get_row_mut(1), ub.as_ref());
1183
1184            //FIXME(Jay): Figure out a way to get Q/8 form modulus
1185            let m = if m {
1186                // Q/8
1187                self.pbs_info.rlwe_q().true_el()
1188            } else {
1189                // -Q/8
1190                self.pbs_info.rlwe_q().false_el()
1191            };
1192
1193            // b*u + e1 + m, where m is constant polynomial
1194            rlwe.set(1, 0, modop.add(rlwe.get(1, 0), &m));
1195
1196            // sample extract index 0
1197            let mut lwe_out = M::R::zeros(ring_size + 1);
1198            sample_extract(&mut lwe_out, &rlwe, modop, 0);
1199
1200            lwe_out
1201        })
1202    }
1203
1204    /// TODO(Jay): Fetch client key from thread local
1205    pub fn sk_encrypt(&self, m: bool, client_key: &ClientKey) -> M::R {
1206        //FIXME(Jay): Figure out a way to get Q/8 form modulus
1207        let m = if m {
1208            // Q/8
1209            self.pbs_info.rlwe_q().true_el()
1210        } else {
1211            // -Q/8
1212            self.pbs_info.rlwe_q().false_el()
1213        };
1214
1215        DefaultSecureRng::with_local_mut(|rng| {
1216            let mut lwe_out = M::R::zeros(self.pbs_info.parameters.rlwe_n().0 + 1);
1217            encrypt_lwe(
1218                &mut lwe_out,
1219                &m,
1220                client_key.sk_rlwe.values(),
1221                &self.pbs_info.rlwe_modop,
1222                rng,
1223            );
1224            lwe_out
1225        })
1226    }
1227
1228    pub fn sk_decrypt(&self, lwe_ct: &M::R, client_key: &ClientKey) -> bool {
1229        let m = decrypt_lwe(
1230            lwe_ct,
1231            client_key.sk_rlwe.values(),
1232            &self.pbs_info.rlwe_modop,
1233        );
1234        self.pbs_info.rlwe_q().decode(m)
1235    }
1236
1237    fn aggregate_multi_party_server_key_shares<S>(
1238        &self,
1239        shares: &[CommonReferenceSeededMultiPartyServerKeyShare<
1240            M,
1241            BoolParameters<M::MatElement>,
1242            S,
1243        >],
1244    ) -> SeededMultiPartyServerKey<M, S, BoolParameters<M::MatElement>>
1245    where
1246        S: PartialEq + Clone,
1247        M: Clone,
1248    {
1249        assert!(shares.len() > 0);
1250        let parameters = shares[0].parameters.clone();
1251        let cr_seed = &shares[0].cr_seed;
1252
1253        let rlwe_n = parameters.rlwe_n().0;
1254        let g = parameters.g() as isize;
1255        let rlwe_q = parameters.rlwe_q();
1256        let lwe_q = parameters.lwe_q();
1257
1258        // sanity checks
1259        shares.iter().skip(1).for_each(|s| {
1260            assert!(s.parameters == parameters);
1261            assert!(&s.cr_seed == cr_seed);
1262        });
1263
1264        let rlweq_modop = &self.pbs_info.rlwe_modop;
1265        let rlweq_nttop = &self.pbs_info.rlwe_nttop;
1266
1267        // auto keys
1268        let mut auto_keys = HashMap::new();
1269        for i in [g, -g] {
1270            let mut key = M::zeros(parameters.auto_decomposition_count().0, rlwe_n);
1271
1272            shares.iter().for_each(|s| {
1273                let auto_key_share_i = s.auto_keys.get(&i).expect("Auto key {i} missing");
1274                assert!(
1275                    auto_key_share_i.dimension()
1276                        == (parameters.auto_decomposition_count().0, rlwe_n)
1277                );
1278                izip!(key.iter_rows_mut(), auto_key_share_i.iter_rows()).for_each(
1279                    |(partb_out, partb_share)| {
1280                        rlweq_modop.elwise_add_mut(partb_out.as_mut(), partb_share.as_ref());
1281                    },
1282                );
1283            });
1284
1285            auto_keys.insert(i, key);
1286        }
1287
1288        // rgsw ciphertext (most expensive part!)
1289        let lwe_n = parameters.lwe_n().0;
1290        let rgsw_by_rgsw_decomposer =
1291            parameters.rgsw_rgsw_decomposer::<DefaultDecomposer<M::MatElement>>();
1292        let mut scratch_matrix = M::zeros(
1293            std::cmp::max(
1294                rgsw_by_rgsw_decomposer.a().decomposition_count(),
1295                rgsw_by_rgsw_decomposer.b().decomposition_count(),
1296            ) + (rgsw_by_rgsw_decomposer.a().decomposition_count() * 2
1297                + rgsw_by_rgsw_decomposer.b().decomposition_count() * 2),
1298            rlwe_n,
1299        );
1300
1301        let mut tmp_rgsw =
1302            RgswCiphertext::<M, _>::empty(rlwe_n, &rgsw_by_rgsw_decomposer, rlwe_q.clone()).data;
1303        let rgsw_cts = (0..lwe_n).into_iter().map(|index| {
1304            // copy over rgsw ciphertext for index^th secret element from first share and
1305            // treat it as accumulating rgsw ciphertext
1306            let mut rgsw_i = shares[0].rgsw_cts[index].clone();
1307
1308            shares.iter().skip(1).for_each(|si| {
1309                // copy over si's RGSW[index] ciphertext and send to evaluation domain
1310                izip!(tmp_rgsw.iter_rows_mut(), si.rgsw_cts[index].iter_rows()).for_each(
1311                    |(to_ri, from_ri)| {
1312                        to_ri.as_mut().copy_from_slice(from_ri.as_ref());
1313                        rlweq_nttop.forward(to_ri.as_mut())
1314                    },
1315                );
1316
1317                rgsw_by_rgsw_inplace(
1318                    &mut rgsw_i,
1319                    &tmp_rgsw,
1320                    &rgsw_by_rgsw_decomposer,
1321                    &mut scratch_matrix,
1322                    rlweq_nttop,
1323                    rlweq_modop,
1324                );
1325            });
1326
1327            rgsw_i
1328        });
1329        // d_a and d_b may differ for RGSWxRGSW multiplication and RLWExRGSW
1330        // multiplication. After this point RGSW ciphertexts will only be used for
1331        // RLWExRGSW multiplication (in blind rotation). Thus we drop any additional
1332        // RLWE ciphertexts in RGSW ciphertexts after RGSw x RGSW multiplication
1333        let (rgswrgsw_d_a, rgswrgsw_d_b) = self.pbs_info.parameters.rgsw_rgsw_decomposition_count();
1334        let (rlrg_d_a, rlrg_d_b) = self.pbs_info.parameters.rlwe_rgsw_decomposition_count();
1335        let rgsw_ct_rows_in = rgswrgsw_d_a.0 * 2 + rgswrgsw_d_b.0 * 2;
1336        let rgsw_ct_rows_out = rlrg_d_a.0 * 2 + rlrg_d_b.0 * 2;
1337        assert!(rgswrgsw_d_a.0 >= rlrg_d_a.0, "RGSWxRGSW part A decomposition count {} must be >= RLWExRGSW part A decomposition count {}", rgswrgsw_d_a.0 , rlrg_d_a.0);
1338        assert!(rgswrgsw_d_b.0 >= rlrg_d_b.0, "RGSWxRGSW part B decomposition count {} must be >= RLWExRGSW part B decomposition count {}", rgswrgsw_d_b.0 , rlrg_d_b.0);
1339        let rgsw_cts = rgsw_cts
1340            .map(|ct_i_in| {
1341                assert!(ct_i_in.dimension() == (rgsw_ct_rows_in, rlwe_n));
1342                let mut reduced_ct_i_out = M::zeros(rgsw_ct_rows_out, rlwe_n);
1343
1344                // RLWE'(-sm) part A
1345                izip!(
1346                    reduced_ct_i_out.iter_rows_mut().take(rlrg_d_a.0),
1347                    ct_i_in
1348                        .iter_rows()
1349                        .skip(rgswrgsw_d_a.0 - rlrg_d_a.0)
1350                        .take(rlrg_d_a.0)
1351                )
1352                .for_each(|(to_ri, from_ri)| {
1353                    to_ri.as_mut().copy_from_slice(from_ri.as_ref());
1354                });
1355
1356                // RLWE'(-sm) part B
1357                izip!(
1358                    reduced_ct_i_out
1359                        .iter_rows_mut()
1360                        .skip(rlrg_d_a.0)
1361                        .take(rlrg_d_a.0),
1362                    ct_i_in
1363                        .iter_rows()
1364                        .skip(rgswrgsw_d_a.0 + (rgswrgsw_d_a.0 - rlrg_d_a.0))
1365                        .take(rlrg_d_a.0)
1366                )
1367                .for_each(|(to_ri, from_ri)| {
1368                    to_ri.as_mut().copy_from_slice(from_ri.as_ref());
1369                });
1370
1371                // RLWE'(m) Part A
1372                izip!(
1373                    reduced_ct_i_out
1374                        .iter_rows_mut()
1375                        .skip(rlrg_d_a.0 * 2)
1376                        .take(rlrg_d_b.0),
1377                    ct_i_in
1378                        .iter_rows()
1379                        .skip(rgswrgsw_d_a.0 * 2 + (rgswrgsw_d_b.0 - rlrg_d_b.0))
1380                        .take(rlrg_d_b.0)
1381                )
1382                .for_each(|(to_ri, from_ri)| {
1383                    to_ri.as_mut().copy_from_slice(from_ri.as_ref());
1384                });
1385
1386                // RLWE'(m) Part B
1387                izip!(
1388                    reduced_ct_i_out
1389                        .iter_rows_mut()
1390                        .skip(rlrg_d_a.0 * 2 + rlrg_d_b.0)
1391                        .take(rlrg_d_b.0),
1392                    ct_i_in
1393                        .iter_rows()
1394                        .skip(rgswrgsw_d_a.0 * 2 + rgswrgsw_d_b.0 + (rgswrgsw_d_b.0 - rlrg_d_b.0))
1395                        .take(rlrg_d_b.0)
1396                )
1397                .for_each(|(to_ri, from_ri)| {
1398                    to_ri.as_mut().copy_from_slice(from_ri.as_ref());
1399                });
1400
1401                reduced_ct_i_out
1402            })
1403            .collect_vec();
1404
1405        // LWE ksks
1406        let mut lwe_ksk = M::R::zeros(rlwe_n * parameters.lwe_decomposition_count().0);
1407        let lweq_modop = &self.pbs_info.lwe_modop;
1408        shares.iter().for_each(|si| {
1409            assert!(si.lwe_ksk.as_ref().len() == rlwe_n * parameters.lwe_decomposition_count().0);
1410            lweq_modop.elwise_add_mut(lwe_ksk.as_mut(), si.lwe_ksk.as_ref())
1411        });
1412
1413        SeededMultiPartyServerKey {
1414            rgsw_cts,
1415            auto_keys,
1416            lwe_ksk,
1417            cr_seed: cr_seed.clone(),
1418            parameters: parameters,
1419        }
1420    }
1421
1422    // TODO(Jay): scratch spaces must be thread local. Don't pass them as arguments
1423    pub fn nand(
1424        &mut self,
1425        c0: &M::R,
1426        c1: &M::R,
1427        server_key: &ServerKeyEvaluationDomain<M, DefaultSecureRng, NttOp>,
1428    ) -> M::R {
1429        let mut c_out = M::R::zeros(c0.as_ref().len());
1430        let modop = &self.pbs_info.rlwe_modop;
1431        izip!(
1432            c_out.as_mut().iter_mut(),
1433            c0.as_ref().iter(),
1434            c1.as_ref().iter()
1435        )
1436        .for_each(|(o, i0, i1)| {
1437            *o = modop.add(i0, i1);
1438        });
1439        // +Q/4
1440        c_out.as_mut()[0] = modop.add(&c_out.as_ref()[0], &self.pbs_info.rlwe_qby4);
1441
1442        // PBS
1443        pbs(
1444            &self.pbs_info,
1445            &self.pbs_info.nand_test_vec,
1446            &mut c_out,
1447            server_key,
1448            &mut self.scratch_memory.lwe_vector,
1449            &mut self.scratch_memory.decomposition_matrix,
1450        );
1451
1452        c_out
1453    }
1454}
1455
1456/// LMKCY+ Blind rotation
1457///
1458/// gk_to_si: [g^0, ..., g^{q/2-1}, -g^0, -g^1, .., -g^{q/2-1}]
1459fn blind_rotation<
1460    MT: IsTrivial + MatrixMut,
1461    Mmut: MatrixMut<MatElement = MT::MatElement> + Matrix,
1462    D: Decomposer<Element = MT::MatElement>,
1463    NttOp: Ntt<Element = MT::MatElement>,
1464    ModOp: ArithmeticOps<Element = MT::MatElement> + VectorOps<Element = MT::MatElement>,
1465    K: PbsKey<M = Mmut>,
1466    P: PbsInfo<Element = MT::MatElement>,
1467>(
1468    trivial_rlwe_test_poly: &mut MT,
1469    scratch_matrix: &mut Mmut,
1470    g: isize,
1471    w: usize,
1472    q: usize,
1473    gk_to_si: &[Vec<usize>],
1474    rlwe_rgsw_decomposer: &(D, D),
1475    auto_decomposer: &D,
1476    ntt_op: &NttOp,
1477    mod_op: &ModOp,
1478    parameters: &P,
1479    pbs_key: &K,
1480) where
1481    <Mmut as Matrix>::R: RowMut,
1482    Mmut::MatElement: Copy + Zero,
1483    <MT as Matrix>::R: RowMut,
1484{
1485    let q_by_2 = q / 2;
1486
1487    // -(g^k)
1488    for i in (1..q_by_2).rev() {
1489        gk_to_si[q_by_2 + i].iter().for_each(|s_index| {
1490            rlwe_by_rgsw(
1491                trivial_rlwe_test_poly,
1492                pbs_key.rgsw_ct_lwe_si(*s_index),
1493                scratch_matrix,
1494                rlwe_rgsw_decomposer,
1495                ntt_op,
1496                mod_op,
1497            );
1498        });
1499
1500        let (auto_map_index, auto_map_sign) = parameters.rlwe_auto_map(g);
1501        galois_auto(
1502            trivial_rlwe_test_poly,
1503            pbs_key.galois_key_for_auto(g),
1504            scratch_matrix,
1505            &auto_map_index,
1506            &auto_map_sign,
1507            mod_op,
1508            ntt_op,
1509            auto_decomposer,
1510        );
1511    }
1512
1513    // -(g^0)
1514    gk_to_si[q_by_2].iter().for_each(|s_index| {
1515        rlwe_by_rgsw(
1516            trivial_rlwe_test_poly,
1517            pbs_key.rgsw_ct_lwe_si(*s_index),
1518            scratch_matrix,
1519            rlwe_rgsw_decomposer,
1520            ntt_op,
1521            mod_op,
1522        );
1523    });
1524    let (auto_map_index, auto_map_sign) = parameters.rlwe_auto_map(-g);
1525    galois_auto(
1526        trivial_rlwe_test_poly,
1527        pbs_key.galois_key_for_auto(-g),
1528        scratch_matrix,
1529        &auto_map_index,
1530        &auto_map_sign,
1531        mod_op,
1532        ntt_op,
1533        auto_decomposer,
1534    );
1535
1536    // +(g^k)
1537    for i in (1..q_by_2).rev() {
1538        gk_to_si[i].iter().for_each(|s_index| {
1539            rlwe_by_rgsw(
1540                trivial_rlwe_test_poly,
1541                pbs_key.rgsw_ct_lwe_si(*s_index),
1542                scratch_matrix,
1543                rlwe_rgsw_decomposer,
1544                ntt_op,
1545                mod_op,
1546            );
1547        });
1548
1549        let (auto_map_index, auto_map_sign) = parameters.rlwe_auto_map(g);
1550        galois_auto(
1551            trivial_rlwe_test_poly,
1552            pbs_key.galois_key_for_auto(g),
1553            scratch_matrix,
1554            &auto_map_index,
1555            &auto_map_sign,
1556            mod_op,
1557            ntt_op,
1558            auto_decomposer,
1559        );
1560    }
1561
1562    // +(g^0)
1563    gk_to_si[0].iter().for_each(|s_index| {
1564        rlwe_by_rgsw(
1565            trivial_rlwe_test_poly,
1566            pbs_key.rgsw_ct_lwe_si(gk_to_si[q_by_2][*s_index]),
1567            scratch_matrix,
1568            rlwe_rgsw_decomposer,
1569            ntt_op,
1570            mod_op,
1571        );
1572    });
1573}
1574
1575/// - Mod down
1576/// - key switching
1577/// - mod down
1578/// - blind rotate
1579fn pbs<
1580    M: Matrix + MatrixMut + MatrixEntity,
1581    P: PbsInfo<Element = M::MatElement>,
1582    K: PbsKey<M = M>,
1583>(
1584    pbs_info: &P,
1585    test_vec: &M::R,
1586    lwe_in: &mut M::R,
1587    pbs_key: &K,
1588    scratch_lwe_vec: &mut M::R,
1589    scratch_blind_rotate_matrix: &mut M,
1590) where
1591    <M as Matrix>::R: RowMut,
1592    M::MatElement: PrimInt + ToPrimitive + FromPrimitive + One + Copy + Zero + Display,
1593{
1594    let rlwe_q = pbs_info.rlwe_q();
1595    let lwe_q = pbs_info.lwe_q();
1596    let br_q = pbs_info.br_q();
1597    let rlwe_qf64 = rlwe_q.q_as_f64().unwrap();
1598    let lwe_qf64 = lwe_q.q_as_f64().unwrap();
1599    let br_qf64 = br_q.to_f64().unwrap();
1600    let rlwe_n = pbs_info.rlwe_n();
1601
1602    // PBSTracer::with_local_mut(|t| {
1603    //     let out = lwe_in
1604    //         .as_ref()
1605    //         .iter()
1606    //         .map(|v| v.to_u64().unwrap())
1607    //         .collect_vec();
1608    //     t.ct_rlwe_q_mod = out;
1609    // });
1610
1611    // moddown Q -> Q_ks
1612    lwe_in.as_mut().iter_mut().for_each(|v| {
1613        *v =
1614            M::MatElement::from_f64(((v.to_f64().unwrap() * lwe_qf64) / rlwe_qf64).round()).unwrap()
1615    });
1616
1617    // PBSTracer::with_local_mut(|t| {
1618    //     let out = lwe_in
1619    //         .as_ref()
1620    //         .iter()
1621    //         .map(|v| v.to_u64().unwrap())
1622    //         .collect_vec();
1623    //     t.ct_lwe_q_mod = out;
1624    // });
1625
1626    // key switch RLWE secret to LWE secret
1627    scratch_lwe_vec.as_mut().fill(M::MatElement::zero());
1628    lwe_key_switch(
1629        scratch_lwe_vec,
1630        lwe_in,
1631        pbs_key.lwe_ksk(),
1632        pbs_info.modop_lweq(),
1633        pbs_info.lwe_decomposer(),
1634    );
1635
1636    // PBSTracer::with_local_mut(|t| {
1637    //     let out = scratch_lwe_vec
1638    //         .as_ref()
1639    //         .iter()
1640    //         .map(|v| v.to_u64().unwrap())
1641    //         .collect_vec();
1642    //     t.ct_lwe_q_mod_after_ksk = out;
1643    // });
1644
1645    // odd mowdown Q_ks -> q
1646    let g_k_dlog_map = pbs_info.g_k_dlog_map();
1647    let mut g_k_si = vec![vec![]; br_q];
1648    scratch_lwe_vec
1649        .as_ref()
1650        .iter()
1651        .skip(1)
1652        .enumerate()
1653        .for_each(|(index, v)| {
1654            let odd_v = mod_switch_odd(v.to_f64().unwrap(), lwe_qf64, br_qf64);
1655            let k = g_k_dlog_map[odd_v];
1656            g_k_si[k].push(index);
1657        });
1658
1659    // PBSTracer::with_local_mut(|t| {
1660    //     let out = scratch_lwe_vec
1661    //         .as_ref()
1662    //         .iter()
1663    //         .map(|v| mod_switch_odd(v.to_f64().unwrap(), lwe_qf64, br_qf64) as
1664    // u64)         .collect_vec();
1665    //     t.ct_br_q_mod = out;
1666    // });
1667
1668    // handle b and set trivial test RLWE
1669    let g = pbs_info.g() as usize;
1670    let g_times_b = (g * mod_switch_odd(
1671        scratch_lwe_vec.as_ref()[0].to_f64().unwrap(),
1672        lwe_qf64,
1673        br_qf64,
1674    )) % (br_q);
1675    // v = (v(X) * X^{g*b}) mod X^{q/2}+1
1676    let br_qby2 = br_q / 2;
1677    let mut gb_monomial_sign = true;
1678    let mut gb_monomial_exp = g_times_b;
1679    // X^{g*b} mod X^{q/2}+1
1680    if gb_monomial_exp > br_qby2 {
1681        gb_monomial_exp -= br_qby2;
1682        gb_monomial_sign = false
1683    }
1684    // monomial mul
1685    let mut trivial_rlwe_test_poly = RlweCiphertext::<_, DefaultSecureRng> {
1686        data: M::zeros(2, rlwe_n),
1687        is_trivial: true,
1688        _phatom: PhantomData,
1689    };
1690    if pbs_info.embedding_factor() == 1 {
1691        monomial_mul(
1692            test_vec.as_ref(),
1693            trivial_rlwe_test_poly.get_row_mut(1).as_mut(),
1694            gb_monomial_exp,
1695            gb_monomial_sign,
1696            br_qby2,
1697            pbs_info.modop_rlweq(),
1698        );
1699    } else {
1700        // use lwe_in to store the `t = v(X) * X^{g*2} mod X^{q/2}+1` temporarily. This
1701        // works because q/2 <= N (where N is lwe_in LWE dimension) always.
1702        monomial_mul(
1703            test_vec.as_ref(),
1704            &mut lwe_in.as_mut()[..br_qby2],
1705            gb_monomial_exp,
1706            gb_monomial_sign,
1707            br_qby2,
1708            pbs_info.modop_rlweq(),
1709        );
1710
1711        // emebed poly `t` in ring X^{q/2}+1 inside the bigger ring X^{N}+1
1712        let embed_factor = pbs_info.embedding_factor();
1713        let partb_trivial_rlwe = trivial_rlwe_test_poly.get_row_mut(1);
1714        lwe_in.as_ref()[..br_qby2]
1715            .iter()
1716            .enumerate()
1717            .for_each(|(index, v)| {
1718                partb_trivial_rlwe[embed_factor * index] = *v;
1719            });
1720    }
1721
1722    // blind rotate
1723    blind_rotation(
1724        &mut trivial_rlwe_test_poly,
1725        scratch_blind_rotate_matrix,
1726        pbs_info.g(),
1727        1,
1728        br_q,
1729        &g_k_si,
1730        pbs_info.rlwe_rgsw_decomposer(),
1731        pbs_info.auto_decomposer(),
1732        pbs_info.nttop_rlweq(),
1733        pbs_info.modop_rlweq(),
1734        pbs_info,
1735        pbs_key,
1736    );
1737
1738    // ClientKey::with_local(|ck| {
1739    //     let ring_size = parameters.rlwe_n();
1740    //     let mut rlwe_ct = vec![vec![0u64; ring_size]; 2];
1741    //     izip!(
1742    //         rlwe_ct[0].iter_mut(),
1743    //         trivial_rlwe_test_poly.0.get_row_slice(0)
1744    //     )
1745    //     .for_each(|(t, f)| {
1746    //         *t = f.to_u64().unwrap();
1747    //     });
1748    //     izip!(
1749    //         rlwe_ct[1].iter_mut(),
1750    //         trivial_rlwe_test_poly.0.get_row_slice(1)
1751    //     )
1752    //     .for_each(|(t, f)| {
1753    //         *t = f.to_u64().unwrap();
1754    //     });
1755    //     let mut m_out = vec![vec![0u64; ring_size]];
1756    //     let modop = ModularOpsU64::new(rlwe_q.to_u64().unwrap());
1757    //     let nttop = NttBackendU64::new(rlwe_q.to_u64().unwrap(), ring_size);
1758    //     decrypt_rlwe(&rlwe_ct, ck.sk_rlwe.values(), &mut m_out, &nttop, &modop);
1759
1760    //     println!("RLWE post PBS message: {:?}", m_out[0]);
1761    // });
1762
1763    // sample extract
1764    sample_extract(lwe_in, &trivial_rlwe_test_poly, pbs_info.modop_rlweq(), 0);
1765}
1766
1767fn mod_switch_odd(v: f64, from_q: f64, to_q: f64) -> usize {
1768    let odd_v = (((v * to_q) / (from_q)).floor()).to_usize().unwrap();
1769    //TODO(Jay): check correctness of this
1770    odd_v + ((odd_v & 1) ^ 1)
1771}
1772
1773// TODO(Jay): Add tests for sample extract
1774fn sample_extract<M: Matrix + MatrixMut, ModOp: ArithmeticOps<Element = M::MatElement>>(
1775    lwe_out: &mut M::R,
1776    rlwe_in: &M,
1777    mod_op: &ModOp,
1778    index: usize,
1779) where
1780    <M as Matrix>::R: RowMut,
1781    M::MatElement: Copy,
1782{
1783    let ring_size = rlwe_in.dimension().1;
1784
1785    // index..=0
1786    let to = &mut lwe_out.as_mut()[1..];
1787    let from = rlwe_in.get_row_slice(0);
1788    for i in 0..index + 1 {
1789        to[i] = from[index - i];
1790    }
1791
1792    // -(N..index)
1793    for i in index + 1..ring_size {
1794        to[i] = mod_op.neg(&from[ring_size + index - i]);
1795    }
1796
1797    // set b
1798    lwe_out.as_mut()[0] = *rlwe_in.get(1, index);
1799}
1800
1801/// TODO(Jay): Write tests for monomial mul
1802fn monomial_mul<El, ModOp: ArithmeticOps<Element = El>>(
1803    p_in: &[El],
1804    p_out: &mut [El],
1805    mon_exp: usize,
1806    mon_sign: bool,
1807    ring_size: usize,
1808    mod_op: &ModOp,
1809) where
1810    El: Copy,
1811{
1812    debug_assert!(p_in.as_ref().len() == ring_size);
1813    debug_assert!(p_in.as_ref().len() == p_out.as_ref().len());
1814    debug_assert!(mon_exp < ring_size);
1815
1816    p_in.as_ref().iter().enumerate().for_each(|(index, v)| {
1817        let mut to_index = index + mon_exp;
1818        let mut to_sign = mon_sign;
1819        if to_index >= ring_size {
1820            to_index = to_index - ring_size;
1821            to_sign = !to_sign;
1822        }
1823
1824        if !to_sign {
1825            p_out.as_mut()[to_index] = mod_op.neg(v);
1826        } else {
1827            p_out.as_mut()[to_index] = *v;
1828        }
1829    });
1830}
1831
1832thread_local! {
1833    static PBS_TRACER: RefCell<PBSTracer<Vec<Vec<u64>>>> =
1834RefCell::new(PBSTracer::default()); }
1835
1836#[derive(Default)]
1837struct PBSTracer<M>
1838where
1839    M: Matrix + Default,
1840{
1841    pub(crate) ct_rlwe_q_mod: M::R,
1842    pub(crate) ct_lwe_q_mod: M::R,
1843    pub(crate) ct_lwe_q_mod_after_ksk: M::R,
1844    pub(crate) ct_br_q_mod: Vec<u64>,
1845}
1846
1847impl PBSTracer<Vec<Vec<u64>>> {
1848    fn trace(&self, parameters: &BoolParameters<u64>, sk_lwe: &[i32], sk_rlwe: &[i32]) {
1849        assert!(parameters.rlwe_n().0 == sk_rlwe.len());
1850        assert!(parameters.lwe_n().0 == sk_lwe.len());
1851
1852        let modop_rlweq = ModularOpsU64::new(*parameters.rlwe_q());
1853        // noise after mod down Q -> Q_ks
1854        let m_back0 = decrypt_lwe(&self.ct_rlwe_q_mod, sk_rlwe, &modop_rlweq);
1855
1856        let modop_lweq = ModularOpsU64::<CiphertextModulus<u64>>::new(*parameters.lwe_q());
1857        // noise after mod down Q -> Q_ks
1858        let m_back1 = decrypt_lwe(&self.ct_lwe_q_mod, sk_rlwe, &modop_lweq);
1859        // noise after key switch from RLWE -> LWE
1860        let m_back2 = decrypt_lwe(&self.ct_lwe_q_mod_after_ksk, sk_lwe, &modop_lweq);
1861
1862        // noise after mod down odd from Q_ks -> q
1863        let modop_br_q = ModularOpsU64::<u64>::new(*parameters.br_q() as u64);
1864        let m_back3 = decrypt_lwe(&self.ct_br_q_mod, sk_lwe, &modop_br_q);
1865
1866        println!(
1867            "
1868            M initial mod Q: {m_back0},
1869            M after mod down Q -> Q_ks: {m_back1},
1870            M after key switch from RLWE -> LWE: {m_back2},
1871            M after mod dwon Q_ks -> q: {m_back3}
1872        "
1873        );
1874    }
1875}
1876
1877impl WithLocal for PBSTracer<Vec<Vec<u64>>> {
1878    fn with_local<F, R>(func: F) -> R
1879    where
1880        F: Fn(&Self) -> R,
1881    {
1882        PBS_TRACER.with_borrow(|t| func(t))
1883    }
1884
1885    fn with_local_mut<F, R>(func: F) -> R
1886    where
1887        F: Fn(&mut Self) -> R,
1888    {
1889        PBS_TRACER.with_borrow_mut(|t| func(t))
1890    }
1891}
1892
1893#[cfg(test)]
1894mod tests {
1895    use rand::{thread_rng, Rng};
1896    use rand_distr::Uniform;
1897
1898    use crate::{
1899        backend::{GetModulus, ModInit, ModularOpsU64, WordSizeModulus},
1900        bool,
1901        ntt::NttBackendU64,
1902        random::{RandomElementInModulus, DEFAULT_RNG},
1903        rgsw::{
1904            self, measure_noise, public_key_encrypt_rlwe, secret_key_encrypt_rlwe,
1905            tests::{_measure_noise_rgsw, _sk_encrypt_rlwe},
1906            RgswCiphertext, RgswCiphertextEvaluationDomain, SeededRgswCiphertext,
1907            SeededRlweCiphertext,
1908        },
1909        utils::{negacyclic_mul, Stats},
1910    };
1911
1912    use super::*;
1913
1914    #[test]
1915    fn bool_encrypt_decrypt_works() {
1916        let bool_evaluator = BoolEvaluator::<
1917            Vec<Vec<u64>>,
1918            NttBackendU64,
1919            ModularOpsU64<CiphertextModulus<u64>>,
1920            ModularOpsU64<CiphertextModulus<u64>>,
1921        >::new(SP_BOOL_PARAMS);
1922        let client_key = bool_evaluator.client_key();
1923
1924        let mut m = true;
1925        for _ in 0..1000 {
1926            let lwe_ct = bool_evaluator.sk_encrypt(m, &client_key);
1927            let m_back = bool_evaluator.sk_decrypt(&lwe_ct, &client_key);
1928            assert_eq!(m, m_back);
1929            m = !m;
1930        }
1931    }
1932
1933    #[test]
1934    fn bool_nand() {
1935        DefaultSecureRng::with_local_mut(|r| {
1936            let rng = DefaultSecureRng::new_seeded([19u8; 32]);
1937            *r = rng;
1938        });
1939
1940        // let mog = WordSizeModulus::<CiphertextModulus<u64>>::new(12u64);
1941
1942        let mut bool_evaluator = BoolEvaluator::<
1943            Vec<Vec<u64>>,
1944            NttBackendU64,
1945            ModularOpsU64<CiphertextModulus<u64>>,
1946            ModularOpsU64<CiphertextModulus<u64>>,
1947        >::new(SP_BOOL_PARAMS);
1948
1949        // println!("{:?}", bool_evaluator.nand_test_vec);
1950        let client_key = bool_evaluator.client_key();
1951        let seeded_server_key = bool_evaluator.server_key(&client_key);
1952        let server_key_eval_domain =
1953            ServerKeyEvaluationDomain::<_, DefaultSecureRng, NttBackendU64>::from(
1954                &seeded_server_key,
1955            );
1956
1957        let mut m0 = false;
1958        let mut m1 = true;
1959        let mut ct0 = bool_evaluator.sk_encrypt(m0, &client_key);
1960        let mut ct1 = bool_evaluator.sk_encrypt(m1, &client_key);
1961
1962        for _ in 0..1000 {
1963            let ct_back = bool_evaluator.nand(&ct0, &ct1, &server_key_eval_domain);
1964
1965            let m_out = !(m0 && m1);
1966
1967            // Trace and measure PBS noise
1968            {
1969                let noise0 = {
1970                    let ideal = if m0 {
1971                        bool_evaluator.pbs_info.parameters.rlwe_q().true_el()
1972                    } else {
1973                        bool_evaluator.pbs_info.parameters.rlwe_q().false_el()
1974                    };
1975                    let n = measure_noise_lwe(
1976                        &ct0,
1977                        client_key.sk_rlwe.values(),
1978                        &bool_evaluator.pbs_info.rlwe_modop,
1979                        &ideal,
1980                    );
1981                    let v = decrypt_lwe(
1982                        &ct0,
1983                        client_key.sk_rlwe.values(),
1984                        &bool_evaluator.pbs_info.rlwe_modop,
1985                    );
1986                    (n, v)
1987                };
1988                let noise1 = {
1989                    let ideal = if m1 {
1990                        bool_evaluator.pbs_info.parameters.rlwe_q().true_el()
1991                    } else {
1992                        bool_evaluator.pbs_info.parameters.rlwe_q().false_el()
1993                    };
1994                    let n = measure_noise_lwe(
1995                        &ct1,
1996                        client_key.sk_rlwe.values(),
1997                        &bool_evaluator.pbs_info.rlwe_modop,
1998                        &ideal,
1999                    );
2000                    let v = decrypt_lwe(
2001                        &ct1,
2002                        client_key.sk_rlwe.values(),
2003                        &bool_evaluator.pbs_info.rlwe_modop,
2004                    );
2005                    (n, v)
2006                };
2007
2008                // // Trace PBS
2009                // PBSTracer::with_local(|t| {
2010                //     t.trace(
2011                //         &SP_BOOL_PARAMS,
2012                //         &client_key.sk_lwe.values(),
2013                //         client_key.sk_rlwe.values(),
2014                //     )
2015                // });
2016
2017                // Calculate noise in ciphertext post PBS
2018                let noise_out = {
2019                    let ideal = if m_out {
2020                        bool_evaluator.pbs_info.parameters.rlwe_q().true_el()
2021                    } else {
2022                        bool_evaluator.pbs_info.parameters.rlwe_q().false_el()
2023                    };
2024                    let n = measure_noise_lwe(
2025                        &ct_back,
2026                        client_key.sk_rlwe.values(),
2027                        &bool_evaluator.pbs_info.rlwe_modop,
2028                        &ideal,
2029                    );
2030                    let v = decrypt_lwe(
2031                        &ct_back,
2032                        client_key.sk_rlwe.values(),
2033                        &bool_evaluator.pbs_info.rlwe_modop,
2034                    );
2035                    (n, v)
2036                };
2037                dbg!(m0, m1, m_out);
2038                println!(
2039                    "ct0 (noise, message): {:?}  \n ct1 (noise, message): {:?} \n PBS (noise, message): {:?}", noise0, noise1, noise_out
2040                );
2041            }
2042            let m_back = bool_evaluator.sk_decrypt(&ct_back, &client_key);
2043            assert!(m_out == m_back, "Expected {m_out}, got {m_back}");
2044            println!("----------");
2045
2046            m1 = m0;
2047            m0 = m_out;
2048
2049            ct1 = ct0;
2050            ct0 = ct_back;
2051        }
2052    }
2053
2054    #[test]
2055    fn multi_party_encryption_decryption() {
2056        let bool_evaluator = BoolEvaluator::<
2057            Vec<Vec<u64>>,
2058            NttBackendU64,
2059            ModularOpsU64<CiphertextModulus<u64>>,
2060            ModularOpsU64<CiphertextModulus<u64>>,
2061        >::new(MP_BOOL_PARAMS);
2062
2063        let no_of_parties = 500;
2064        let parties = (0..no_of_parties)
2065            .map(|_| bool_evaluator.client_key())
2066            .collect_vec();
2067
2068        let mut ideal_rlwe_sk = vec![0i32; bool_evaluator.pbs_info.rlwe_n()];
2069        parties.iter().for_each(|k| {
2070            izip!(ideal_rlwe_sk.iter_mut(), k.sk_rlwe.values()).for_each(|(ideal_i, s_i)| {
2071                *ideal_i = *ideal_i + s_i;
2072            });
2073        });
2074
2075        println!("{:?}", &ideal_rlwe_sk);
2076
2077        let mut m = true;
2078        for i in 0..100 {
2079            let pk_cr_seed = [0u8; 32];
2080
2081            let public_key_share = parties
2082                .iter()
2083                .map(|k| bool_evaluator.multi_party_public_key_share(pk_cr_seed, k))
2084                .collect_vec();
2085
2086            let collective_pk = PublicKey::<
2087                Vec<Vec<u64>>,
2088                DefaultSecureRng,
2089                ModularOpsU64<CiphertextModulus<u64>>,
2090            >::from(public_key_share.as_slice());
2091            let lwe_ct = bool_evaluator.pk_encrypt(&collective_pk.key, m);
2092
2093            let decryption_shares = parties
2094                .iter()
2095                .map(|k| bool_evaluator.multi_party_decryption_share(&lwe_ct, k))
2096                .collect_vec();
2097
2098            let m_back = bool_evaluator.multi_party_decrypt(&decryption_shares, &lwe_ct);
2099
2100            {
2101                let ideal_m = if m {
2102                    bool_evaluator.pbs_info.parameters.rlwe_q().true_el()
2103                } else {
2104                    bool_evaluator.pbs_info.parameters.rlwe_q().false_el()
2105                };
2106                let noise = measure_noise_lwe(
2107                    &lwe_ct,
2108                    &ideal_rlwe_sk,
2109                    &bool_evaluator.pbs_info.rlwe_modop,
2110                    &ideal_m,
2111                );
2112                println!("Noise: {noise}");
2113            }
2114
2115            assert_eq!(m_back, m);
2116            m = !m;
2117        }
2118    }
2119
2120    fn _collecitve_public_key_gen(rlwe_q: u64, parties_rlwe_sk: &[RlweSecret]) -> Vec<Vec<u64>> {
2121        let ring_size = parties_rlwe_sk[0].values.len();
2122        assert!(ring_size.is_power_of_two());
2123        let mut rng = DefaultSecureRng::new();
2124        let nttop = NttBackendU64::new(&rlwe_q, ring_size);
2125        let modop = ModularOpsU64::new(rlwe_q);
2126
2127        // Generate Pk shares
2128        let pk_seed = [0u8; 32];
2129        let pk_shares = parties_rlwe_sk.iter().map(|sk| {
2130            let mut p_rng = DefaultSecureRng::new_seeded(pk_seed);
2131            let mut share_out = vec![0u64; ring_size];
2132            public_key_share(
2133                &mut share_out,
2134                sk.values(),
2135                &modop,
2136                &nttop,
2137                &mut p_rng,
2138                &mut rng,
2139            );
2140            share_out
2141        });
2142
2143        let mut pk_part_b = vec![0u64; ring_size];
2144        pk_shares.for_each(|share| modop.elwise_add_mut(&mut pk_part_b, &share));
2145        let mut pk_part_a = vec![0u64; ring_size];
2146        let mut p_rng = DefaultSecureRng::new_seeded(pk_seed);
2147        RandomFillUniformInModulus::random_fill(&mut p_rng, &rlwe_q, pk_part_a.as_mut_slice());
2148
2149        vec![pk_part_a, pk_part_b]
2150    }
2151
2152    fn _multi_party_all_keygen(
2153        bool_evaluator: &BoolEvaluator<
2154            Vec<Vec<u64>>,
2155            NttBackendU64,
2156            ModularOpsU64<CiphertextModulus<u64>>,
2157            ModularOpsU64<CiphertextModulus<u64>>,
2158        >,
2159        no_of_parties: usize,
2160    ) -> (
2161        Vec<ClientKey>,
2162        PublicKey<Vec<Vec<u64>>, DefaultSecureRng, ModularOpsU64<CiphertextModulus<u64>>>,
2163        Vec<
2164            CommonReferenceSeededMultiPartyServerKeyShare<
2165                Vec<Vec<u64>>,
2166                BoolParameters<u64>,
2167                [u8; 32],
2168            >,
2169        >,
2170        SeededMultiPartyServerKey<Vec<Vec<u64>>, [u8; 32], BoolParameters<u64>>,
2171        ServerKeyEvaluationDomain<Vec<Vec<u64>>, DefaultSecureRng, NttBackendU64>,
2172        ClientKey,
2173    ) {
2174        let parties = (0..no_of_parties)
2175            .map(|_| bool_evaluator.client_key())
2176            .collect_vec();
2177
2178        let mut rng = DefaultSecureRng::new();
2179
2180        // Collective public key
2181        let mut pk_cr_seed = [0u8; 32];
2182        rng.fill_bytes(&mut pk_cr_seed);
2183        let public_key_share = parties
2184            .iter()
2185            .map(|k| bool_evaluator.multi_party_public_key_share(pk_cr_seed, k))
2186            .collect_vec();
2187        let collective_pk =
2188            PublicKey::<Vec<Vec<u64>>, DefaultSecureRng, _>::from(public_key_share.as_slice());
2189
2190        // Server key
2191        let mut pbs_cr_seed = [0u8; 32];
2192        rng.fill_bytes(&mut pbs_cr_seed);
2193        let server_key_shares = parties
2194            .iter()
2195            .map(|k| {
2196                bool_evaluator.multi_party_server_key_share(pbs_cr_seed, &collective_pk.key, k)
2197            })
2198            .collect_vec();
2199        let seeded_server_key =
2200            bool_evaluator.aggregate_multi_party_server_key_shares(&server_key_shares);
2201        let server_key_eval = ServerKeyEvaluationDomain::<_, DefaultSecureRng, NttBackendU64>::from(
2202            &seeded_server_key,
2203        );
2204
2205        // construct ideal rlwe sk for meauring noise
2206        let ideal_client_key = {
2207            let mut ideal_rlwe_sk = vec![0i32; bool_evaluator.pbs_info.rlwe_n()];
2208            parties.iter().for_each(|k| {
2209                izip!(ideal_rlwe_sk.iter_mut(), k.sk_rlwe.values()).for_each(|(ideal_i, s_i)| {
2210                    *ideal_i = *ideal_i + s_i;
2211                });
2212            });
2213            let mut ideal_lwe_sk = vec![0i32; bool_evaluator.pbs_info.lwe_n()];
2214            parties.iter().for_each(|k| {
2215                izip!(ideal_lwe_sk.iter_mut(), k.sk_lwe.values()).for_each(|(ideal_i, s_i)| {
2216                    *ideal_i = *ideal_i + s_i;
2217                });
2218            });
2219
2220            ClientKey {
2221                sk_lwe: LweSecret {
2222                    values: ideal_lwe_sk,
2223                },
2224                sk_rlwe: RlweSecret {
2225                    values: ideal_rlwe_sk,
2226                },
2227            }
2228        };
2229
2230        (
2231            parties,
2232            collective_pk,
2233            server_key_shares,
2234            seeded_server_key,
2235            server_key_eval,
2236            ideal_client_key,
2237        )
2238    }
2239
2240    #[test]
2241    fn multi_party_nand() {
2242        let mut bool_evaluator = BoolEvaluator::<
2243            Vec<Vec<u64>>,
2244            NttBackendU64,
2245            ModularOpsU64<CiphertextModulus<u64>>,
2246            ModularOpsU64<CiphertextModulus<u64>>,
2247        >::new(MP_BOOL_PARAMS);
2248
2249        let (parties, collective_pk, _, _, server_key_eval, ideal_client_key) =
2250            _multi_party_all_keygen(&bool_evaluator, 8);
2251
2252        let mut m0 = true;
2253        let mut m1 = false;
2254
2255        let mut lwe0 = bool_evaluator.pk_encrypt(&collective_pk.key, m0);
2256        let mut lwe1 = bool_evaluator.pk_encrypt(&collective_pk.key, m1);
2257
2258        for _ in 0..2000 {
2259            let lwe_out = bool_evaluator.nand(&lwe0, &lwe1, &server_key_eval);
2260
2261            let m_expected = !(m0 & m1);
2262
2263            // measure noise
2264            {
2265                let noise0 = {
2266                    let ideal = if m0 {
2267                        bool_evaluator.pbs_info.rlwe_q().true_el()
2268                    } else {
2269                        bool_evaluator.pbs_info.rlwe_q().false_el()
2270                    };
2271                    let n = measure_noise_lwe(
2272                        &lwe0,
2273                        ideal_client_key.sk_rlwe.values(),
2274                        &bool_evaluator.pbs_info.rlwe_modop,
2275                        &ideal,
2276                    );
2277                    let v = decrypt_lwe(
2278                        &lwe0,
2279                        ideal_client_key.sk_rlwe.values(),
2280                        &bool_evaluator.pbs_info.rlwe_modop,
2281                    );
2282                    (n, v)
2283                };
2284                let noise1 = {
2285                    let ideal = if m1 {
2286                        bool_evaluator.pbs_info.rlwe_q().true_el()
2287                    } else {
2288                        bool_evaluator.pbs_info.rlwe_q().false_el()
2289                    };
2290                    let n = measure_noise_lwe(
2291                        &lwe1,
2292                        ideal_client_key.sk_rlwe.values(),
2293                        &bool_evaluator.pbs_info.rlwe_modop,
2294                        &ideal,
2295                    );
2296                    let v = decrypt_lwe(
2297                        &lwe1,
2298                        ideal_client_key.sk_rlwe.values(),
2299                        &bool_evaluator.pbs_info.rlwe_modop,
2300                    );
2301                    (n, v)
2302                };
2303
2304                // // Trace PBS
2305                // PBSTracer::with_local(|t| {
2306                //     t.trace(
2307                //         &MP_BOOL_PARAMS,
2308                //         &ideal_client_key.sk_lwe.values(),
2309                //         &ideal_client_key.sk_rlwe.values(),
2310                //     )
2311                // });
2312
2313                let noise_out = {
2314                    let ideal_m = if m_expected {
2315                        bool_evaluator.pbs_info.rlwe_q().true_el()
2316                    } else {
2317                        bool_evaluator.pbs_info.rlwe_q().false_el()
2318                    };
2319                    let n = measure_noise_lwe(
2320                        &lwe_out,
2321                        ideal_client_key.sk_rlwe.values(),
2322                        &bool_evaluator.pbs_info.rlwe_modop,
2323                        &ideal_m,
2324                    );
2325                    let v = decrypt_lwe(
2326                        &lwe_out,
2327                        ideal_client_key.sk_rlwe.values(),
2328                        &bool_evaluator.pbs_info.rlwe_modop,
2329                    );
2330                    (n, v)
2331                };
2332                dbg!(m0, m1, m_expected);
2333                println!(
2334                    "ct0 (noise, message): {:?}  \n ct1 (noise, message): {:?} \n PBS (noise, message): {:?}", noise0, noise1, noise_out
2335                );
2336            }
2337
2338            // multi-party decrypt
2339            let decryption_shares = parties
2340                .iter()
2341                .map(|k| bool_evaluator.multi_party_decryption_share(&lwe_out, k))
2342                .collect_vec();
2343            let m_back = bool_evaluator.multi_party_decrypt(&decryption_shares, &lwe_out);
2344
2345            // let m_back = bool_evaluator.sk_decrypt(&lwe_out, &ideal_client_key);
2346
2347            assert!(m_expected == m_back, "Expected {m_expected}, got {m_back}");
2348            m1 = m0;
2349            m0 = m_expected;
2350
2351            lwe1 = lwe0;
2352            lwe0 = lwe_out;
2353        }
2354    }
2355
2356    #[test]
2357    fn noise_tester() {
2358        let bool_evaluator = BoolEvaluator::<
2359            Vec<Vec<u64>>,
2360            NttBackendU64,
2361            ModularOpsU64<CiphertextModulus<u64>>,
2362            ModularOpsU64<CiphertextModulus<u64>>,
2363        >::new(MP_BOOL_PARAMS);
2364
2365        // let (_, collective_pk, _, _, server_key_eval, ideal_client_key) =
2366        //     _multi_party_all_keygen(&bool_evaluator, 20);
2367        let no_of_parties = 32;
2368        let lwe_q = bool_evaluator.pbs_info.parameters.lwe_q();
2369        let rlwe_q = bool_evaluator.pbs_info.parameters.rlwe_q();
2370        let lwe_n = bool_evaluator.pbs_info.parameters.lwe_n().0;
2371        let rlwe_n = bool_evaluator.pbs_info.parameters.rlwe_n().0;
2372        let lwe_modop = &bool_evaluator.pbs_info.lwe_modop;
2373        let rlwe_nttop = &bool_evaluator.pbs_info.rlwe_nttop;
2374        let rlwe_modop = &bool_evaluator.pbs_info.rlwe_modop;
2375
2376        // let rgsw_rgsw_decomposer = &bool_evaluator
2377        //     .pbs_info
2378        //     .parameters
2379        //     .rgsw_rgsw_decomposer::<DefaultDecomposer<u64>>();
2380        // let rgsw_rgsw_gadget_a = rgsw_rgsw_decomposer.0.gadget_vector();
2381        // let rgsw_rgsw_gadget_b = rgsw_rgsw_decomposer.1.gadget_vector();
2382
2383        let rlwe_rgsw_decomposer = &bool_evaluator.pbs_info.rlwe_rgsw_decomposer;
2384        let rlwe_rgsw_gadget_a = rlwe_rgsw_decomposer.0.gadget_vector();
2385        let rlwe_rgsw_gadget_b = rlwe_rgsw_decomposer.1.gadget_vector();
2386
2387        let auto_decomposer = &bool_evaluator.pbs_info.auto_decomposer;
2388        let auto_gadget = auto_decomposer.gadget_vector();
2389
2390        let parties = (0..no_of_parties)
2391            .map(|_| bool_evaluator.client_key())
2392            .collect_vec();
2393
2394        let ideal_client_key = {
2395            let mut ideal_rlwe_sk = vec![0i32; bool_evaluator.pbs_info.rlwe_n()];
2396            parties.iter().for_each(|k| {
2397                izip!(ideal_rlwe_sk.iter_mut(), k.sk_rlwe.values()).for_each(|(ideal_i, s_i)| {
2398                    *ideal_i = *ideal_i + s_i;
2399                });
2400            });
2401            let mut ideal_lwe_sk = vec![0i32; bool_evaluator.pbs_info.lwe_n()];
2402            parties.iter().for_each(|k| {
2403                izip!(ideal_lwe_sk.iter_mut(), k.sk_lwe.values()).for_each(|(ideal_i, s_i)| {
2404                    *ideal_i = *ideal_i + s_i;
2405                });
2406            });
2407
2408            ClientKey {
2409                sk_lwe: LweSecret {
2410                    values: ideal_lwe_sk,
2411                },
2412                sk_rlwe: RlweSecret {
2413                    values: ideal_rlwe_sk,
2414                },
2415            }
2416        };
2417
2418        // check noise in freshly encrypted RLWE ciphertext (ie var_fresh)
2419        if true {
2420            let mut rng = DefaultSecureRng::new();
2421            let mut check = Stats { samples: vec![] };
2422            for _ in 0..10 {
2423                // generate a new collective public key
2424                let mut pk_cr_seed = [0u8; 32];
2425                rng.fill_bytes(&mut pk_cr_seed);
2426                let public_key_share = parties
2427                    .iter()
2428                    .map(|k| bool_evaluator.multi_party_public_key_share(pk_cr_seed, k))
2429                    .collect_vec();
2430                let collective_pk = PublicKey::<
2431                    Vec<Vec<u64>>,
2432                    DefaultSecureRng,
2433                    ModularOpsU64<CiphertextModulus<u64>>,
2434                >::from(public_key_share.as_slice());
2435
2436                let mut m = vec![0u64; rlwe_n];
2437                RandomFillUniformInModulus::random_fill(&mut rng, rlwe_q, m.as_mut_slice());
2438                let mut rlwe_ct = vec![vec![0u64; rlwe_n]; 2];
2439                public_key_encrypt_rlwe::<_, _, _, _, i32, _>(
2440                    &mut rlwe_ct,
2441                    &collective_pk.key,
2442                    &m,
2443                    rlwe_modop,
2444                    rlwe_nttop,
2445                    &mut rng,
2446                );
2447
2448                let mut m_back = vec![0u64; rlwe_n];
2449                decrypt_rlwe(
2450                    &rlwe_ct,
2451                    ideal_client_key.sk_rlwe.values(),
2452                    &mut m_back,
2453                    rlwe_nttop,
2454                    rlwe_modop,
2455                );
2456
2457                rlwe_modop.elwise_sub_mut(m_back.as_mut_slice(), m.as_slice());
2458
2459                check.add_more(Vec::<i64>::try_convert_from(&m_back, rlwe_q).as_slice());
2460            }
2461
2462            println!("Public key Std: {}", check.std_dev().abs().log2());
2463        }
2464
2465        if true {
2466            // Generate server key shares
2467            let mut rng = DefaultSecureRng::new();
2468            let mut pk_cr_seed = [0u8; 32];
2469            rng.fill_bytes(&mut pk_cr_seed);
2470            let public_key_share = parties
2471                .iter()
2472                .map(|k| bool_evaluator.multi_party_public_key_share(pk_cr_seed, k))
2473                .collect_vec();
2474            let collective_pk = PublicKey::<
2475                Vec<Vec<u64>>,
2476                DefaultSecureRng,
2477                ModularOpsU64<CiphertextModulus<u64>>,
2478            >::from(public_key_share.as_slice());
2479
2480            let pbs_cr_seed = [0u8; 32];
2481            rng.fill_bytes(&mut pk_cr_seed);
2482            let server_key_shares = parties
2483                .iter()
2484                .map(|k| {
2485                    bool_evaluator.multi_party_server_key_share(pbs_cr_seed, &collective_pk.key, k)
2486                })
2487                .collect_vec();
2488
2489            let seeded_server_key =
2490                bool_evaluator.aggregate_multi_party_server_key_shares(&server_key_shares);
2491
2492            // Check noise in RGSW ciphertexts of ideal LWE secret elements
2493            if true {
2494                let mut check = Stats { samples: vec![] };
2495                izip!(
2496                    ideal_client_key.sk_lwe.values.iter(),
2497                    seeded_server_key.rgsw_cts.iter()
2498                )
2499                .for_each(|(s_i, rgsw_ct_i)| {
2500                    // X^{s[i]}
2501                    let mut m_si = vec![0u64; rlwe_n];
2502                    let s_i = *s_i * (bool_evaluator.pbs_info.embedding_factor as i32);
2503                    if s_i < 0 {
2504                        m_si[rlwe_n - (s_i.abs() as usize)] = rlwe_q.neg_one();
2505                    } else {
2506                        m_si[s_i as usize] = 1;
2507                    }
2508
2509                    // RLWE'(-sm)
2510                    let mut neg_s_eval =
2511                        Vec::<u64>::try_convert_from(ideal_client_key.sk_rlwe.values(), rlwe_q);
2512                    rlwe_modop.elwise_neg_mut(&mut neg_s_eval);
2513                    rlwe_nttop.forward(&mut neg_s_eval);
2514                    for j in 0..rlwe_rgsw_decomposer.a().decomposition_count() {
2515                        // RLWE(B^{j} * -s[X]*X^{s_lwe[i]})
2516
2517                        // -s[X]*X^{s_lwe[i]}*B_j
2518                        let mut m_ideal = m_si.clone();
2519                        rlwe_nttop.forward(m_ideal.as_mut_slice());
2520                        rlwe_modop.elwise_mul_mut(m_ideal.as_mut_slice(), neg_s_eval.as_slice());
2521                        rlwe_nttop.backward(m_ideal.as_mut_slice());
2522                        rlwe_modop
2523                            .elwise_scalar_mul_mut(m_ideal.as_mut_slice(), &rlwe_rgsw_gadget_a[j]);
2524
2525                        // RLWE(-s*X^{s_lwe[i]}*B_j)
2526                        let mut rlwe_ct = vec![vec![0u64; rlwe_n]; 2];
2527                        rlwe_ct[0].copy_from_slice(&rgsw_ct_i[j]);
2528                        rlwe_ct[1].copy_from_slice(
2529                            &rgsw_ct_i[j + rlwe_rgsw_decomposer.a().decomposition_count()],
2530                        );
2531
2532                        let mut m_back = vec![0u64; rlwe_n];
2533                        decrypt_rlwe(
2534                            &rlwe_ct,
2535                            ideal_client_key.sk_rlwe.values(),
2536                            &mut m_back,
2537                            rlwe_nttop,
2538                            rlwe_modop,
2539                        );
2540
2541                        // diff
2542                        rlwe_modop.elwise_sub_mut(&mut m_back, &m_ideal);
2543                        check.add_more(&Vec::<i64>::try_convert_from(&m_back, rlwe_q));
2544                    }
2545
2546                    // RLWE'(m)
2547                    for j in 0..rlwe_rgsw_decomposer.b().decomposition_count() {
2548                        // RLWE(B^{j} * X^{s_lwe[i]})
2549
2550                        // X^{s_lwe[i]}*B_j
2551                        let mut m_ideal = m_si.clone();
2552                        rlwe_modop
2553                            .elwise_scalar_mul_mut(m_ideal.as_mut_slice(), &rlwe_rgsw_gadget_b[j]);
2554
2555                        // RLWE(X^{s_lwe[i]}*B_j)
2556                        let mut rlwe_ct = vec![vec![0u64; rlwe_n]; 2];
2557                        rlwe_ct[0].copy_from_slice(
2558                            &rgsw_ct_i[j + (2 * rlwe_rgsw_decomposer.a().decomposition_count())],
2559                        );
2560                        rlwe_ct[1].copy_from_slice(
2561                            &rgsw_ct_i[j
2562                                + (2 * rlwe_rgsw_decomposer.a().decomposition_count()
2563                                    + rlwe_rgsw_decomposer.b().decomposition_count())],
2564                        );
2565
2566                        let mut m_back = vec![0u64; rlwe_n];
2567                        decrypt_rlwe(
2568                            &rlwe_ct,
2569                            ideal_client_key.sk_rlwe.values(),
2570                            &mut m_back,
2571                            rlwe_nttop,
2572                            rlwe_modop,
2573                        );
2574
2575                        // diff
2576                        rlwe_modop.elwise_sub_mut(&mut m_back, &m_ideal);
2577                        check.add_more(&Vec::<i64>::try_convert_from(&m_back, rlwe_q));
2578                    }
2579                });
2580                println!(
2581                    "RGSW Std: {} {} ;; max={}",
2582                    check.mean(),
2583                    check.std_dev().abs().log2(),
2584                    check.samples.iter().max().unwrap()
2585                );
2586            }
2587
2588            // server key in Evaluation domain
2589            let server_key_eval_domain =
2590                ServerKeyEvaluationDomain::<_, DefaultSecureRng, NttBackendU64>::from(
2591                    &seeded_server_key,
2592                );
2593
2594            // check noise in RLWE x RGSW(X^{s_i}) where RGSW is accunulated RGSW ciphertext
2595            if true {
2596                let mut check = Stats { samples: vec![] };
2597
2598                izip!(
2599                    ideal_client_key.sk_lwe.values(),
2600                    server_key_eval_domain.rgsw_cts.iter()
2601                )
2602                .for_each(|(s_i, rgsw_ct_i)| {
2603                    let mut m = vec![0u64; rlwe_n];
2604                    RandomFillUniformInModulus::random_fill(&mut rng, rlwe_q, m.as_mut_slice());
2605                    let mut rlwe_ct = vec![vec![0u64; rlwe_n]; 2];
2606                    public_key_encrypt_rlwe::<_, _, _, _, i32, _>(
2607                        &mut rlwe_ct,
2608                        &collective_pk.key,
2609                        &m,
2610                        rlwe_modop,
2611                        rlwe_nttop,
2612                        &mut rng,
2613                    );
2614
2615                    // RLWE(m*X^{s[i]}) = RLWE(m) x RGSW(X^{s[i]})
2616                    // let mut rlwe_after = RlweCiphertext::<_, DefaultSecureRng>::new_trivial(vec![
2617                    //     vec![0u64; rlwe_n],
2618                    //     m.clone(),
2619                    // ]);
2620                    let mut rlwe_after = RlweCiphertext::<_, DefaultSecureRng> {
2621                        data: rlwe_ct.clone(),
2622                        is_trivial: false,
2623                        _phatom: PhantomData,
2624                    };
2625                    let mut scratch = vec![
2626                        vec![0u64; rlwe_n];
2627                        std::cmp::max(
2628                            rlwe_rgsw_decomposer.0.decomposition_count(),
2629                            rlwe_rgsw_decomposer.1.decomposition_count()
2630                        ) + 2
2631                    ];
2632                    rlwe_by_rgsw(
2633                        &mut rlwe_after,
2634                        rgsw_ct_i,
2635                        &mut scratch,
2636                        rlwe_rgsw_decomposer,
2637                        rlwe_nttop,
2638                        rlwe_modop,
2639                    );
2640
2641                    // m1 = X^{s[i]}
2642                    let mut m1 = vec![0u64; rlwe_n];
2643                    let s_i = *s_i * (bool_evaluator.pbs_info.embedding_factor as i32);
2644                    if s_i < 0 {
2645                        m1[rlwe_n - (s_i.abs() as usize)] = rlwe_q.neg_one()
2646                    } else {
2647                        m1[s_i as usize] = 1;
2648                    }
2649
2650                    // (m+e) * m1
2651                    let mut m_plus_e_times_m1 = vec![0u64; rlwe_n];
2652                    decrypt_rlwe(
2653                        &rlwe_ct,
2654                        ideal_client_key.sk_rlwe.values(),
2655                        &mut m_plus_e_times_m1,
2656                        rlwe_nttop,
2657                        rlwe_modop,
2658                    );
2659                    rlwe_nttop.forward(m_plus_e_times_m1.as_mut_slice());
2660                    rlwe_nttop.forward(m1.as_mut_slice());
2661                    rlwe_modop.elwise_mul_mut(m_plus_e_times_m1.as_mut_slice(), m1.as_slice());
2662                    rlwe_nttop.backward(m_plus_e_times_m1.as_mut_slice());
2663
2664                    // Resulting RLWE ciphertext will equal: (m0m1 + em1) + e_{rlsw x rgsw}.
2665                    // Hence, resulting rlwe ciphertext will have error em1 + e_{rlwe x rgsw}.
2666                    // Here we're only concerned with e_{rlwe x rgsw}, that is noise added by
2667                    // RLWExRGSW. Also note in practice m1 is a monomial, for ex, X^{s_{i}}, for
2668                    // some i and var(em1) = var(e).
2669                    let mut m_plus_e_times_m1_more_e = vec![0u64; rlwe_n];
2670                    decrypt_rlwe(
2671                        &rlwe_after,
2672                        ideal_client_key.sk_rlwe.values(),
2673                        &mut m_plus_e_times_m1_more_e,
2674                        rlwe_nttop,
2675                        rlwe_modop,
2676                    );
2677
2678                    // diff
2679                    rlwe_modop.elwise_sub_mut(
2680                        m_plus_e_times_m1_more_e.as_mut_slice(),
2681                        m_plus_e_times_m1.as_slice(),
2682                    );
2683
2684                    // let noise = measure_noise(
2685                    //     &rlwe_after,
2686                    //     &m_plus_e_times_m1,
2687                    //     rlwe_nttop,
2688                    //     rlwe_modop,
2689                    //     ideal_client_key.sk_rlwe.values(),
2690                    // );
2691                    // print!("NOISE: {}", noise);
2692
2693                    check.add_more(&Vec::<i64>::try_convert_from(
2694                        &m_plus_e_times_m1_more_e,
2695                        rlwe_q,
2696                    ));
2697                });
2698                println!(
2699                    "RLWE x RGSW, where RGSW has noise var_brk, std: {} {}",
2700                    check.std_dev(),
2701                    check.std_dev().abs().log2()
2702                )
2703            }
2704
2705            // check noise in Auto key
2706            if true {
2707                let mut check = Stats { samples: vec![] };
2708
2709                let mut neg_s_poly =
2710                    Vec::<u64>::try_convert_from(ideal_client_key.sk_rlwe.values(), rlwe_q);
2711                rlwe_modop.elwise_neg_mut(neg_s_poly.as_mut_slice());
2712
2713                let g = bool_evaluator.pbs_info.g();
2714                for i in [-g, g] {
2715                    // -s[X^k]
2716                    let (auto_indices, auto_sign) = generate_auto_map(rlwe_n, i);
2717                    let mut neg_s_poly_auto_i = vec![0u64; rlwe_n];
2718                    izip!(neg_s_poly.iter(), auto_indices.iter(), auto_sign.iter()).for_each(
2719                        |(v, to_i, to_sign)| {
2720                            if !to_sign {
2721                                neg_s_poly_auto_i[*to_i] = rlwe_modop.neg(v);
2722                            } else {
2723                                neg_s_poly_auto_i[*to_i] = *v;
2724                            }
2725                        },
2726                    );
2727
2728                    let mut auto_key_i = server_key_eval_domain.galois_key_for_auto(i).clone();
2729                    // send i^th auto key to coefficient domain
2730                    auto_key_i
2731                        .iter_mut()
2732                        .for_each(|r| rlwe_nttop.backward(r.as_mut_slice()));
2733                    auto_gadget.iter().enumerate().for_each(|(i, b_i)| {
2734                        // B^i * -s[X^k]
2735                        let mut m_ideal = neg_s_poly_auto_i.clone();
2736                        rlwe_modop.elwise_scalar_mul_mut(m_ideal.as_mut_slice(), b_i);
2737
2738                        let mut m_out = vec![0u64; rlwe_n];
2739                        let mut rlwe_ct = vec![vec![0u64; rlwe_n]; 2];
2740                        rlwe_ct[0].copy_from_slice(&auto_key_i[i]);
2741                        rlwe_ct[1].copy_from_slice(
2742                            &auto_key_i[auto_decomposer.decomposition_count() + i],
2743                        );
2744                        decrypt_rlwe(
2745                            &rlwe_ct,
2746                            ideal_client_key.sk_rlwe.values(),
2747                            &mut m_out,
2748                            rlwe_nttop,
2749                            rlwe_modop,
2750                        );
2751
2752                        // diff
2753                        rlwe_modop.elwise_sub_mut(m_out.as_mut_slice(), m_ideal.as_slice());
2754
2755                        check.add_more(&Vec::<i64>::try_convert_from(&m_out, rlwe_q));
2756                    });
2757                }
2758
2759                println!("Auto key noise std dev: {}", check.std_dev().abs().log2());
2760            }
2761
2762            // check noise in RLWE(X^k) after sending RLWE(X) -> RLWE(X^k) using collective
2763            // auto key
2764            if true {
2765                let mut check = Stats { samples: vec![] };
2766
2767                let g = bool_evaluator.pbs_info.g();
2768                for i in [-g, g] {
2769                    for _ in 0..10 {
2770                        let mut m = vec![0u64; rlwe_n];
2771                        RandomFillUniformInModulus::random_fill(&mut rng, rlwe_q, m.as_mut_slice());
2772                        let mut rlwe_ct = RlweCiphertext::<_, DefaultSecureRng> {
2773                            data: vec![vec![0u64; rlwe_n]; 2],
2774                            is_trivial: false,
2775                            _phatom: PhantomData,
2776                        };
2777                        public_key_encrypt_rlwe::<_, _, _, _, i32, _>(
2778                            &mut rlwe_ct,
2779                            &collective_pk.key,
2780                            &m,
2781                            rlwe_modop,
2782                            rlwe_nttop,
2783                            &mut rng,
2784                        );
2785
2786                        // We're only interested in noise increased as a result of automorphism.
2787                        // Hence, we take m+e as the bench.
2788                        let mut m_plus_e = vec![0u64; rlwe_n];
2789                        decrypt_rlwe(
2790                            &rlwe_ct,
2791                            ideal_client_key.sk_rlwe.values(),
2792                            &mut m_plus_e,
2793                            rlwe_nttop,
2794                            rlwe_modop,
2795                        );
2796
2797                        let auto_key = server_key_eval_domain.galois_key_for_auto(i);
2798                        let (auto_map_index, auto_map_sign) = generate_auto_map(rlwe_n, i);
2799                        let mut scratch =
2800                            vec![vec![0u64; rlwe_n]; auto_decomposer.decomposition_count() + 2];
2801                        galois_auto(
2802                            &mut rlwe_ct,
2803                            auto_key,
2804                            &mut scratch,
2805                            &auto_map_index,
2806                            &auto_map_sign,
2807                            rlwe_modop,
2808                            rlwe_nttop,
2809                            auto_decomposer,
2810                        );
2811
2812                        // send m+e from X to X^k
2813                        let mut m_plus_e_auto = vec![0u64; rlwe_n];
2814                        izip!(m_plus_e.iter(), auto_map_index.iter(), auto_map_sign.iter())
2815                            .for_each(|(v, to_index, to_sign)| {
2816                                if !to_sign {
2817                                    m_plus_e_auto[*to_index] = rlwe_modop.neg(v);
2818                                } else {
2819                                    m_plus_e_auto[*to_index] = *v
2820                                }
2821                            });
2822
2823                        let mut m_out = vec![0u64; rlwe_n];
2824                        decrypt_rlwe(
2825                            &rlwe_ct,
2826                            ideal_client_key.sk_rlwe.values(),
2827                            &mut m_out,
2828                            rlwe_nttop,
2829                            rlwe_modop,
2830                        );
2831
2832                        // diff
2833                        rlwe_modop.elwise_sub_mut(m_out.as_mut_slice(), m_plus_e_auto.as_slice());
2834
2835                        check.add_more(&Vec::<i64>::try_convert_from(m_out.as_slice(), rlwe_q));
2836                    }
2837                }
2838
2839                println!("Rlwe Auto Noise Std: {}", check.std_dev().abs().log2());
2840            }
2841
2842            // Check noise growth in ksk
2843            // TODO check in LWE key switching keys
2844            if true {
2845                // 1. encrypt LWE ciphertext
2846                // 2. Key switching
2847                // 3.
2848                let mut check = Stats { samples: vec![] };
2849
2850                for _ in 0..1024 {
2851                    // Encrypt m \in Q_{ks} using RLWE sk
2852                    let mut lwe_in_ct = vec![0u64; rlwe_n + 1];
2853                    let m = RandomElementInModulus::random(&mut rng, &lwe_q.q().unwrap());
2854                    encrypt_lwe(
2855                        &mut lwe_in_ct,
2856                        &m,
2857                        ideal_client_key.sk_rlwe.values(),
2858                        lwe_modop,
2859                        &mut rng,
2860                    );
2861
2862                    // Key switch
2863                    let mut lwe_out = vec![0u64; lwe_n + 1];
2864                    lwe_key_switch(
2865                        &mut lwe_out,
2866                        &lwe_in_ct,
2867                        server_key_eval_domain.lwe_ksk(),
2868                        lwe_modop,
2869                        bool_evaluator.pbs_info.lwe_decomposer(),
2870                    );
2871
2872                    // We only care about noise added by LWE key switch
2873                    // m+e
2874                    let m_plus_e =
2875                        decrypt_lwe(&lwe_in_ct, ideal_client_key.sk_rlwe.values(), lwe_modop);
2876
2877                    let m_plus_e_plus_lwe_ksk_noise =
2878                        decrypt_lwe(&lwe_out, ideal_client_key.sk_lwe.values(), lwe_modop);
2879
2880                    let diff = lwe_modop.sub(&m_plus_e_plus_lwe_ksk_noise, &m_plus_e);
2881
2882                    check.add_more(&vec![lwe_q.map_element_to_i64(&diff)]);
2883                }
2884
2885                println!("Lwe ksk std dev: {}", check.std_dev().abs().log2());
2886            }
2887        }
2888
2889        // Check noise in fresh RGSW ciphertexts, ie X^{s_j[i]}, must equal noise in
2890        // fresh RLWE ciphertext
2891        if true {}
2892        // test LWE ksk from RLWE -> LWE
2893        // if false {
2894        //     let logp = 2;
2895        //     let mut rng = DefaultSecureRng::new();
2896
2897        //     let m = 1;
2898        //     let encoded_m = m << (lwe_logq - logp);
2899
2900        //     // Encrypt
2901        //     let mut lwe_ct = vec![0u64; rlwe_n + 1];
2902        //     encrypt_lwe(
2903        //         &mut lwe_ct,
2904        //         &encoded_m,
2905        //         ideal_client_key.sk_rlwe.values(),
2906        //         lwe_modop,
2907        //         &mut rng,
2908        //     );
2909
2910        //     // key switch
2911        //     let lwe_decomposer = &bool_evaluator.decomposer_lwe;
2912        //     let mut lwe_out = vec![0u64; lwe_n + 1];
2913        //     lwe_key_switch(
2914        //         &mut lwe_out,
2915        //         &lwe_ct,
2916        //         &server_key_eval.lwe_ksk,
2917        //         lwe_modop,
2918        //         lwe_decomposer,
2919        //     );
2920
2921        //     let encoded_m_back = decrypt_lwe(&lwe_out,
2922        // ideal_client_key.sk_lwe.values(), lwe_modop);     let m_back
2923        // =         ((encoded_m_back as f64 * (1 << logp) as f64) /
2924        // (lwe_q as f64)).round() as u64;     dbg!(m_back, m);
2925
2926        //     let noise = measure_noise_lwe(
2927        //         &lwe_out,
2928        //         ideal_client_key.sk_lwe.values(),
2929        //         lwe_modop,
2930        //         &encoded_m,
2931        //     );
2932
2933        //     println!("Noise: {noise}");
2934        // }
2935
2936        // Measure noise in RGSW ciphertexts of ideal LWE secrets
2937        // if true {
2938        //     let gadget_vec = gadget_vector(
2939        //         bool_evaluator.parameters.rlwe_logq,
2940        //         bool_evaluator.parameters.logb_rgsw,
2941        //         bool_evaluator.parameters.d_rgsw,
2942        //     );
2943
2944        //     for i in 0..20 {
2945        //         // measure noise in RGSW(s[i])
2946        //         let si =
2947        //             ideal_client_key.sk_lwe.values[i] *
2948        // (bool_evaluator.embedding_factor as i32);         let mut
2949        // si_poly = vec![0u64; rlwe_n];         if si < 0 {
2950        //             si_poly[rlwe_n - (si.abs() as usize)] = rlwe_q - 1;
2951        //         } else {
2952        //             si_poly[(si.abs() as usize)] = 1;
2953        //         }
2954
2955        //         let mut rgsw_si = server_key_eval.rgsw_cts[i].clone();
2956        //         rgsw_si
2957        //             .iter_mut()
2958        //             .for_each(|ri| rlwe_nttop.backward(ri.as_mut()));
2959
2960        //         println!("####### Noise in RGSW(X^s_{i}) #######");
2961        //         _measure_noise_rgsw(
2962        //             &rgsw_si,
2963        //             &si_poly,
2964        //             ideal_client_key.sk_rlwe.values(),
2965        //             &gadget_vec,
2966        //             rlwe_q,
2967        //         );
2968        //         println!("####### ##################### #######");
2969        //     }
2970        // }
2971
2972        // // measure noise grwoth in RLWExRGSW
2973        // if true {
2974        //     let mut rng = DefaultSecureRng::new();
2975        //     let mut carry_m = vec![0u64; rlwe_n];
2976        //     RandomUniformDist1::random_fill(&mut rng, &rlwe_q,
2977        // carry_m.as_mut_slice());
2978
2979        //     // RGSW(carrym)
2980        //     let trivial_rlwect = vec![vec![0u64; rlwe_n], carry_m.clone()];
2981        //     let mut rlwe_ct = RlweCiphertext::<_,
2982        // DefaultSecureRng>::from_raw(trivial_rlwect, true);
2983
2984        //     let mut scratch_matrix_dplus2_ring = vec![vec![0u64; rlwe_n];
2985        // d_rgsw + 2];     let mul_mod =
2986        //         |v0: &u64, v1: &u64| (((*v0 as u128 * *v1 as u128) % (rlwe_q as u128)) as u64);
2987
2988        //     for i in 0..bool_evaluator.parameters.lwe_n {
2989        //         rlwe_by_rgsw(
2990        //             &mut rlwe_ct,
2991        //             server_key_eval.rgsw_ct_lwe_si(i),
2992        //             &mut scratch_matrix_dplus2_ring,
2993        //             rlwe_decomposer,
2994        //             rlwe_nttop,
2995        //             rlwe_modop,
2996        //         );
2997
2998        //         // carry_m[X] * s_i[X]
2999        //         let si =
3000        //             ideal_client_key.sk_lwe.values[i] *
3001        // (bool_evaluator.embedding_factor as i32);         let mut
3002        // si_poly = vec![0u64; rlwe_n];         if si < 0 {
3003        //             si_poly[rlwe_n - (si.abs() as usize)] = rlwe_q - 1;
3004        //         } else {
3005        //             si_poly[(si.abs() as usize)] = 1;
3006        //         }
3007        //         carry_m = negacyclic_mul(&carry_m, &si_poly, mul_mod,
3008        // rlwe_q);
3009
3010        //         let noise = measure_noise(
3011        //             &rlwe_ct,
3012        //             &carry_m,
3013        //             rlwe_nttop,
3014        //             rlwe_modop,
3015        //             ideal_client_key.sk_rlwe.values(),
3016        //         );
3017        //         println!("Noise RLWE(carry_m) accumulating {i}^th secret
3018        // monomial: {noise}");     }
3019        // }
3020
3021        // // Check galois keys
3022        // if false {
3023        //     let g = bool_evaluator.g() as isize;
3024        //     let mut rng = DefaultSecureRng::new();
3025        //     let mut scratch_matrix_dplus2_ring = vec![vec![0u64; rlwe_n];
3026        // d_rgsw + 2];     for i in [g, -g] {
3027        //         let mut m = vec![0u64; rlwe_n];
3028        //         RandomUniformDist1::random_fill(&mut rng, &rlwe_q,
3029        // m.as_mut_slice());         let mut rlwe_ct = {
3030        //             let mut data = vec![vec![0u64; rlwe_n]; 2];
3031        //             public_key_encrypt_rlwe(
3032        //                 &mut data,
3033        //                 &collective_pk.key,
3034        //                 &m,
3035        //                 rlwe_modop,
3036        //                 rlwe_nttop,
3037        //                 &mut rng,
3038        //             );
3039        //             RlweCiphertext::<_, DefaultSecureRng>::from_raw(data,
3040        // false)         };
3041
3042        //         let auto_key = server_key_eval.galois_key_for_auto(i);
3043        //         let (auto_map_index, auto_map_sign) =
3044        // generate_auto_map(rlwe_n, i);         galois_auto(
3045        //             &mut rlwe_ct,
3046        //             auto_key,
3047        //             &mut scratch_matrix_dplus2_ring,
3048        //             &auto_map_index,
3049        //             &auto_map_sign,
3050        //             rlwe_modop,
3051        //             rlwe_nttop,
3052        //             rlwe_decomposer,
3053        //         );
3054
3055        //         // send m(X) -> m(X^i)
3056        //         let mut m_k = vec![0u64; rlwe_n];
3057        //         izip!(m.iter(), auto_map_index.iter(),
3058        // auto_map_sign.iter()).for_each(             |(mi, to_index, to_sign)|
3059        // {                 if !to_sign {
3060        //                     m_k[*to_index] = rlwe_q - *mi;
3061        //                 } else {
3062        //                     m_k[*to_index] = *mi;
3063        //                 }
3064        //             },
3065        //         );
3066
3067        //         // measure noise
3068        //         let noise = measure_noise(
3069        //             &rlwe_ct,
3070        //             &m_k,
3071        //             rlwe_nttop,
3072        //             rlwe_modop,
3073        //             ideal_client_key.sk_rlwe.values(),
3074        //         );
3075
3076        //         println!("Noise after auto k={i}: {noise}");
3077        //     }
3078        // }
3079    }
3080}