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
use ark_ff::{Field, One, Zero};
use kimchi_msm::{Logup, LookupTableID};

/// Enum representing the two different modes of a RAMLookup
#[derive(Copy, Clone, Debug)]
pub enum LookupMode {
    Read,
    Write,
}

/// Struct containing a RAMLookup
#[derive(Clone, Debug)]
pub struct RAMLookup<T, ID: LookupTableID> {
    /// The table ID corresponding to this lookup
    pub(crate) table_id: ID,
    /// Whether it is a read or write lookup
    pub(crate) mode: LookupMode,
    /// The number of times that this lookup value should be added to / subtracted from the lookup accumulator.
    pub(crate) magnitude: T,
    /// The columns containing the content of this lookup
    pub(crate) value: Vec<T>,
}

impl<T, ID> RAMLookup<T, ID>
where
    T: Clone
        + std::ops::Add<T, Output = T>
        + std::ops::Sub<T, Output = T>
        + std::ops::Mul<T, Output = T>
        + std::fmt::Debug
        + One
        + Zero,
    ID: LookupTableID,
{
    /// Creates a new RAMLookup from a mode, a table ID, a magnitude, and a value
    pub fn new(mode: LookupMode, table_id: ID, magnitude: T, value: &[T]) -> Self {
        Self {
            mode,
            table_id,
            magnitude,
            value: value.to_vec(),
        }
    }

    /// Returns the numerator corresponding to this lookup in the Logup argument
    pub fn numerator(&self) -> T {
        match self.mode {
            LookupMode::Read => T::zero() - self.magnitude.clone(),
            LookupMode::Write => self.magnitude.clone(),
        }
    }

    /// Transforms the current RAMLookup into an equivalent Logup
    pub fn into_logup(self) -> Logup<T, ID> {
        Logup::new(self.table_id, self.numerator(), &self.value)
    }

    /// Reads one value when `if_is_true` is 1.
    pub fn read_if(if_is_true: T, table_id: ID, value: Vec<T>) -> Self {
        Self {
            mode: LookupMode::Read,
            magnitude: if_is_true,
            table_id,
            value,
        }
    }

    /// Writes one value when `if_is_true` is 1.
    pub fn write_if(if_is_true: T, table_id: ID, value: Vec<T>) -> Self {
        Self {
            mode: LookupMode::Write,
            magnitude: if_is_true,
            table_id,
            value,
        }
    }

    /// Reads one value from a table.
    pub fn read_one(table_id: ID, value: Vec<T>) -> Self {
        Self {
            mode: LookupMode::Read,
            magnitude: T::one(),
            table_id,
            value,
        }
    }

    /// Writes one value to a table.
    pub fn write_one(table_id: ID, value: Vec<T>) -> Self {
        Self {
            mode: LookupMode::Write,
            magnitude: T::one(),
            table_id,
            value,
        }
    }
}

impl<F: std::fmt::Display + Field, ID: LookupTableID> std::fmt::Display for RAMLookup<F, ID> {
    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let numerator = match self.mode {
            LookupMode::Read => -self.magnitude,
            LookupMode::Write => self.magnitude,
        };
        write!(
            formatter,
            "numerator: {}\ntable_id: {:?}\nvalue:\n[\n",
            numerator,
            self.table_id.to_field::<F>()
        )?;
        for value in self.value.iter() {
            writeln!(formatter, "\t{}", value)?;
        }
        write!(formatter, "]")?;
        Ok(())
    }
}