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};
   7
   8use itertools::{izip, partition, Itertools};
   9use num_traits::{FromPrimitive, Num, One, PrimInt, ToPrimitive, WrappingSub, Zero};
  10
  11use crate::{
  12    backend::{ArithmeticOps, GetModulus, ModInit, ModularOpsU64, Modulus, VectorOps},
  13    bool::parameters::{MP_BOOL_PARAMS, SP_BOOL_PARAMS},
  14    decomposer::{Decomposer, DefaultDecomposer, NumInfo, RlweDecomposer},
  15    lwe::{decrypt_lwe, encrypt_lwe, lwe_key_switch, lwe_ksk_keygen, measure_noise_lwe, LweSecret},
  16    multi_party::public_key_share,
  17    ntt::{self, Ntt, NttBackendU64, NttInit},
  18    random::{
  19        DefaultSecureRng, NewWithSeed, RandomFillGaussianInModulus, RandomFillUniformInModulus,
  20        RandomGaussianElementInModulus,
  21    },
  22    rgsw::{
  23        decrypt_rlwe, galois_auto, galois_key_gen, generate_auto_map, public_key_encrypt_rgsw,
  24        rgsw_by_rgsw_inplace, rlwe_by_rgsw, secret_key_encrypt_rgsw, IsTrivial, RgswCiphertext,
  25        RlweCiphertext, RlweSecret,
  26    },
  27    utils::{
  28        fill_random_ternary_secret_with_hamming_weight, generate_prime, mod_exponent,
  29        TryConvertFrom1, WithLocal,
  30    },
  31    Matrix, MatrixEntity, MatrixMut, Row, RowEntity, RowMut, Secret,
  32};
  33
  34use super::parameters::{BoolParameters, CiphertextModulus};
  35
  36thread_local! {
  37    static BOOL_EVALUATOR: RefCell<BoolEvaluator<Vec<Vec<u64>>, NttBackendU64, ModularOpsU64<CiphertextModulus<u64>>,  ModularOpsU64<CiphertextModulus<u64>>>> = RefCell::new(BoolEvaluator::new(MP_BOOL_PARAMS));
  38}
  39
  40pub fn set_parameter_set(parameter: &BoolParameters<u64>) {
  41    BoolEvaluator::with_local_mut(|e| *e = BoolEvaluator::new(parameter.clone()))
  42}
  43
  44impl WithLocal
  45    for BoolEvaluator<
  46        Vec<Vec<u64>>,
  47        NttBackendU64,
  48        ModularOpsU64<CiphertextModulus<u64>>,
  49        ModularOpsU64<CiphertextModulus<u64>>,
  50    >
  51{
  52    fn with_local<F, R>(func: F) -> R
  53    where
  54        F: Fn(&Self) -> R,
  55    {
  56        BOOL_EVALUATOR.with_borrow(|s| func(s))
  57    }
  58
  59    fn with_local_mut<F, R>(func: F) -> R
  60    where
  61        F: Fn(&mut Self) -> R,
  62    {
  63        BOOL_EVALUATOR.with_borrow_mut(|s| func(s))
  64    }
  65}
  66
  67struct ScratchMemory<M>
  68where
  69    M: Matrix,
  70{
  71    lwe_vector: M::R,
  72    decomposition_matrix: M,
  73}
  74
  75impl<M: MatrixEntity> ScratchMemory<M>
  76where
  77    M::R: RowEntity,
  78{
  79    fn new(parameters: &BoolParameters<M::MatElement>) -> Self {
  80        // Vector to store LWE ciphertext with LWE dimesnion n
  81        let lwe_vector = M::R::zeros(parameters.lwe_n().0 + 1);
  82
  83        // Matrix to store decomposed polynomials
  84        // Max decompistion count + space for temporary RLWE
  85        let d = std::cmp::max(
  86            parameters.auto_decomposition_count().0,
  87            std::cmp::max(
  88                parameters.rlwe_rgsw_decomposition_count().0 .0,
  89                parameters.rlwe_rgsw_decomposition_count().1 .0,
  90            ),
  91        ) + 2;
  92        let decomposition_matrix = M::zeros(d, parameters.rlwe_n().0);
  93
  94        Self {
  95            lwe_vector,
  96            decomposition_matrix,
  97        }
  98    }
  99}
 100
 101// thread_local! {
 102//     pub(crate) static CLIENT_KEY: RefCell<ClientKey> =
 103// RefCell::new(ClientKey::random()); }
 104
 105trait BoolEncoding {
 106    type Element;
 107    fn true_el(&self) -> Self::Element;
 108    fn false_el(&self) -> Self::Element;
 109    fn qby4(&self) -> Self::Element;
 110    fn decode(&self, m: Self::Element) -> bool;
 111}
 112
 113impl<T> BoolEncoding for CiphertextModulus<T> {
 114    type Element = T;
 115    fn false_el(&self) -> Self::Element {
 116        todo!()
 117    }
 118    fn qby4(&self) -> Self::Element {
 119        todo!()
 120    }
 121    fn true_el(&self) -> Self::Element {
 122        todo!()
 123    }
 124    fn decode(&self, m: Self::Element) -> bool {
 125        // let m = (((encoded_m + self.pbs_info.rlweq_by8).to_f64().unwrap() * 4f64)
 126        //     / self.pbs_info.parameters.rlwe_q().0.to_f64().unwrap())
 127        // .round() as usize
 128        //     % 4usize;
 129        //            panic!("Incorrect bool decryption. Got m={m} but expected m to be
 130        // 0 or 1")
 131
 132        todo!()
 133    }
 134}
 135
 136trait PbsKey {
 137    type M: Matrix;
 138
 139    /// RGSW ciphertext of LWE secret elements
 140    fn rgsw_ct_lwe_si(&self, si: usize) -> &Self::M;
 141    /// Key for automorphism
 142    fn galois_key_for_auto(&self, k: isize) -> &Self::M;
 143    /// LWE ksk to key switch from RLWE secret to LWE secret
 144    fn lwe_ksk(&self) -> &Self::M;
 145}
 146
 147trait PbsInfo {
 148    type Element;
 149    type Modulus: Modulus<Element = Self::Element>;
 150    type NttOp: Ntt<Element = Self::Element>;
 151    type D: Decomposer<Element = Self::Element>;
 152
 153    // Although both types have same bounds, they can be different types. For ex,
 154    // type RlweModOp may only support native modulus, where LweModOp may only
 155    // support prime modulus, etc.
 156    type RlweModOp: VectorOps<Element = Self::Element> + ArithmeticOps<Element = Self::Element>;
 157    type LweModOp: VectorOps<Element = Self::Element> + ArithmeticOps<Element = Self::Element>;
 158
 159    fn rlwe_q(&self) -> &Self::Modulus;
 160    fn lwe_q(&self) -> &Self::Modulus;
 161    fn br_q(&self) -> usize;
 162    fn rlwe_n(&self) -> usize;
 163    fn lwe_n(&self) -> usize;
 164    /// Embedding fator for ring X^{q}+1 inside
 165    fn embedding_factor(&self) -> usize;
 166    /// generator g
 167    fn g(&self) -> isize;
 168    /// Decomposers
 169    fn lwe_decomposer(&self) -> &Self::D;
 170    fn rlwe_rgsw_decomposer(&self) -> &(Self::D, Self::D);
 171    fn auto_decomposer(&self) -> &Self::D;
 172
 173    /// Modulus operators
 174    fn modop_lweq(&self) -> &Self::LweModOp;
 175    fn modop_rlweq(&self) -> &Self::RlweModOp;
 176
 177    /// Ntt operators
 178    fn nttop_rlweq(&self) -> &Self::NttOp;
 179
 180    /// Maps a \in Z^*_{q} to discrete log k, with generator g (i.e. g^k =
 181    /// a). Returned vector is of size q that stores dlog of a at `vec[a]`.
 182    /// For any a, if k is s.t. a = g^{k}, then k is expressed as k. If k is s.t
 183    /// a = -g^{k}, then k is expressed as k=k+q/2
 184    fn g_k_dlog_map(&self) -> &[usize];
 185    fn rlwe_auto_map(&self, k: isize) -> &(Vec<usize>, Vec<bool>);
 186}
 187
 188#[derive(Clone)]
 189struct ClientKey {
 190    sk_rlwe: RlweSecret,
 191    sk_lwe: LweSecret,
 192}
 193
 194impl ClientKey {
 195    fn random() -> Self {
 196        let sk_rlwe = RlweSecret::random(0, 0);
 197        let sk_lwe = LweSecret::random(0, 0);
 198        Self { sk_rlwe, sk_lwe }
 199    }
 200}
 201
 202// impl WithLocal for ClientKey {
 203//     fn with_local<F, R>(func: F) -> R
 204//     where
 205//         F: Fn(&Self) -> R,
 206//     {
 207//         CLIENT_KEY.with_borrow(|client_key| func(client_key))
 208//     }
 209
 210//     fn with_local_mut<F, R>(func: F) -> R
 211//     where
 212//         F: Fn(&mut Self) -> R,
 213//     {
 214//         CLIENT_KEY.with_borrow_mut(|client_key| func(client_key))
 215//     }
 216// }
 217
 218// fn set_client_key(key: &ClientKey) {
 219//     ClientKey::with_local_mut(|k| *k = key.clone())
 220// }
 221
 222struct MultiPartyDecryptionShare<E> {
 223    share: E,
 224}
 225
 226struct CommonReferenceSeededCollectivePublicKeyShare<R, S, P> {
 227    share: R,
 228    cr_seed: S,
 229    parameters: P,
 230}
 231
 232struct PublicKey<M, R, O> {
 233    key: M,
 234    _phantom: PhantomData<(R, O)>,
 235}
 236
 237impl<
 238        M: MatrixMut + MatrixEntity,
 239        Rng: NewWithSeed + RandomFillUniformInModulus<[M::MatElement], CiphertextModulus<M::MatElement>>,
 240        ModOp: VectorOps<Element = M::MatElement> + ModInit<M = CiphertextModulus<M::MatElement>>,
 241    >
 242    From<
 243        &[CommonReferenceSeededCollectivePublicKeyShare<
 244            M::R,
 245            Rng::Seed,
 246            BoolParameters<M::MatElement>,
 247        >],
 248    > for PublicKey<M, Rng, ModOp>
 249where
 250    <M as Matrix>::R: RowMut,
 251    Rng::Seed: Copy + PartialEq,
 252    M::MatElement: PartialEq + Copy,
 253{
 254    fn from(
 255        value: &[CommonReferenceSeededCollectivePublicKeyShare<
 256            M::R,
 257            Rng::Seed,
 258            BoolParameters<M::MatElement>,
 259        >],
 260    ) -> Self {
 261        assert!(value.len() > 0);
 262
 263        let parameters = &value[0].parameters;
 264        let mut key = M::zeros(2, parameters.rlwe_n().0);
 265
 266        // sample A
 267        let seed = value[0].cr_seed;
 268        let mut main_rng = Rng::new_with_seed(seed);
 269        RandomFillUniformInModulus::random_fill(
 270            &mut main_rng,
 271            parameters.rlwe_q(),
 272            key.get_row_mut(0),
 273        );
 274
 275        // Sum all Bs
 276        let rlweq_modop = ModOp::new(parameters.rlwe_q().clone());
 277        value.iter().for_each(|share_i| {
 278            assert!(share_i.cr_seed == seed);
 279            assert!(&share_i.parameters == parameters);
 280
 281            rlweq_modop.elwise_add_mut(key.get_row_mut(1), share_i.share.as_ref());
 282        });
 283
 284        PublicKey {
 285            key,
 286            _phantom: PhantomData,
 287        }
 288    }
 289}
 290
 291struct CommonReferenceSeededMultiPartyServerKeyShare<M: Matrix, P, S> {
 292    rgsw_cts: Vec<M>,
 293    auto_keys: HashMap<isize, M>,
 294    lwe_ksk: M::R,
 295    /// Common reference seed
 296    cr_seed: S,
 297    parameters: P,
 298}
 299struct SeededMultiPartyServerKey<M: Matrix, S, P> {
 300    rgsw_cts: Vec<M>,
 301    auto_keys: HashMap<isize, M>,
 302    lwe_ksk: M::R,
 303    cr_seed: S,
 304    parameters: P,
 305}
 306
 307fn aggregate_multi_party_server_key_shares<
 308    M: MatrixMut + MatrixEntity,
 309    S: Copy + PartialEq,
 310    D: RlweDecomposer<Element = M::MatElement>,
 311    ModOp: VectorOps<Element = M::MatElement> + ModInit<M = CiphertextModulus<M::MatElement>>,
 312    NttOp: Ntt<Element = M::MatElement> + NttInit<CiphertextModulus<M::MatElement>>,
 313>(
 314    shares: &[CommonReferenceSeededMultiPartyServerKeyShare<M, BoolParameters<M::MatElement>, S>],
 315) -> SeededMultiPartyServerKey<M, S, BoolParameters<M::MatElement>>
 316where
 317    <M as Matrix>::R: RowMut + RowEntity,
 318    M::MatElement: PrimInt + PartialEq + Zero,
 319    M: Clone,
 320{
 321    assert!(shares.len() > 0);
 322    let parameters = shares[0].parameters.clone();
 323    let cr_seed = shares[0].cr_seed;
 324
 325    let rlwe_n = parameters.rlwe_n().0;
 326    let g = parameters.g() as isize;
 327    let rlwe_q = parameters.rlwe_q();
 328    let lwe_q = parameters.lwe_q();
 329
 330    // sanity checks
 331    shares.iter().skip(1).for_each(|s| {
 332        assert!(s.parameters == parameters);
 333        assert!(s.cr_seed == cr_seed);
 334    });
 335
 336    let rlweq_modop = ModOp::new(*rlwe_q);
 337    let rlweq_nttop = NttOp::new(rlwe_q, rlwe_n);
 338
 339    // auto keys
 340    let mut auto_keys = HashMap::new();
 341    for i in [g, -g] {
 342        let mut key = M::zeros(parameters.auto_decomposition_count().0, rlwe_n);
 343
 344        shares.iter().for_each(|s| {
 345            let auto_key_share_i = s.auto_keys.get(&i).expect("Auto key {i} missing");
 346            assert!(
 347                auto_key_share_i.dimension() == (parameters.auto_decomposition_count().0, rlwe_n)
 348            );
 349            izip!(key.iter_rows_mut(), auto_key_share_i.iter_rows()).for_each(
 350                |(partb_out, partb_share)| {
 351                    rlweq_modop.elwise_add_mut(partb_out.as_mut(), partb_share.as_ref());
 352                },
 353            );
 354        });
 355
 356        auto_keys.insert(i, key);
 357    }
 358
 359    // rgsw ciphertext (most expensive part!)
 360    let lwe_n = parameters.lwe_n().0;
 361    let rgsw_by_rgsw_decomposer = parameters.rgsw_rgsw_decomposer::<D::D>();
 362    let mut scratch_matrix = M::zeros(
 363        std::cmp::max(
 364            rgsw_by_rgsw_decomposer.a().decomposition_count(),
 365            rgsw_by_rgsw_decomposer.b().decomposition_count(),
 366        ) + (rgsw_by_rgsw_decomposer.a().decomposition_count() * 2
 367            + rgsw_by_rgsw_decomposer.b().decomposition_count() * 2),
 368        rlwe_n,
 369    );
 370
 371    let mut tmp_rgsw =
 372        RgswCiphertext::<M, _>::empty(rlwe_n, &rgsw_by_rgsw_decomposer, rlwe_q.clone()).data;
 373    let rgsw_cts = (0..lwe_n)
 374        .into_iter()
 375        .map(|index| {
 376            // copy over rgsw ciphertext for index^th secret element from first share and
 377            // treat it as accumulating rgsw ciphertext
 378            let mut rgsw_i = shares[0].rgsw_cts[index].clone();
 379
 380            shares.iter().skip(1).for_each(|si| {
 381                // copy over si's RGSW[index] ciphertext and send to evaluation domain
 382                izip!(tmp_rgsw.iter_rows_mut(), si.rgsw_cts[index].iter_rows()).for_each(
 383                    |(to_ri, from_ri)| {
 384                        to_ri.as_mut().copy_from_slice(from_ri.as_ref());
 385                        rlweq_nttop.forward(to_ri.as_mut())
 386                    },
 387                );
 388
 389                rgsw_by_rgsw_inplace(
 390                    &mut rgsw_i,
 391                    &tmp_rgsw,
 392                    &rgsw_by_rgsw_decomposer,
 393                    &mut scratch_matrix,
 394                    &rlweq_nttop,
 395                    &rlweq_modop,
 396                );
 397            });
 398
 399            rgsw_i
 400        })
 401        .collect_vec();
 402
 403    // LWE ksks
 404    let mut lwe_ksk = M::R::zeros(rlwe_n * parameters.lwe_decomposition_count().0);
 405    let lweq_modop = ModOp::new(*lwe_q);
 406    shares.iter().for_each(|si| {
 407        assert!(si.lwe_ksk.as_ref().len() == rlwe_n * parameters.lwe_decomposition_count().0);
 408        lweq_modop.elwise_add_mut(lwe_ksk.as_mut(), si.lwe_ksk.as_ref())
 409    });
 410
 411    SeededMultiPartyServerKey {
 412        rgsw_cts,
 413        auto_keys,
 414        lwe_ksk,
 415        cr_seed,
 416        parameters: parameters,
 417    }
 418}
 419
 420/// Seeded single party server key
 421struct SeededServerKey<M: Matrix, P, S> {
 422    /// Rgsw cts of LWE secret elements
 423    pub(crate) rgsw_cts: Vec<M>,
 424    /// Auto keys
 425    pub(crate) auto_keys: HashMap<isize, M>,
 426    /// LWE ksk to key switching LWE ciphertext from RLWE secret to LWE secret
 427    pub(crate) lwe_ksk: M::R,
 428    /// Parameters
 429    pub(crate) parameters: P,
 430    /// Main seed
 431    pub(crate) seed: S,
 432}
 433
 434impl<M: Matrix, S> SeededServerKey<M, BoolParameters<M::MatElement>, S> {
 435    pub(crate) fn from_raw(
 436        auto_keys: HashMap<isize, M>,
 437        rgsw_cts: Vec<M>,
 438        lwe_ksk: M::R,
 439        parameters: BoolParameters<M::MatElement>,
 440        seed: S,
 441    ) -> Self {
 442        // sanity checks
 443        auto_keys.iter().for_each(|v| {
 444            assert!(
 445                v.1.dimension()
 446                    == (
 447                        parameters.auto_decomposition_count().0,
 448                        parameters.rlwe_n().0
 449                    )
 450            )
 451        });
 452
 453        let (part_a_d, part_b_d) = parameters.rlwe_rgsw_decomposition_count();
 454        rgsw_cts.iter().for_each(|v| {
 455            assert!(v.dimension() == (part_a_d.0 * 2 + part_b_d.0, parameters.rlwe_n().0))
 456        });
 457        assert!(
 458            lwe_ksk.as_ref().len()
 459                == (parameters.lwe_decomposition_count().0 * parameters.rlwe_n().0)
 460        );
 461
 462        SeededServerKey {
 463            rgsw_cts,
 464            auto_keys,
 465            lwe_ksk,
 466            parameters,
 467            seed,
 468        }
 469    }
 470}
 471
 472/// Server key in evaluation domain
 473struct ServerKeyEvaluationDomain<M, R, N> {
 474    /// Rgsw cts of LWE secret elements
 475    rgsw_cts: Vec<M>,
 476    /// Galois keys
 477    galois_keys: HashMap<isize, M>,
 478    /// LWE ksk to key switching LWE ciphertext from RLWE secret to LWE secret
 479    lwe_ksk: M,
 480    _phanton: PhantomData<(R, N)>,
 481}
 482
 483impl<
 484        M: MatrixMut + MatrixEntity,
 485        R: RandomFillUniformInModulus<[M::MatElement], CiphertextModulus<M::MatElement>> + NewWithSeed,
 486        N: NttInit<CiphertextModulus<M::MatElement>> + Ntt<Element = M::MatElement>,
 487    > From<&SeededServerKey<M, BoolParameters<M::MatElement>, R::Seed>>
 488    for ServerKeyEvaluationDomain<M, R, N>
 489where
 490    <M as Matrix>::R: RowMut,
 491    M::MatElement: Copy,
 492    R::Seed: Clone,
 493{
 494    fn from(value: &SeededServerKey<M, BoolParameters<M::MatElement>, R::Seed>) -> Self {
 495        let mut main_prng = R::new_with_seed(value.seed.clone());
 496        let parameters = &value.parameters;
 497        let g = parameters.g() as isize;
 498        let ring_size = value.parameters.rlwe_n().0;
 499        let lwe_n = value.parameters.lwe_n().0;
 500        let rlwe_q = value.parameters.rlwe_q();
 501        let lwq_q = value.parameters.lwe_q();
 502
 503        let nttop = N::new(rlwe_q, ring_size);
 504
 505        // galois keys
 506        let mut auto_keys = HashMap::new();
 507        let auto_decomp_count = parameters.auto_decomposition_count().0;
 508        for i in [g, -g] {
 509            let seeded_auto_key = value.auto_keys.get(&i).unwrap();
 510            assert!(seeded_auto_key.dimension() == (auto_decomp_count, ring_size));
 511
 512            let mut data = M::zeros(auto_decomp_count * 2, ring_size);
 513
 514            // sample RLWE'_A(-s(X^k))
 515            data.iter_rows_mut().take(auto_decomp_count).for_each(|ri| {
 516                RandomFillUniformInModulus::random_fill(&mut main_prng, &rlwe_q, ri.as_mut())
 517            });
 518
 519            // copy over RLWE'B_(-s(X^k))
 520            izip!(
 521                data.iter_rows_mut().skip(auto_decomp_count),
 522                seeded_auto_key.iter_rows()
 523            )
 524            .for_each(|(to_ri, from_ri)| to_ri.as_mut().copy_from_slice(from_ri.as_ref()));
 525
 526            // Send to Evaluation domain
 527            data.iter_rows_mut()
 528                .for_each(|ri| nttop.forward(ri.as_mut()));
 529
 530            auto_keys.insert(i, data);
 531        }
 532
 533        // RGSW ciphertexts
 534        let (rlrg_a_decomp, rlrg_b_decomp) = parameters.rlwe_rgsw_decomposition_count();
 535        let rgsw_cts = value
 536            .rgsw_cts
 537            .iter()
 538            .map(|seeded_rgsw_si| {
 539                assert!(
 540                    seeded_rgsw_si.dimension()
 541                        == (rlrg_a_decomp.0 * 2 + rlrg_b_decomp.0, ring_size)
 542                );
 543
 544                let mut data = M::zeros(rlrg_a_decomp.0 * 2 + rlrg_b_decomp.0 * 2, ring_size);
 545
 546                // copy over RLWE'(-sm)
 547                izip!(
 548                    data.iter_rows_mut().take(rlrg_a_decomp.0 * 2),
 549                    seeded_rgsw_si.iter_rows().take(rlrg_a_decomp.0 * 2)
 550                )
 551                .for_each(|(to_ri, from_ri)| to_ri.as_mut().copy_from_slice(from_ri.as_ref()));
 552
 553                // sample RLWE'_A(m)
 554                data.iter_rows_mut()
 555                    .skip(rlrg_a_decomp.0 * 2)
 556                    .take(rlrg_b_decomp.0)
 557                    .for_each(|ri| {
 558                        RandomFillUniformInModulus::random_fill(
 559                            &mut main_prng,
 560                            &rlwe_q,
 561                            ri.as_mut(),
 562                        )
 563                    });
 564
 565                // copy over RLWE'_B(m)
 566                izip!(
 567                    data.iter_rows_mut()
 568                        .skip(rlrg_a_decomp.0 * 2 + rlrg_b_decomp.0),
 569                    seeded_rgsw_si.iter_rows().skip(rlrg_a_decomp.0 * 2)
 570                )
 571                .for_each(|(to_ri, from_ri)| to_ri.as_mut().copy_from_slice(from_ri.as_ref()));
 572
 573                // send polynomials to evaluation domain
 574                data.iter_rows_mut()
 575                    .for_each(|ri| nttop.forward(ri.as_mut()));
 576
 577                data
 578            })
 579            .collect_vec();
 580
 581        // LWE ksk
 582        let lwe_ksk = {
 583            let d = parameters.lwe_decomposition_count().0;
 584            assert!(value.lwe_ksk.as_ref().len() == d * ring_size);
 585
 586            let mut data = M::zeros(d * ring_size, lwe_n + 1);
 587            izip!(data.iter_rows_mut(), value.lwe_ksk.as_ref().iter()).for_each(|(lwe_i, bi)| {
 588                RandomFillUniformInModulus::random_fill(
 589                    &mut main_prng,
 590                    &lwq_q,
 591                    &mut lwe_i.as_mut()[1..],
 592                );
 593                lwe_i.as_mut()[0] = *bi;
 594            });
 595
 596            data
 597        };
 598
 599        ServerKeyEvaluationDomain {
 600            rgsw_cts,
 601            galois_keys: auto_keys,
 602            lwe_ksk,
 603            _phanton: PhantomData,
 604        }
 605    }
 606}
 607
 608impl<
 609        M: MatrixMut + MatrixEntity,
 610        Rng: NewWithSeed,
 611        N: NttInit<CiphertextModulus<M::MatElement>> + Ntt<Element = M::MatElement>,
 612    > From<&SeededMultiPartyServerKey<M, Rng::Seed, BoolParameters<M::MatElement>>>
 613    for ServerKeyEvaluationDomain<M, Rng, N>
 614where
 615    <M as Matrix>::R: RowMut,
 616    Rng::Seed: Copy,
 617    Rng: RandomFillUniformInModulus<[M::MatElement], CiphertextModulus<M::MatElement>>,
 618    M::MatElement: Copy,
 619{
 620    fn from(
 621        value: &SeededMultiPartyServerKey<M, Rng::Seed, BoolParameters<M::MatElement>>,
 622    ) -> Self {
 623        let g = value.parameters.g() as isize;
 624        let rlwe_n = value.parameters.rlwe_n().0;
 625        let lwe_n = value.parameters.lwe_n().0;
 626        let rlwe_q = value.parameters.rlwe_q();
 627        let lwe_q = value.parameters.lwe_q();
 628
 629        let mut main_prng = Rng::new_with_seed(value.cr_seed);
 630
 631        let rlwe_nttop = N::new(rlwe_q, rlwe_n);
 632
 633        // auto keys
 634        let mut auto_keys = HashMap::new();
 635        let auto_d_count = value.parameters.auto_decomposition_count().0;
 636        for i in [g, -g] {
 637            let mut key = M::zeros(auto_d_count * 2, rlwe_n);
 638
 639            // sample a
 640            key.iter_rows_mut().take(auto_d_count).for_each(|ri| {
 641                RandomFillUniformInModulus::random_fill(&mut main_prng, &rlwe_q, ri.as_mut())
 642            });
 643
 644            let key_part_b = value.auto_keys.get(&i).unwrap();
 645            assert!(key_part_b.dimension() == (auto_d_count, rlwe_n));
 646            izip!(
 647                key.iter_rows_mut().skip(auto_d_count),
 648                key_part_b.iter_rows()
 649            )
 650            .for_each(|(to_ri, from_ri)| {
 651                to_ri.as_mut().copy_from_slice(from_ri.as_ref());
 652            });
 653
 654            // send to evaluation domain
 655            key.iter_rows_mut()
 656                .for_each(|ri| rlwe_nttop.forward(ri.as_mut()));
 657
 658            auto_keys.insert(i, key);
 659        }
 660
 661        // rgsw cts
 662        let (rgswrgsw_d_a, rgswrgsw_d_b) = value.parameters.rgsw_rgsw_decomposition_count();
 663        let (rlrg_d_a, rlrg_d_b) = value.parameters.rlwe_rgsw_decomposition_count();
 664        // d_a and d_b may differ for RGSWxRGSW multiplication and RLWExRGSW
 665        // multiplication. After this point RGSW ciphertexts will only be used for
 666        // RLWExRGSW multiplication (in blind rotation). Thus we drop any additional
 667        // RLWE ciphertexts in incoming collective RGSW ciphertexts, which are
 668        // result of RGSWxRGSW multiplications.
 669        let rgsw_ct_rows_in = rgswrgsw_d_a.0 * 2 + rgswrgsw_d_b.0 * 2;
 670        let rgsw_ct_rows_out = rlrg_d_a.0 * 2 + rlrg_d_b.0 * 2;
 671        let rgsw_cts = value
 672            .rgsw_cts
 673            .iter()
 674            .map(|ct_i_in| {
 675                assert!(ct_i_in.dimension() == (rgsw_ct_rows_in, rlwe_n));
 676                let mut eval_ct_i_out = M::zeros(rgsw_ct_rows_out, rlwe_n);
 677
 678                // RLWE'(-sm) part A
 679                izip!(
 680                    eval_ct_i_out.iter_rows_mut().take(rlrg_d_a.0),
 681                    ct_i_in.iter_rows().take(rlrg_d_a.0)
 682                )
 683                .for_each(|(to_ri, from_ri)| {
 684                    to_ri.as_mut().copy_from_slice(from_ri.as_ref());
 685                    rlwe_nttop.forward(to_ri.as_mut());
 686                });
 687
 688                // RLWE'(-sm) part B
 689                izip!(
 690                    eval_ct_i_out
 691                        .iter_rows_mut()
 692                        .skip(rlrg_d_a.0)
 693                        .take(rlrg_d_a.0),
 694                    ct_i_in.iter_rows().skip(rgswrgsw_d_a.0).take(rlrg_d_a.0)
 695                )
 696                .for_each(|(to_ri, from_ri)| {
 697                    to_ri.as_mut().copy_from_slice(from_ri.as_ref());
 698                    rlwe_nttop.forward(to_ri.as_mut());
 699                });
 700
 701                // RLWE'(m) Part A
 702                izip!(
 703                    eval_ct_i_out
 704                        .iter_rows_mut()
 705                        .skip(rlrg_d_a.0 * 2)
 706                        .take(rlrg_d_b.0),
 707                    ct_i_in
 708                        .iter_rows()
 709                        .skip(rgswrgsw_d_a.0 * 2)
 710                        .take(rlrg_d_b.0)
 711                )
 712                .for_each(|(to_ri, from_ri)| {
 713                    to_ri.as_mut().copy_from_slice(from_ri.as_ref());
 714                    rlwe_nttop.forward(to_ri.as_mut());
 715                });
 716
 717                // RLWE'(m) Part B
 718                izip!(
 719                    eval_ct_i_out
 720                        .iter_rows_mut()
 721                        .skip(rlrg_d_a.0 * 2 + rlrg_d_b.0)
 722                        .take(rlrg_d_b.0),
 723                    ct_i_in
 724                        .iter_rows()
 725                        .skip(rgswrgsw_d_a.0 * 2 + rgswrgsw_d_b.0)
 726                        .take(rlrg_d_b.0)
 727                )
 728                .for_each(|(to_ri, from_ri)| {
 729                    to_ri.as_mut().copy_from_slice(from_ri.as_ref());
 730                    rlwe_nttop.forward(to_ri.as_mut());
 731                });
 732
 733                eval_ct_i_out
 734            })
 735            .collect_vec();
 736
 737        // lwe ksk
 738        let d_lwe = value.parameters.lwe_decomposition_count().0;
 739        let mut lwe_ksk = M::zeros(rlwe_n * d_lwe, lwe_n + 1);
 740        izip!(lwe_ksk.iter_rows_mut(), value.lwe_ksk.as_ref().iter()).for_each(|(lwe_i, bi)| {
 741            RandomFillUniformInModulus::random_fill(
 742                &mut main_prng,
 743                &lwe_q,
 744                &mut lwe_i.as_mut()[1..],
 745            );
 746            lwe_i.as_mut()[0] = *bi;
 747        });
 748
 749        ServerKeyEvaluationDomain {
 750            rgsw_cts,
 751            galois_keys: auto_keys,
 752            lwe_ksk,
 753            _phanton: PhantomData,
 754        }
 755    }
 756}
 757
 758//FIXME(Jay): Figure out a way for BoolEvaluator to have access to ServerKey
 759// via a pointer and implement PbsKey for BoolEvaluator instead of ServerKey
 760// directly
 761impl<M: Matrix, R, N> PbsKey for ServerKeyEvaluationDomain<M, R, N> {
 762    type M = M;
 763    fn galois_key_for_auto(&self, k: isize) -> &Self::M {
 764        self.galois_keys.get(&k).unwrap()
 765    }
 766    fn rgsw_ct_lwe_si(&self, si: usize) -> &Self::M {
 767        &self.rgsw_cts[si]
 768    }
 769
 770    fn lwe_ksk(&self) -> &Self::M {
 771        &self.lwe_ksk
 772    }
 773}
 774
 775struct BoolPbsInfo<M: Matrix, Ntt, RlweModOp, LweModOp> {
 776    auto_decomposer: DefaultDecomposer<M::MatElement>,
 777    rlwe_rgsw_decomposer: (
 778        DefaultDecomposer<M::MatElement>,
 779        DefaultDecomposer<M::MatElement>,
 780    ),
 781    lwe_decomposer: DefaultDecomposer<M::MatElement>,
 782    g_k_dlog_map: Vec<usize>,
 783    rlwe_nttop: Ntt,
 784    rlwe_modop: RlweModOp,
 785    lwe_modop: LweModOp,
 786    embedding_factor: usize,
 787    nand_test_vec: M::R,
 788    rlwe_qby4: M::MatElement,
 789    rlwe_auto_maps: Vec<(Vec<usize>, Vec<bool>)>,
 790    parameters: BoolParameters<M::MatElement>,
 791}
 792
 793impl<M: Matrix, NttOp, RlweModOp, LweModOp> PbsInfo for BoolPbsInfo<M, NttOp, RlweModOp, LweModOp>
 794where
 795    M::MatElement: PrimInt + WrappingSub + NumInfo + Debug,
 796    RlweModOp: ArithmeticOps<Element = M::MatElement> + VectorOps<Element = M::MatElement>,
 797    LweModOp: ArithmeticOps<Element = M::MatElement> + VectorOps<Element = M::MatElement>,
 798    NttOp: Ntt<Element = M::MatElement>,
 799{
 800    type Modulus = CiphertextModulus<M::MatElement>;
 801    type Element = M::MatElement;
 802    type D = DefaultDecomposer<M::MatElement>;
 803    type RlweModOp = RlweModOp;
 804    type LweModOp = LweModOp;
 805    type NttOp = NttOp;
 806    fn rlwe_auto_map(&self, k: isize) -> &(Vec<usize>, Vec<bool>) {
 807        let g = self.parameters.g() as isize;
 808        if k == g {
 809            &self.rlwe_auto_maps[0]
 810        } else if k == -g {
 811            &self.rlwe_auto_maps[1]
 812        } else {
 813            panic!("RLWE auto map only supports k in [-g, g], but got k={k}");
 814        }
 815    }
 816    fn br_q(&self) -> usize {
 817        *self.parameters.br_q()
 818    }
 819    fn lwe_decomposer(&self) -> &Self::D {
 820        &self.lwe_decomposer
 821    }
 822    fn rlwe_rgsw_decomposer(&self) -> &(Self::D, Self::D) {
 823        &self.rlwe_rgsw_decomposer
 824    }
 825    fn auto_decomposer(&self) -> &Self::D {
 826        &self.auto_decomposer
 827    }
 828    fn embedding_factor(&self) -> usize {
 829        self.embedding_factor
 830    }
 831    fn g(&self) -> isize {
 832        self.parameters.g() as isize
 833    }
 834    fn g_k_dlog_map(&self) -> &[usize] {
 835        &self.g_k_dlog_map
 836    }
 837    fn lwe_n(&self) -> usize {
 838        self.parameters.lwe_n().0
 839    }
 840    fn lwe_q(&self) -> &Self::Modulus {
 841        self.parameters.lwe_q()
 842    }
 843    fn rlwe_n(&self) -> usize {
 844        self.parameters.rlwe_n().0
 845    }
 846    fn rlwe_q(&self) -> &Self::Modulus {
 847        self.parameters.rlwe_q()
 848    }
 849    fn modop_lweq(&self) -> &Self::LweModOp {
 850        &self.lwe_modop
 851    }
 852    fn modop_rlweq(&self) -> &Self::RlweModOp {
 853        &self.rlwe_modop
 854    }
 855    fn nttop_rlweq(&self) -> &Self::NttOp {
 856        &self.rlwe_nttop
 857    }
 858}
 859
 860struct BoolEvaluator<M, Ntt, RlweModOp, LweModOp>
 861where
 862    M: Matrix,
 863{
 864    pbs_info: BoolPbsInfo<M, Ntt, RlweModOp, LweModOp>,
 865    scratch_memory: ScratchMemory<M>,
 866    _phantom: PhantomData<M>,
 867}
 868
 869impl<M: Matrix, NttOp, RlweModOp, LweModOp> BoolEvaluator<M, NttOp, RlweModOp, LweModOp> {}
 870
 871impl<M: Matrix, NttOp, RlweModOp, LweModOp> BoolEvaluator<M, NttOp, RlweModOp, LweModOp>
 872where
 873    M: MatrixEntity + MatrixMut,
 874    M::MatElement: PrimInt + Debug + Display + NumInfo + FromPrimitive + WrappingSub,
 875    NttOp: Ntt<Element = M::MatElement>,
 876    RlweModOp: ArithmeticOps<Element = M::MatElement>
 877        + VectorOps<Element = M::MatElement>
 878        + GetModulus<Element = M::MatElement, M = CiphertextModulus<M::MatElement>>,
 879    LweModOp: ArithmeticOps<Element = M::MatElement>
 880        + VectorOps<Element = M::MatElement>
 881        + GetModulus<Element = M::MatElement, M = CiphertextModulus<M::MatElement>>,
 882    M::R: TryConvertFrom1<[i32], CiphertextModulus<M::MatElement>> + RowEntity + Debug,
 883    <M as Matrix>::R: RowMut,
 884    DefaultSecureRng: RandomFillGaussianInModulus<[M::MatElement], CiphertextModulus<M::MatElement>>
 885        + RandomFillUniformInModulus<[M::MatElement], CiphertextModulus<M::MatElement>>
 886        + RandomGaussianElementInModulus<M::MatElement, CiphertextModulus<M::MatElement>>
 887        + NewWithSeed,
 888{
 889    fn new(parameters: BoolParameters<M::MatElement>) -> Self
 890    where
 891        RlweModOp: ModInit<M = CiphertextModulus<M::MatElement>>,
 892        LweModOp: ModInit<M = CiphertextModulus<M::MatElement>>,
 893        NttOp: NttInit<CiphertextModulus<M::MatElement>>,
 894    {
 895        //TODO(Jay): Run sanity checks for modulus values in parameters
 896
 897        // generatr dlog map s.t. g^{k} % q = a, for all a \in Z*_{q}
 898        let g = parameters.g();
 899        let q = *parameters.br_q();
 900        let mut g_k_dlog_map = vec![0usize; q];
 901        for i in 0..q / 2 {
 902            let v = mod_exponent(g as u64, i as u64, q as u64) as usize;
 903            // g^i
 904            g_k_dlog_map[v] = i;
 905            // -(g^i)
 906            g_k_dlog_map[q - v] = i + (q / 2);
 907        }
 908
 909        let embedding_factor = (2 * parameters.rlwe_n().0) / q;
 910
 911        let rlwe_nttop = NttOp::new(parameters.rlwe_q(), parameters.rlwe_n().0);
 912        let rlwe_modop = RlweModOp::new(*parameters.rlwe_q());
 913        let lwe_modop = LweModOp::new(*parameters.lwe_q());
 914
 915        // set test vectors
 916        let q = *parameters.br_q();
 917        let qby2 = q >> 1;
 918        let qby8 = q >> 3;
 919        let mut nand_test_vec = M::R::zeros(qby2);
 920        // Q/8 (Q: rlwe_q)
 921        let true_m_el = parameters.rlwe_q().true_el();
 922        // -Q/8
 923        let false_m_el = parameters.rlwe_q().false_el();
 924        for i in 0..qby2 {
 925            if i < (3 * qby8) {
 926                nand_test_vec.as_mut()[i] = true_m_el;
 927            } else {
 928                nand_test_vec.as_mut()[i] = false_m_el;
 929            }
 930        }
 931
 932        // v(X) -> v(X^{-g})
 933        let (auto_map_index, auto_map_sign) = generate_auto_map(qby2, -(g as isize));
 934        let mut nand_test_vec_autog = M::R::zeros(qby2);
 935        izip!(
 936            nand_test_vec.as_ref().iter(),
 937            auto_map_index.iter(),
 938            auto_map_sign.iter()
 939        )
 940        .for_each(|(v, to_index, to_sign)| {
 941            if !to_sign {
 942                // negate
 943                nand_test_vec_autog.as_mut()[*to_index] = rlwe_modop.neg(v);
 944            } else {
 945                nand_test_vec_autog.as_mut()[*to_index] = *v;
 946            }
 947        });
 948
 949        // auto map indices and sign
 950        let mut rlwe_auto_maps = vec![];
 951        let ring_size = parameters.rlwe_n().0;
 952        let g = parameters.g() as isize;
 953        for i in [g, -g] {
 954            rlwe_auto_maps.push(generate_auto_map(ring_size, i))
 955        }
 956
 957        let rlwe_qby4 = parameters.rlwe_q().qby4();
 958
 959        let scratch_memory = ScratchMemory::new(&parameters);
 960
 961        let pbs_info = BoolPbsInfo {
 962            auto_decomposer: parameters.auto_decomposer(),
 963            lwe_decomposer: parameters.lwe_decomposer(),
 964            rlwe_rgsw_decomposer: parameters.rlwe_rgsw_decomposer(),
 965            g_k_dlog_map,
 966            embedding_factor,
 967            lwe_modop,
 968            rlwe_modop,
 969            rlwe_nttop,
 970            nand_test_vec: nand_test_vec_autog,
 971            rlwe_qby4,
 972            rlwe_auto_maps,
 973            parameters: parameters,
 974        };
 975
 976        BoolEvaluator {
 977            pbs_info,
 978            scratch_memory,
 979            _phantom: PhantomData,
 980        }
 981    }
 982
 983    fn client_key(&self) -> ClientKey {
 984        let sk_lwe = LweSecret::random(
 985            self.pbs_info.parameters.lwe_n().0 >> 1,
 986            self.pbs_info.parameters.lwe_n().0,
 987        );
 988        let sk_rlwe = RlweSecret::random(
 989            self.pbs_info.parameters.rlwe_n().0 >> 1,
 990            self.pbs_info.parameters.rlwe_n().0,
 991        );
 992        ClientKey { sk_rlwe, sk_lwe }
 993    }
 994
 995    fn server_key(
 996        &self,
 997        client_key: &ClientKey,
 998    ) -> SeededServerKey<M, BoolParameters<M::MatElement>, [u8; 32]> {
 999        DefaultSecureRng::with_local_mut(|rng| {
1000            let mut main_seed = [0u8; 32];
1001            rng.fill_bytes(&mut main_seed);
1002
1003            let mut main_prng = DefaultSecureRng::new_seeded(main_seed);
1004
1005            let rlwe_n = self.pbs_info.parameters.rlwe_n().0;
1006            let sk_rlwe = &client_key.sk_rlwe;
1007            let sk_lwe = &client_key.sk_lwe;
1008
1009            // generate auto keys -g, g
1010            let mut auto_keys = HashMap::new();
1011            let auto_gadget = self.pbs_info.auto_decomposer.gadget_vector();
1012            let g = self.pbs_info.parameters.g() as isize;
1013            for i in [g, -g] {
1014                let mut gk = M::zeros(self.pbs_info.auto_decomposer.decomposition_count(), rlwe_n);
1015                galois_key_gen(
1016                    &mut gk,
1017                    sk_rlwe.values(),
1018                    i,
1019                    &auto_gadget,
1020                    &self.pbs_info.rlwe_modop,
1021                    &self.pbs_info.rlwe_nttop,
1022                    &mut main_prng,
1023                    rng,
1024                );
1025                auto_keys.insert(i, gk);
1026            }
1027
1028            // generate rgsw ciphertexts RGSW(si) where si is i^th LWE secret element
1029            let ring_size = self.pbs_info.parameters.rlwe_n().0;
1030            let rlwe_q = self.pbs_info.parameters.rlwe_q();
1031            let (rlrg_d_a, rlrg_d_b) = (
1032                self.pbs_info.rlwe_rgsw_decomposer.0.decomposition_count(),
1033                self.pbs_info.rlwe_rgsw_decomposer.1.decomposition_count(),
1034            );
1035            let rlrg_gadget_a = self.pbs_info.rlwe_rgsw_decomposer.0.gadget_vector();
1036            let rlrg_gadget_b = self.pbs_info.rlwe_rgsw_decomposer.1.gadget_vector();
1037            let rgsw_cts = sk_lwe
1038                .values()
1039                .iter()
1040                .map(|si| {
1041                    // X^{si}; assume |emebedding_factor * si| < N
1042                    let mut m = M::R::zeros(ring_size);
1043                    let si = (self.pbs_info.embedding_factor as i32) * si;
1044                    // dbg!(si);
1045                    if si < 0 {
1046                        // X^{-i} = X^{2N - i} = -X^{N-i}
1047                        m.as_mut()[ring_size - (si.abs() as usize)] = rlwe_q.neg_one();
1048                    } else {
1049                        // X^{i}
1050                        m.as_mut()[si.abs() as usize] = M::MatElement::one();
1051                    }
1052
1053                    let mut rgsw_si = M::zeros(rlrg_d_a * 2 + rlrg_d_b, ring_size);
1054                    secret_key_encrypt_rgsw(
1055                        &mut rgsw_si,
1056                        m.as_ref(),
1057                        &rlrg_gadget_a,
1058                        &rlrg_gadget_b,
1059                        sk_rlwe.values(),
1060                        &self.pbs_info.rlwe_modop,
1061                        &self.pbs_info.rlwe_nttop,
1062                        &mut main_prng,
1063                        rng,
1064                    );
1065
1066                    rgsw_si
1067                })
1068                .collect_vec();
1069
1070            // LWE KSK from RLWE secret s -> LWE secret z
1071            let d_lwe_gadget = self.pbs_info.lwe_decomposer.gadget_vector();
1072            let mut lwe_ksk =
1073                M::R::zeros(self.pbs_info.lwe_decomposer.decomposition_count() * ring_size);
1074            lwe_ksk_keygen(
1075                &sk_rlwe.values(),
1076                &sk_lwe.values(),
1077                &mut lwe_ksk,
1078                &d_lwe_gadget,
1079                &self.pbs_info.lwe_modop,
1080                &mut main_prng,
1081                rng,
1082            );
1083
1084            SeededServerKey::from_raw(
1085                auto_keys,
1086                rgsw_cts,
1087                lwe_ksk,
1088                self.pbs_info.parameters.clone(),
1089                main_seed,
1090            )
1091        })
1092    }
1093
1094    fn multi_party_server_key_share(
1095        &self,
1096        cr_seed: [u8; 32],
1097        collective_pk: &M,
1098        client_key: &ClientKey,
1099    ) -> CommonReferenceSeededMultiPartyServerKeyShare<M, BoolParameters<M::MatElement>, [u8; 32]>
1100    {
1101        DefaultSecureRng::with_local_mut(|rng| {
1102            let mut main_prng = DefaultSecureRng::new_seeded(cr_seed);
1103
1104            let sk_rlwe = &client_key.sk_rlwe;
1105            let sk_lwe = &client_key.sk_lwe;
1106
1107            let g = self.pbs_info.parameters.g() as isize;
1108            let ring_size = self.pbs_info.parameters.rlwe_n().0;
1109            let rlwe_q = self.pbs_info.parameters.rlwe_q();
1110            let lwe_q = self.pbs_info.parameters.lwe_q();
1111
1112            let rlweq_modop = &self.pbs_info.rlwe_modop;
1113            let rlweq_nttop = &self.pbs_info.rlwe_nttop;
1114
1115            // sanity check
1116            assert!(sk_rlwe.values().len() == ring_size);
1117            assert!(sk_lwe.values().len() == self.pbs_info.parameters.lwe_n().0);
1118
1119            // auto keys
1120            let mut auto_keys = HashMap::new();
1121            let auto_gadget = self.pbs_info.auto_decomposer.gadget_vector();
1122            for i in [g, -g] {
1123                let mut ksk_out = M::zeros(
1124                    self.pbs_info.auto_decomposer.decomposition_count(),
1125                    ring_size,
1126                );
1127                galois_key_gen(
1128                    &mut ksk_out,
1129                    sk_rlwe.values(),
1130                    i,
1131                    &auto_gadget,
1132                    rlweq_modop,
1133                    rlweq_nttop,
1134                    &mut main_prng,
1135                    rng,
1136                );
1137                auto_keys.insert(i, ksk_out);
1138            }
1139
1140            // rgsw ciphertexts of lwe secret elements
1141            let rgsw_rgsw_decomposer = self
1142                .pbs_info
1143                .parameters
1144                .rgsw_rgsw_decomposer::<DefaultDecomposer<M::MatElement>>();
1145            let (rgrg_d_a, rgrg_d_b) = (
1146                rgsw_rgsw_decomposer.0.decomposition_count(),
1147                rgsw_rgsw_decomposer.1.decomposition_count(),
1148            );
1149            let (rgrg_gadget_a, rgrg_gadget_b) = (
1150                rgsw_rgsw_decomposer.0.gadget_vector(),
1151                rgsw_rgsw_decomposer.1.gadget_vector(),
1152            );
1153            let rgsw_cts = sk_lwe
1154                .values()
1155                .iter()
1156                .map(|si| {
1157                    let mut m = M::R::zeros(ring_size);
1158                    //TODO(Jay): It will be nice to have a function that returns polynomial
1159                    // (monomial infact!) corresponding to secret element embedded in ring X^{2N+1}.
1160                    // Save lots of mistakes where one forgest to emebed si in bigger ring.
1161                    let si = *si * (self.pbs_info.embedding_factor as i32);
1162                    if si < 0 {
1163                        // X^{-si} = X^{2N-si} = -X^{N-si}, assuming abs(si) < N
1164                        // (which it is given si is secret element)
1165                        m.as_mut()[ring_size - (si.abs() as usize)] = rlwe_q.neg_one();
1166                    } else {
1167                        m.as_mut()[si as usize] = M::MatElement::one();
1168                    }
1169
1170                    // public key RGSW encryption has no part that can be seeded, unlike secret key
1171                    // RGSW encryption where RLWE'_A(m) is seeded
1172                    let mut out_rgsw = M::zeros(rgrg_d_a * 2 + rgrg_d_b * 2, ring_size);
1173                    public_key_encrypt_rgsw(
1174                        &mut out_rgsw,
1175                        &m.as_ref(),
1176                        collective_pk,
1177                        &rgrg_gadget_a,
1178                        &rgrg_gadget_b,
1179                        rlweq_modop,
1180                        rlweq_nttop,
1181                        rng,
1182                    );
1183
1184                    out_rgsw
1185                })
1186                .collect_vec();
1187
1188            // LWE ksk
1189            let mut lwe_ksk =
1190                M::R::zeros(self.pbs_info.lwe_decomposer.decomposition_count() * ring_size);
1191            let lwe_modop = &self.pbs_info.lwe_modop;
1192            let d_lwe_gadget_vec = self.pbs_info.lwe_decomposer.gadget_vector();
1193            lwe_ksk_keygen(
1194                sk_rlwe.values(),
1195                sk_lwe.values(),
1196                &mut lwe_ksk,
1197                &d_lwe_gadget_vec,
1198                lwe_modop,
1199                &mut main_prng,
1200                rng,
1201            );
1202
1203            CommonReferenceSeededMultiPartyServerKeyShare {
1204                auto_keys,
1205                rgsw_cts,
1206                lwe_ksk,
1207                cr_seed,
1208                parameters: self.pbs_info.parameters.clone(),
1209            }
1210        })
1211    }
1212
1213    fn multi_party_public_key_share(
1214        &self,
1215        cr_seed: [u8; 32],
1216        client_key: &ClientKey,
1217    ) -> CommonReferenceSeededCollectivePublicKeyShare<
1218        <M as Matrix>::R,
1219        [u8; 32],
1220        BoolParameters<<M as Matrix>::MatElement>,
1221    > {
1222        DefaultSecureRng::with_local_mut(|rng| {
1223            let mut share_out = M::R::zeros(self.pbs_info.parameters.rlwe_n().0);
1224            let modop = &self.pbs_info.rlwe_modop;
1225            let nttop = &self.pbs_info.rlwe_nttop;
1226            let mut main_prng = DefaultSecureRng::new_seeded(cr_seed);
1227            public_key_share(
1228                &mut share_out,
1229                client_key.sk_rlwe.values(),
1230                modop,
1231                nttop,
1232                &mut main_prng,
1233                rng,
1234            );
1235
1236            CommonReferenceSeededCollectivePublicKeyShare {
1237                share: share_out,
1238                cr_seed: cr_seed,
1239                parameters: self.pbs_info.parameters.clone(),
1240            }
1241        })
1242    }
1243
1244    fn multi_party_decryption_share(
1245        &self,
1246        lwe_ct: &M::R,
1247        client_key: &ClientKey,
1248    ) -> MultiPartyDecryptionShare<<M as Matrix>::MatElement> {
1249        assert!(lwe_ct.as_ref().len() == self.pbs_info.parameters.rlwe_n().0 + 1);
1250        let modop = &self.pbs_info.rlwe_modop;
1251        let mut neg_s = M::R::try_convert_from(
1252            client_key.sk_rlwe.values(),
1253            &self.pbs_info.parameters.rlwe_q(),
1254        );
1255        modop.elwise_neg_mut(neg_s.as_mut());
1256
1257        let mut neg_sa = M::MatElement::zero();
1258        izip!(lwe_ct.as_ref().iter().skip(1), neg_s.as_ref().iter()).for_each(|(ai, nsi)| {
1259            neg_sa = modop.add(&neg_sa, &modop.mul(ai, nsi));
1260        });
1261
1262        let e = DefaultSecureRng::with_local_mut(|rng| {
1263            let mut e =
1264                RandomGaussianElementInModulus::random(rng, self.pbs_info.parameters.rlwe_q());
1265            e
1266        });
1267        let share = modop.add(&neg_sa, &e);
1268
1269        MultiPartyDecryptionShare { share }
1270    }
1271
1272    pub(crate) fn multi_party_decrypt(
1273        &self,
1274        shares: &[MultiPartyDecryptionShare<M::MatElement>],
1275        lwe_ct: &M::R,
1276    ) -> bool {
1277        let modop = &self.pbs_info.rlwe_modop;
1278        let mut sum_a = M::MatElement::zero();
1279        shares
1280            .iter()
1281            .for_each(|share_i| sum_a = modop.add(&sum_a, &share_i.share));
1282
1283        let encoded_m = modop.add(&lwe_ct.as_ref()[0], &sum_a);
1284        self.pbs_info.parameters.rlwe_q().decode(encoded_m)
1285    }
1286
1287    /// First encrypt as RLWE(m) with m as constant polynomial and extract it as
1288    /// LWE ciphertext
1289    pub(crate) fn pk_encrypt(&self, pk: &M, m: bool) -> M::R {
1290        DefaultSecureRng::with_local_mut(|rng| {
1291            let modop = &self.pbs_info.rlwe_modop;
1292            let nttop = &self.pbs_info.rlwe_nttop;
1293
1294            // RLWE(0)
1295            // sample ephemeral key u
1296            let ring_size = self.pbs_info.parameters.rlwe_n().0;
1297            let mut u = vec![0i32; ring_size];
1298            fill_random_ternary_secret_with_hamming_weight(u.as_mut(), ring_size >> 1, rng);
1299            let mut u = M::R::try_convert_from(&u, &self.pbs_info.parameters.rlwe_q());
1300            nttop.forward(u.as_mut());
1301
1302            let mut ua = M::R::zeros(ring_size);
1303            ua.as_mut().copy_from_slice(pk.get_row_slice(0));
1304            let mut ub = M::R::zeros(ring_size);
1305            ub.as_mut().copy_from_slice(pk.get_row_slice(1));
1306
1307            // a*u
1308            nttop.forward(ua.as_mut());
1309            modop.elwise_mul_mut(ua.as_mut(), u.as_ref());
1310            nttop.backward(ua.as_mut());
1311
1312            // b*u
1313            nttop.forward(ub.as_mut());
1314            modop.elwise_mul_mut(ub.as_mut(), u.as_ref());
1315            nttop.backward(ub.as_mut());
1316
1317            let mut rlwe = M::zeros(2, ring_size);
1318            // sample error
1319            rlwe.iter_rows_mut().for_each(|ri| {
1320                RandomFillGaussianInModulus::random_fill(
1321                    rng,
1322                    &self.pbs_info.parameters.rlwe_q(),
1323                    ri.as_mut(),
1324                );
1325            });
1326
1327            // a*u + e0
1328            modop.elwise_add_mut(rlwe.get_row_mut(0), ua.as_ref());
1329            // b*u + e1
1330            modop.elwise_add_mut(rlwe.get_row_mut(1), ub.as_ref());
1331
1332            //FIXME(Jay): Figure out a way to get Q/8 form modulus
1333            let m = if m {
1334                // Q/8
1335                self.pbs_info.rlwe_q().true_el()
1336            } else {
1337                // -Q/8
1338                self.pbs_info.rlwe_q().false_el()
1339            };
1340
1341            // b*u + e1 + m, where m is constant polynomial
1342            rlwe.set(1, 0, modop.add(rlwe.get(1, 0), &m));
1343
1344            // sample extract index 0
1345            let mut lwe_out = M::R::zeros(ring_size + 1);
1346            sample_extract(&mut lwe_out, &rlwe, modop, 0);
1347
1348            lwe_out
1349        })
1350    }
1351
1352    /// TODO(Jay): Fetch client key from thread local
1353    pub fn sk_encrypt(&self, m: bool, client_key: &ClientKey) -> M::R {
1354        //FIXME(Jay): Figure out a way to get Q/8 form modulus
1355        let m = if m {
1356            // Q/8
1357            self.pbs_info.rlwe_q().true_el()
1358        } else {
1359            // -Q/8
1360            self.pbs_info.rlwe_q().false_el()
1361        };
1362
1363        DefaultSecureRng::with_local_mut(|rng| {
1364            let mut lwe_out = M::R::zeros(self.pbs_info.parameters.rlwe_n().0 + 1);
1365            encrypt_lwe(
1366                &mut lwe_out,
1367                &m,
1368                client_key.sk_rlwe.values(),
1369                &self.pbs_info.rlwe_modop,
1370                rng,
1371            );
1372            lwe_out
1373        })
1374    }
1375
1376    pub fn sk_decrypt(&self, lwe_ct: &M::R, client_key: &ClientKey) -> bool {
1377        let m = decrypt_lwe(
1378            lwe_ct,
1379            client_key.sk_rlwe.values(),
1380            &self.pbs_info.rlwe_modop,
1381        );
1382        self.pbs_info.rlwe_q().decode(m)
1383    }
1384
1385    // TODO(Jay): scratch spaces must be thread local. Don't pass them as arguments
1386    pub fn nand(
1387        &mut self,
1388        c0: &M::R,
1389        c1: &M::R,
1390        server_key: &ServerKeyEvaluationDomain<M, DefaultSecureRng, NttOp>,
1391    ) -> M::R {
1392        let mut c_out = M::R::zeros(c0.as_ref().len());
1393        let modop = &self.pbs_info.rlwe_modop;
1394        izip!(
1395            c_out.as_mut().iter_mut(),
1396            c0.as_ref().iter(),
1397            c1.as_ref().iter()
1398        )
1399        .for_each(|(o, i0, i1)| {
1400            *o = modop.add(i0, i1);
1401        });
1402        // +Q/4
1403        c_out.as_mut()[0] = modop.add(&c_out.as_ref()[0], &self.pbs_info.rlwe_qby4);
1404
1405        // PBS
1406        pbs(
1407            &self.pbs_info,
1408            &self.pbs_info.nand_test_vec,
1409            &mut c_out,
1410            server_key,
1411            &mut self.scratch_memory.lwe_vector,
1412            &mut self.scratch_memory.decomposition_matrix,
1413        );
1414
1415        c_out
1416    }
1417}
1418
1419/// LMKCY+ Blind rotation
1420///
1421/// gk_to_si: [g^0, ..., g^{q/2-1}, -g^0, -g^1, .., -g^{q/2-1}]
1422fn blind_rotation<
1423    MT: IsTrivial + MatrixMut,
1424    Mmut: MatrixMut<MatElement = MT::MatElement> + Matrix,
1425    D: Decomposer<Element = MT::MatElement>,
1426    NttOp: Ntt<Element = MT::MatElement>,
1427    ModOp: ArithmeticOps<Element = MT::MatElement> + VectorOps<Element = MT::MatElement>,
1428    K: PbsKey<M = Mmut>,
1429    P: PbsInfo<Element = MT::MatElement>,
1430>(
1431    trivial_rlwe_test_poly: &mut MT,
1432    scratch_matrix: &mut Mmut,
1433    g: isize,
1434    w: usize,
1435    q: usize,
1436    gk_to_si: &[Vec<usize>],
1437    rlwe_rgsw_decomposer: &(D, D),
1438    auto_decomposer: &D,
1439    ntt_op: &NttOp,
1440    mod_op: &ModOp,
1441    parameters: &P,
1442    pbs_key: &K,
1443) where
1444    <Mmut as Matrix>::R: RowMut,
1445    Mmut::MatElement: Copy + Zero,
1446    <MT as Matrix>::R: RowMut,
1447{
1448    let q_by_2 = q / 2;
1449
1450    // -(g^k)
1451    for i in (1..q_by_2).rev() {
1452        gk_to_si[q_by_2 + i].iter().for_each(|s_index| {
1453            rlwe_by_rgsw(
1454                trivial_rlwe_test_poly,
1455                pbs_key.rgsw_ct_lwe_si(*s_index),
1456                scratch_matrix,
1457                rlwe_rgsw_decomposer,
1458                ntt_op,
1459                mod_op,
1460            );
1461        });
1462
1463        let (auto_map_index, auto_map_sign) = parameters.rlwe_auto_map(g);
1464        galois_auto(
1465            trivial_rlwe_test_poly,
1466            pbs_key.galois_key_for_auto(g),
1467            scratch_matrix,
1468            &auto_map_index,
1469            &auto_map_sign,
1470            mod_op,
1471            ntt_op,
1472            auto_decomposer,
1473        );
1474    }
1475
1476    // -(g^0)
1477    gk_to_si[q_by_2].iter().for_each(|s_index| {
1478        rlwe_by_rgsw(
1479            trivial_rlwe_test_poly,
1480            pbs_key.rgsw_ct_lwe_si(*s_index),
1481            scratch_matrix,
1482            rlwe_rgsw_decomposer,
1483            ntt_op,
1484            mod_op,
1485        );
1486    });
1487    let (auto_map_index, auto_map_sign) = parameters.rlwe_auto_map(-g);
1488    galois_auto(
1489        trivial_rlwe_test_poly,
1490        pbs_key.galois_key_for_auto(-g),
1491        scratch_matrix,
1492        &auto_map_index,
1493        &auto_map_sign,
1494        mod_op,
1495        ntt_op,
1496        auto_decomposer,
1497    );
1498
1499    // +(g^k)
1500    for i in (1..q_by_2).rev() {
1501        gk_to_si[i].iter().for_each(|s_index| {
1502            rlwe_by_rgsw(
1503                trivial_rlwe_test_poly,
1504                pbs_key.rgsw_ct_lwe_si(*s_index),
1505                scratch_matrix,
1506                rlwe_rgsw_decomposer,
1507                ntt_op,
1508                mod_op,
1509            );
1510        });
1511
1512        let (auto_map_index, auto_map_sign) = parameters.rlwe_auto_map(g);
1513        galois_auto(
1514            trivial_rlwe_test_poly,
1515            pbs_key.galois_key_for_auto(g),
1516            scratch_matrix,
1517            &auto_map_index,
1518            &auto_map_sign,
1519            mod_op,
1520            ntt_op,
1521            auto_decomposer,
1522        );
1523    }
1524
1525    // +(g^0)
1526    gk_to_si[0].iter().for_each(|s_index| {
1527        rlwe_by_rgsw(
1528            trivial_rlwe_test_poly,
1529            pbs_key.rgsw_ct_lwe_si(gk_to_si[q_by_2][*s_index]),
1530            scratch_matrix,
1531            rlwe_rgsw_decomposer,
1532            ntt_op,
1533            mod_op,
1534        );
1535    });
1536}
1537
1538/// - Mod down
1539/// - key switching
1540/// - mod down
1541/// - blind rotate
1542fn pbs<
1543    M: Matrix + MatrixMut + MatrixEntity,
1544    P: PbsInfo<Element = M::MatElement>,
1545    // NttOp: Ntt<Element = M::MatElement>,
1546    // ModOp: ArithmeticOps<Element = M::MatElement> + VectorOps<Element = M::MatElement>,
1547    K: PbsKey<M = M>,
1548>(
1549    pbs_info: &P,
1550    test_vec: &M::R,
1551    lwe_in: &mut M::R,
1552    pbs_key: &K,
1553    scratch_lwe_vec: &mut M::R,
1554    scratch_blind_rotate_matrix: &mut M,
1555) where
1556    <M as Matrix>::R: RowMut,
1557    M::MatElement: PrimInt + ToPrimitive + FromPrimitive + One + Copy + Zero + Display,
1558{
1559    let rlwe_q = pbs_info.rlwe_q();
1560    let lwe_q = pbs_info.lwe_q();
1561    let br_q = pbs_info.br_q();
1562    let rlwe_qf64 = rlwe_q.q().unwrap().to_f64().unwrap();
1563    let lwe_qf64 = lwe_q.q().unwrap().to_f64().unwrap();
1564    let br_qf64 = br_q.to_f64().unwrap();
1565    let rlwe_n = pbs_info.rlwe_n();
1566
1567    // PBSTracer::with_local_mut(|t| {
1568    //     let out = lwe_in
1569    //         .as_ref()
1570    //         .iter()
1571    //         .map(|v| v.to_u64().unwrap())
1572    //         .collect_vec();
1573    //     t.ct_rlwe_q_mod = out;
1574    // });
1575
1576    // moddown Q -> Q_ks
1577    lwe_in.as_mut().iter_mut().for_each(|v| {
1578        *v =
1579            M::MatElement::from_f64(((v.to_f64().unwrap() * lwe_qf64) / rlwe_qf64).round()).unwrap()
1580    });
1581
1582    // PBSTracer::with_local_mut(|t| {
1583    //     let out = lwe_in
1584    //         .as_ref()
1585    //         .iter()
1586    //         .map(|v| v.to_u64().unwrap())
1587    //         .collect_vec();
1588    //     t.ct_lwe_q_mod = out;
1589    // });
1590
1591    // key switch RLWE secret to LWE secret
1592    scratch_lwe_vec.as_mut().fill(M::MatElement::zero());
1593    lwe_key_switch(
1594        scratch_lwe_vec,
1595        lwe_in,
1596        pbs_key.lwe_ksk(),
1597        pbs_info.modop_lweq(),
1598        pbs_info.lwe_decomposer(),
1599    );
1600
1601    // PBSTracer::with_local_mut(|t| {
1602    //     let out = scratch_lwe_vec
1603    //         .as_ref()
1604    //         .iter()
1605    //         .map(|v| v.to_u64().unwrap())
1606    //         .collect_vec();
1607    //     t.ct_lwe_q_mod_after_ksk = out;
1608    // });
1609
1610    // odd mowdown Q_ks -> q
1611    let g_k_dlog_map = pbs_info.g_k_dlog_map();
1612    let mut g_k_si = vec![vec![]; br_q];
1613    scratch_lwe_vec
1614        .as_ref()
1615        .iter()
1616        .skip(1)
1617        .enumerate()
1618        .for_each(|(index, v)| {
1619            let odd_v = mod_switch_odd(v.to_f64().unwrap(), lwe_qf64, br_qf64);
1620            let k = g_k_dlog_map[odd_v];
1621            g_k_si[k].push(index);
1622        });
1623
1624    // PBSTracer::with_local_mut(|t| {
1625    //     let out = scratch_lwe_vec
1626    //         .as_ref()
1627    //         .iter()
1628    //         .map(|v| mod_switch_odd(v.to_f64().unwrap(), lwe_qf64, br_qf64) as
1629    // u64)         .collect_vec();
1630    //     t.ct_br_q_mod = out;
1631    // });
1632
1633    // handle b and set trivial test RLWE
1634    let g = pbs_info.g() as usize;
1635    let g_times_b = (g * mod_switch_odd(
1636        scratch_lwe_vec.as_ref()[0].to_f64().unwrap(),
1637        lwe_qf64,
1638        br_qf64,
1639    )) % (br_q);
1640    // v = (v(X) * X^{g*b}) mod X^{q/2}+1
1641    let br_qby2 = br_q / 2;
1642    let mut gb_monomial_sign = true;
1643    let mut gb_monomial_exp = g_times_b;
1644    // X^{g*b} mod X^{q/2}+1
1645    if gb_monomial_exp > br_qby2 {
1646        gb_monomial_exp -= br_qby2;
1647        gb_monomial_sign = false
1648    }
1649    // monomial mul
1650    let mut trivial_rlwe_test_poly = RlweCiphertext::<_, DefaultSecureRng> {
1651        data: M::zeros(2, rlwe_n),
1652        is_trivial: true,
1653        _phatom: PhantomData,
1654    };
1655    if pbs_info.embedding_factor() == 1 {
1656        monomial_mul(
1657            test_vec.as_ref(),
1658            trivial_rlwe_test_poly.get_row_mut(1).as_mut(),
1659            gb_monomial_exp,
1660            gb_monomial_sign,
1661            br_qby2,
1662            pbs_info.modop_rlweq(),
1663        );
1664    } else {
1665        // use lwe_in to store the `t = v(X) * X^{g*2} mod X^{q/2}+1` temporarily. This
1666        // works because q/2 <= N (where N is lwe_in LWE dimension) always.
1667        monomial_mul(
1668            test_vec.as_ref(),
1669            &mut lwe_in.as_mut()[..br_qby2],
1670            gb_monomial_exp,
1671            gb_monomial_sign,
1672            br_qby2,
1673            pbs_info.modop_rlweq(),
1674        );
1675
1676        // emebed poly `t` in ring X^{q/2}+1 inside the bigger ring X^{N}+1
1677        let embed_factor = pbs_info.embedding_factor();
1678        let partb_trivial_rlwe = trivial_rlwe_test_poly.get_row_mut(1);
1679        lwe_in.as_ref()[..br_qby2]
1680            .iter()
1681            .enumerate()
1682            .for_each(|(index, v)| {
1683                partb_trivial_rlwe[embed_factor * index] = *v;
1684            });
1685    }
1686
1687    // blind rotate
1688    blind_rotation(
1689        &mut trivial_rlwe_test_poly,
1690        scratch_blind_rotate_matrix,
1691        pbs_info.g(),
1692        1,
1693        br_q,
1694        &g_k_si,
1695        pbs_info.rlwe_rgsw_decomposer(),
1696        pbs_info.auto_decomposer(),
1697        pbs_info.nttop_rlweq(),
1698        pbs_info.modop_rlweq(),
1699        pbs_info,
1700        pbs_key,
1701    );
1702
1703    // ClientKey::with_local(|ck| {
1704    //     let ring_size = parameters.rlwe_n();
1705    //     let mut rlwe_ct = vec![vec![0u64; ring_size]; 2];
1706    //     izip!(
1707    //         rlwe_ct[0].iter_mut(),
1708    //         trivial_rlwe_test_poly.0.get_row_slice(0)
1709    //     )
1710    //     .for_each(|(t, f)| {
1711    //         *t = f.to_u64().unwrap();
1712    //     });
1713    //     izip!(
1714    //         rlwe_ct[1].iter_mut(),
1715    //         trivial_rlwe_test_poly.0.get_row_slice(1)
1716    //     )
1717    //     .for_each(|(t, f)| {
1718    //         *t = f.to_u64().unwrap();
1719    //     });
1720    //     let mut m_out = vec![vec![0u64; ring_size]];
1721    //     let modop = ModularOpsU64::new(rlwe_q.to_u64().unwrap());
1722    //     let nttop = NttBackendU64::new(rlwe_q.to_u64().unwrap(), ring_size);
1723    //     decrypt_rlwe(&rlwe_ct, ck.sk_rlwe.values(), &mut m_out, &nttop, &modop);
1724
1725    //     println!("RLWE post PBS message: {:?}", m_out[0]);
1726    // });
1727
1728    // sample extract
1729    sample_extract(lwe_in, &trivial_rlwe_test_poly, pbs_info.modop_rlweq(), 0);
1730}
1731
1732fn mod_switch_odd(v: f64, from_q: f64, to_q: f64) -> usize {
1733    let odd_v = (((v * to_q) / (from_q)).floor()).to_usize().unwrap();
1734    //TODO(Jay): check correctness of this
1735    odd_v + ((odd_v & 1) ^ 1)
1736}
1737
1738// TODO(Jay): Add tests for sample extract
1739fn sample_extract<M: Matrix + MatrixMut, ModOp: ArithmeticOps<Element = M::MatElement>>(
1740    lwe_out: &mut M::R,
1741    rlwe_in: &M,
1742    mod_op: &ModOp,
1743    index: usize,
1744) where
1745    <M as Matrix>::R: RowMut,
1746    M::MatElement: Copy,
1747{
1748    let ring_size = rlwe_in.dimension().1;
1749
1750    // index..=0
1751    let to = &mut lwe_out.as_mut()[1..];
1752    let from = rlwe_in.get_row_slice(0);
1753    for i in 0..index + 1 {
1754        to[i] = from[index - i];
1755    }
1756
1757    // -(N..index)
1758    for i in index + 1..ring_size {
1759        to[i] = mod_op.neg(&from[ring_size + index - i]);
1760    }
1761
1762    // set b
1763    lwe_out.as_mut()[0] = *rlwe_in.get(1, index);
1764}
1765
1766/// TODO(Jay): Write tests for monomial mul
1767fn monomial_mul<El, ModOp: ArithmeticOps<Element = El>>(
1768    p_in: &[El],
1769    p_out: &mut [El],
1770    mon_exp: usize,
1771    mon_sign: bool,
1772    ring_size: usize,
1773    mod_op: &ModOp,
1774) where
1775    El: Copy,
1776{
1777    debug_assert!(p_in.as_ref().len() == ring_size);
1778    debug_assert!(p_in.as_ref().len() == p_out.as_ref().len());
1779    debug_assert!(mon_exp < ring_size);
1780
1781    p_in.as_ref().iter().enumerate().for_each(|(index, v)| {
1782        let mut to_index = index + mon_exp;
1783        let mut to_sign = mon_sign;
1784        if to_index >= ring_size {
1785            to_index = to_index - ring_size;
1786            to_sign = !to_sign;
1787        }
1788
1789        if !to_sign {
1790            p_out.as_mut()[to_index] = mod_op.neg(v);
1791        } else {
1792            p_out.as_mut()[to_index] = *v;
1793        }
1794    });
1795}
1796
1797// thread_local! {
1798//     static PBS_TRACER: RefCell<PBSTracer<Vec<Vec<u64>>>> =
1799// RefCell::new(PBSTracer::default()); }
1800
1801// #[derive(Default)]
1802// struct PBSTracer<M>
1803// where
1804//     M: Matrix + Default,
1805// {
1806//     pub(crate) ct_rlwe_q_mod: M::R,
1807//     pub(crate) ct_lwe_q_mod: M::R,
1808//     pub(crate) ct_lwe_q_mod_after_ksk: M::R,
1809//     pub(crate) ct_br_q_mod: Vec<u64>,
1810// }
1811
1812// impl PBSTracer<Vec<Vec<u64>>> {
1813//     fn trace(&self, parameters: &BoolParameters<u64>, sk_lwe: &[i32],
1814// sk_rlwe: &[i32]) {         assert!(parameters.rlwe_n().0 == sk_rlwe.len());
1815//         assert!(parameters.lwe_n().0 == sk_lwe.len());
1816
1817//         let modop_rlweq = ModularOpsU64::new(*parameters.rlwe_q());
1818//         // noise after mod down Q -> Q_ks
1819//         let m_back0 = decrypt_lwe(&self.ct_rlwe_q_mod, sk_rlwe,
1820// &modop_rlweq);
1821
1822//         let modop_lweq = ModularOpsU64::new(parameters.lwe_q().0);
1823//         // noise after mod down Q -> Q_ks
1824//         let m_back1 = decrypt_lwe(&self.ct_lwe_q_mod, sk_rlwe, &modop_lweq);
1825//         // noise after key switch from RLWE -> LWE
1826//         let m_back2 = decrypt_lwe(&self.ct_lwe_q_mod_after_ksk, sk_lwe,
1827// &modop_lweq);
1828
1829//         // noise after mod down odd from Q_ks -> q
1830//         let modop_br_q = ModularOpsU64::new(parameters.br_q().0);
1831//         let m_back3 = decrypt_lwe(&self.ct_br_q_mod, sk_lwe, &modop_br_q);
1832
1833//         println!(
1834//             "
1835//             M initial mod Q: {m_back0},
1836//             M after mod down Q -> Q_ks: {m_back1},
1837//             M after key switch from RLWE -> LWE: {m_back2},
1838//             M after mod dwon Q_ks -> q: {m_back3}
1839//         "
1840//         );
1841//     }
1842// }
1843
1844// impl WithLocal for PBSTracer<Vec<Vec<u64>>> {
1845//     fn with_local<F, R>(func: F) -> R
1846//     where
1847//         F: Fn(&Self) -> R,
1848//     {
1849//         PBS_TRACER.with_borrow(|t| func(t))
1850//     }
1851
1852//     fn with_local_mut<F, R>(func: F) -> R
1853//     where
1854//         F: Fn(&mut Self) -> R,
1855//     {
1856//         PBS_TRACER.with_borrow_mut(|t| func(t))
1857//     }
1858// }