#[encrypted_library]
mod arcis_library {

    struct ArcisRNGUtils;
    impl ArcisRNGUtils {
        #[arcis_circuit = "bitwise_and"]
        fn bitwise_and(a: u128, b: u128) -> u128 {}
        #[arcis_circuit = "lowest_bigger_power_of_two_minus_one"]
        fn lowest_bigger_power_of_two_minus_one(a: u128) -> u128 {}
    }

    /// This struct gives access to randomness operations.
    /// ```
    /// use arcis::*;
    ///
    /// #[encrypted]
    /// mod circuits {
    ///     use arcis::*;
    ///
    ///     #[instruction]
    ///     pub fn doc_arcis_rng(s: Shared) -> (Enc<Shared, bool>, [u8; 32], Enc<Mxe, [u128; 3]>, bool) {
    ///         let random_bool = ArcisRNG::bool();
    ///
    ///         // Generates an u128 with bit width 3.
    ///         let first_integer = ArcisRNG::gen_integer_from_width(3);
    ///         // 0 <= first_integer < 2^3 = 8
    ///
    ///         // Generates an integer between 17 and 27, both included.
    ///         // Will try 24 times with each attempt having > 1/2 chance of success.
    ///         // So chance of failure is below 2^-24.
    ///         let (second_integer, did_it_work) = ArcisRNG::gen_integer_in_range(17, 27, 24);
    ///
    ///         let mut u128_array = [first_integer, second_integer, 42];
    ///         // Shuffles a slice in-place.
    ///         ArcisRNG::shuffle(&mut u128_array);
    ///
    ///         // Generates uniformly an [u8; 32].
    ///         let u8_array = ArcisRNG::gen_uniform::<[u8;32]>(); // You need to put the type there.
    ///         // Our interpreter does not have type inference and needs to know the type right away.
    ///
    ///         // Does not work:
    ///         // let b: bool = ArcisRNG::gen_uniform(); // We do not do type inference.
    ///         // let random_float = ArcisRNG::gen_uniform::<f64>(); // floats cannot be generated uniformly.
    ///
    ///         // You can send the randomness to someone.
    ///         let enc_bool = s.from_arcis(random_bool);
    ///
    ///         // Or reveal it to the world.
    ///         let revealed_u8_array = u8_array.reveal();
    ///
    ///         // Or keep it for later.
    ///         let stored_shuffled_u128_array = Mxe::get().from_arcis(u128_array);
    ///
    ///         (enc_bool, revealed_u8_array, stored_shuffled_u128_array, did_it_work.reveal())
    ///     }
    /// }
    /// ```
    pub struct ArcisRNG;

    impl ArcisRNG {
        fn gen_and_reject(max: u128) -> (u128, bool) {
            let lbpotmo = ArcisRNGUtils::lowest_bigger_power_of_two_minus_one(max);
            let rand = Self::gen_integer_from_width(128);
            let rand = ArcisRNGUtils::bitwise_and(rand, lbpotmo);
            if rand <= max {
                (rand, true)
            } else {
                (0, false)
            }
        }
        /// Generates an integer between `min` and `max`, inclusive.
        /// It tries `n_attempts` times, with each attempt having > 1/2 chance of success.
        /// Produces incorrect results if `min > max`.
        /// Returns `(min, false)` in case of failure, `(result, true)` in case of success.
        /// Note that `n_attempts` must be known at compile time.
        pub fn gen_integer_in_range(min: u128, max: u128, n_attempts: usize) -> (u128, bool) {
            let range = max - min;
            let mut res = 0;
            let mut success = false;
            for _ in 0..n_attempts {
                let (r, s) = Self::gen_and_reject(range);
                if !success {
                    res = r;
                    success = s;
                }
            }
            (res + min, success)
        }
    }
}
