use crate::LinearCombination;
use ff::PrimeField;
pub mod test_cs;
pub type Constraint<Scalar> = (
    LinearCombination<Scalar>,
    LinearCombination<Scalar>,
    LinearCombination<Scalar>,
    String,
);
pub trait Comparable<Scalar: PrimeField> {
    fn num_inputs(&self) -> usize;
    fn num_constraints(&self) -> usize;
    fn inputs(&self) -> Vec<String>;
    fn aux(&self) -> Vec<String>;
    fn constraints(&self) -> &[Constraint<Scalar>];
    fn delta<C: Comparable<Scalar>>(&self, other: &C, ignore_counts: bool) -> Delta<Scalar>
    where
        Scalar: PrimeField,
    {
        let input_count_matches = self.num_inputs() == other.num_inputs();
        let constraint_count_matches = self.num_constraints() == other.num_constraints();
        let inputs_match = self.inputs() == other.inputs();
        let constraints_match = self.constraints() == other.constraints();
        let equal =
            input_count_matches && constraint_count_matches && inputs_match && constraints_match;
        if !ignore_counts && !input_count_matches {
            Delta::InputCountMismatch(self.num_inputs(), other.num_inputs())
        } else if !ignore_counts && !constraint_count_matches {
            Delta::ConstraintCountMismatch(self.num_constraints(), other.num_constraints())
        } else if !constraints_match {
            let c = self.constraints();
            let o = other.constraints();
            let mismatch = c
                .iter()
                .zip(o)
                .enumerate()
                .filter(|(_, (a, b))| a != b)
                .map(|(i, (a, b))| (i, a, b))
                .next();
            let m = mismatch.unwrap();
            Delta::ConstraintMismatch(m.0, m.1.clone(), m.2.clone())
        } else if equal {
            Delta::Equal
        } else {
            Delta::Different
        }
    }
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug, PartialEq)]
pub enum Delta<Scalar: PrimeField> {
    Equal,
    Different,
    InputCountMismatch(usize, usize),
    ConstraintCountMismatch(usize, usize),
    ConstraintMismatch(usize, Constraint<Scalar>, Constraint<Scalar>),
}