use crate::{
    core::{
        actually_used_field::ActuallyUsedField,
        bounds::FieldBounds,
        circuits::traits::arithmetic_circuit::ArithmeticCircuit,
        expressions::expr::EvalFailure,
        global_value::value::FieldValue,
    },
    utils::used_field::UsedField,
};

/// A simple circuit which accepts any kind of inputs (number and bounds),
/// which is convenient for the compilation tests.
/// Not really meant to be useful.
#[derive(Clone, Debug)]
#[allow(dead_code)]
pub struct SumCircuit {}

impl<F: UsedField> ArithmeticCircuit<F> for SumCircuit {
    fn eval(&self, x: Vec<F>) -> Result<Vec<F>, EvalFailure> {
        Ok(vec![x.into_iter().sum::<F>()])
    }

    fn bounds(&self, bounds: Vec<FieldBounds<F>>) -> Vec<FieldBounds<F>> {
        vec![bounds
            .into_iter()
            .reduce(|x, x1| x + x1)
            .unwrap_or_else(|| FieldBounds::from(F::ZERO))]
    }

    fn run(&self, vals: Vec<FieldValue<F>>) -> Vec<FieldValue<F>>
    where
        F: ActuallyUsedField,
    {
        let mut value: FieldValue<F> = 0.into();
        for val in vals {
            value += val;
        }
        vec![value]
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{
        core::circuits::traits::arithmetic_circuit::tests::TestedArithmeticCircuit,
        utils::field::ScalarField,
    };
    use rand::Rng;
    use std::marker::PhantomData;

    impl<F: ActuallyUsedField> TestedArithmeticCircuit<F> for SumCircuit {
        fn gen_desc<R: Rng + ?Sized>(_rng: &mut R) -> Self {
            SumCircuit {}
        }

        fn gen_n_inputs<R: Rng + ?Sized>(&self, rng: &mut R) -> usize {
            rng.next_u64().trailing_zeros() as usize
        }

        fn extra_checks(&self, inputs: Vec<F>, outputs: Vec<F>) {
            if inputs.is_empty() {
                assert_eq!(outputs[0], F::ZERO);
            }
        }
    }
    #[test]
    fn tested() {
        SumCircuit::test_with_marker(16, 16, PhantomData::<ScalarField>)
    }
}
