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

mod arcis_macros;
#[doc(hidden)]
pub mod arcis_type;
mod enc;
mod pack;
#[doc(hidden)]
pub mod performance;
mod rng;
mod standard_library;

#[doc(hidden)]
pub use crate::arcis_type::ArcisType;
pub use crate::{enc::*, pack::*};
use arcis_compiler::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;
pub use standard_library::*;

#[doc(hidden)]
pub mod testing {
    pub use crate::rng::generated_bools;
    pub use arcis_compiler::compile::read_ir;
    pub use rand::{thread_rng, Rng};
}

/// 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 {
        panic!("Cannot generate random BaseFields.")
    }

    fn from_bools(_bools: &[bool]) -> Self {
        panic!("Cannot generate random BaseFields.")
    }

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

    fn pack(&self, locations: &mut &[PackLocation], containers: &mut [BaseField]) {
        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: &[BaseField]) -> Self {
        let location = locations[0];
        *locations = &locations[1..];
        assert_eq!(location.bit_offset, 0, "BaseField item has a bit offset.");
        containers[location.index]
    }
}
