#![doc = include_str!("../README.md")]

mod arcis_macros;
#[doc(hidden)]
pub mod arcis_type;
mod base_field;
mod box_functions;
mod enc;
mod math;
mod pack;
#[doc(hidden)]
pub mod performance;
mod rng;
mod standard_library;
#[cfg(test)]
#[doc(hidden)]
mod tests;

#[doc(hidden)]
pub use crate::arcis_type::ArcisType;
pub use crate::{base_field::*, box_functions::*, enc::*, pack::*};
use arcis_compiler::{traits::FromLeBits, ArcisField};
#[doc(hidden)]
pub use arcis_compiler::{
    utils::{
        field::BaseField,
        number::Number,
        packing::{DataSize, PackLocation},
    },
    BaseCircuitId as ArcisCircuit,
    EvalValue,
};
pub use arcis_interpreter_proc_macros::*;
use ff::{Field, PrimeField};
pub use standard_library::*;

/// Tools to test an `#[instruction]`.
///
/// See [ArcisInstructionWithInfo](struct.ArcisInstructionWithInfo.html) for an example.
pub mod testing {
    #[doc(hidden)]
    pub use crate::rng::generated_bools;
    #[doc(hidden)]
    pub use arcis_compiler::compile::{compile_and_write_all, read_ir};
    pub use arcis_instruction_with_info::*;
    #[doc(hidden)]
    pub use rand::{thread_rng, Rng};

    mod arcis_instruction_with_info;
}

/// Call `.reveal()` to tell the compiler to reveal that piece of data.
/// Cannot be called in conditional execution (inside `if` branches when the compiler does not know
/// the branch at compile-time).
pub trait Reveal {
    fn reveal(self) -> Self;
}

impl<T: ArcisType> Reveal for T {
    /// Reveals `self`.
    /// Cannot be called in conditional execution. (inside `if` branches when the compiler does not
    /// know the branch at compile-time)
    /// This performs a deep copy of `self`.
    fn reveal(self) -> Self {
        let mut v = Vec::new();
        self.handle_outputs(&mut v);
        Self::from_values(&v)
    }
}

#[doc(hidden)]
pub type ArcisCiphertext = ArcisField;

impl ArcisType for ArcisCiphertext {
    fn n_values() -> usize {
        1
    }

    fn gen_input(values: &mut Vec<EvalValue>) -> Self {
        let rng = &mut rand::thread_rng();
        let res = ArcisCiphertext::random(rng);
        values.push(EvalValue::Base(res));
        res
    }

    fn from_values(values: &[EvalValue]) -> Self {
        values[0].to_signed_number().into()
    }

    fn handle_outputs(&self, outputs: &mut Vec<EvalValue>) {
        outputs.push(EvalValue::Base(*self));
    }

    fn is_similar(&self, other: &Self) -> bool {
        self == other
    }

    fn n_bools() -> usize {
        ArcisField::NUM_BITS as usize
    }

    fn from_bools(bools: &[bool]) -> Self {
        ArcisField::from_le_bits(bools[0..Self::n_bools()].to_vec(), false)
    }

    fn data_size(acc: &mut Vec<DataSize>) {
        acc.push(DataSize::Full);
    }

    fn pack(&self, locations: &mut &[PackLocation], containers: &mut [ArcisCiphertext]) {
        let location = locations[0];
        *locations = &locations[1..];
        assert_eq!(location.bit_offset, 0, "BaseField item has a bit offset.");
        containers[location.index] = *self;
    }

    fn unpack(locations: &mut &[PackLocation], containers: &[ArcisCiphertext]) -> Self {
        let location = locations[0];
        *locations = &locations[1..];
        assert_eq!(location.bit_offset, 0, "BaseField item has a bit offset.");
        containers[location.index]
    }
}
