use crate::{gaussian_noise::conversion::modular_variance_to_variance, utils::square};

/// Additional noise generated by the bit multiplication
pub fn variance_keyswitch_one_bit(
    log2_base: u64,
    level: u64,
    ciphertext_modulus_log: u32,
    variance_ksk: f64,
) -> f64 {
    let variance_key_coefficient_binary: f64 =
        modular_variance_to_variance(1. / 4., ciphertext_modulus_log);

    let square_expectation_key_coefficient_binary: f64 =
        modular_variance_to_variance(square(1. / 2.), ciphertext_modulus_log);

    let base = 2_f64.powi(log2_base as i32);
    let b2l = 2_f64.powi((log2_base * 2 * level) as i32);
    let q_square = 2_f64.powi((2 * ciphertext_modulus_log) as i32);

    // res 2
    let res_2 = (q_square / (12. * b2l) - 1. / 12.)
        * (variance_key_coefficient_binary + square_expectation_key_coefficient_binary);

    // res 3
    let res_3 = 1. / 4. * variance_key_coefficient_binary;

    // res 4
    let res_4 = (level as f64) * variance_ksk * (square(base) + 2.) / 12.;

    res_2 + res_3 + res_4
}
