1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use ark_ff::Zero;
use core::{
    fmt::{Display, Formatter, Result},
    ops::{Index, IndexMut},
};
use kimchi::circuits::expr::AlphaChallengeTerm;
use serde::{Deserialize, Serialize};
use strum::EnumCount;
use strum_macros::EnumCount as EnumCountMacro;

#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, EnumCountMacro)]
pub enum ChallengeTerm {
    /// Used to aggregate the constraints describing the relation. It is used to
    /// enforce all constraints are satisfied at the same time.
    /// Often noted `α`.
    ConstraintCombiner,
    /// Both challenges used in the permutation argument
    Beta,
    Gamma,
    /// Used to homogenize the constraints and allow the protocol to fold two
    /// instances of the same relation into a new one.
    /// Often noted `u` in the paper mentioning "folding protocols".
    ConstraintHomogeniser,
    /// Used by the accumulation protocol (folding) to perform a random linear
    /// transformation of the witnesses and the public values.
    /// Often noted `r` in the paper mentioning "folding protocols".
    RelationCombiner,
}

impl Display for ChallengeTerm {
    fn fmt(&self, f: &mut Formatter) -> Result {
        match self {
            ChallengeTerm::ConstraintCombiner => write!(f, "alpha"),
            ChallengeTerm::Beta => write!(f, "beta"),
            ChallengeTerm::Gamma => write!(f, "gamma"),
            ChallengeTerm::ConstraintHomogeniser => write!(f, "u"),
            ChallengeTerm::RelationCombiner => write!(f, "r"),
        }
    }
}

pub struct Challenges<F> {
    /// Used to aggregate the constraints describing the relation. It is used to
    /// enforce all constraints are satisfied at the same time.
    /// Often noted `α`.
    pub constraint_combiner: F,

    /// Both challenges used in the permutation argument.
    pub beta: F,
    pub gamma: F,

    /// Used to homogenize the constraints and allow the protocol to fold two
    /// instances of the same relation into a new one.
    /// Often noted `u` in the paper mentioning "folding protocols".
    pub constraint_homogeniser: F,

    /// Used by the accumulation protocol (folding) to perform a random linear
    /// transformation of the witnesses and the public values.
    /// Often noted `r` in the paper mentioning "folding protocols".
    pub relation_combiner: F,
}

impl<F> Index<usize> for Challenges<F> {
    type Output = F;

    fn index(&self, index: usize) -> &Self::Output {
        if index == 0 {
            &self.constraint_combiner
        } else if index == 1 {
            &self.beta
        } else if index == 2 {
            &self.gamma
        } else if index == 3 {
            &self.constraint_homogeniser
        } else if index == 4 {
            &self.relation_combiner
        } else {
            panic!(
                "Index out of bounds, only {} are defined",
                ChallengeTerm::COUNT
            )
        }
    }
}

impl<F> IndexMut<usize> for Challenges<F> {
    fn index_mut(&mut self, index: usize) -> &mut F {
        if index == 0 {
            &mut self.constraint_combiner
        } else if index == 1 {
            &mut self.beta
        } else if index == 2 {
            &mut self.gamma
        } else if index == 3 {
            &mut self.constraint_homogeniser
        } else if index == 4 {
            &mut self.relation_combiner
        } else {
            panic!(
                "Index out of bounds, only {} are defined",
                ChallengeTerm::COUNT
            )
        }
    }
}

impl<F> IndexMut<ChallengeTerm> for Challenges<F> {
    fn index_mut(&mut self, term: ChallengeTerm) -> &mut F {
        match term {
            ChallengeTerm::ConstraintCombiner => &mut self.constraint_combiner,
            ChallengeTerm::Beta => &mut self.beta,
            ChallengeTerm::Gamma => &mut self.gamma,
            ChallengeTerm::ConstraintHomogeniser => &mut self.constraint_homogeniser,
            ChallengeTerm::RelationCombiner => &mut self.relation_combiner,
        }
    }
}

impl<F: Zero> Default for Challenges<F> {
    fn default() -> Self {
        Self {
            constraint_combiner: F::zero(),
            beta: F::zero(),
            gamma: F::zero(),
            constraint_homogeniser: F::zero(),
            relation_combiner: F::zero(),
        }
    }
}

impl<F> Index<ChallengeTerm> for Challenges<F> {
    type Output = F;

    fn index(&self, term: ChallengeTerm) -> &Self::Output {
        match term {
            ChallengeTerm::ConstraintCombiner => &self.constraint_combiner,
            ChallengeTerm::Beta => &self.beta,
            ChallengeTerm::Gamma => &self.gamma,
            ChallengeTerm::ConstraintHomogeniser => &self.constraint_homogeniser,
            ChallengeTerm::RelationCombiner => &self.relation_combiner,
        }
    }
}

impl<'a> AlphaChallengeTerm<'a> for ChallengeTerm {
    const ALPHA: Self = Self::ConstraintCombiner;
}