reed_solomon_simd/engine/
tables.rs

1//! Lookup-tables used by [`Engine`]:s.
2//!
3//! All tables are global and each is initialized at most once.
4//!
5//! # Tables
6//!
7//! | Table        | Size    | Used in encoding | Used in decoding | By engines         |
8//! | ------------ | ------- | ---------------- | ---------------- | ------------------ |
9//! | [`Exp`]      | 128 kiB | yes              | yes              | all                |
10//! | [`Log`]      | 128 kiB | yes              | yes              | all                |
11//! | [`LogWalsh`] | 128 kiB | -                | yes              | all                |
12//! | [`Mul16`]    | 8 MiB   | yes              | yes              | [`NoSimd`]         |
13//! | [`Mul128`]   | 8 MiB   | yes              | yes              | [`Avx2`] [`Ssse3`] |
14//! | [`Skew`]     | 128 kiB | yes              | yes              | all                |
15//!
16//! [`NoSimd`]: crate::engine::NoSimd
17//! [`Avx2`]: crate::engine::Avx2
18//! [`Ssse3`]: crate::engine::Ssse3
19//! [`Engine`]: crate::engine
20//!
21
22use std::sync::LazyLock;
23
24use crate::engine::{
25    fwht, utils, GfElement, CANTOR_BASIS, GF_BITS, GF_MODULUS, GF_ORDER, GF_POLYNOMIAL,
26};
27
28// ======================================================================
29// TYPE ALIASES - PUBLIC
30
31/// Used by [`Naive`] engine for multiplications
32/// and by all [`Engine`]:s to initialize other tables.
33///
34/// [`Naive`]: crate::engine::Naive
35/// [`Engine`]: crate::engine
36pub type Exp = [GfElement; GF_ORDER];
37
38/// Used by [`Naive`] engine for multiplications
39/// and by all [`Engine`]:s to initialize other tables.
40///
41/// [`Naive`]: crate::engine::Naive
42/// [`Engine`]: crate::engine
43pub type Log = [GfElement; GF_ORDER];
44
45/// Used by [`Avx2`] and [`Ssse3`] engines for multiplications.
46///
47/// [`Avx2`]: crate::engine::Avx2
48/// [`Ssse3`]: crate::engine::Ssse3
49pub type Mul128 = [Multiply128lutT; GF_ORDER];
50
51/// Elements of the Mul128 table
52#[derive(Clone, Debug)]
53pub struct Multiply128lutT {
54    /// Lower half of `GfElements`
55    pub lo: [u128; 4],
56    /// Upper half of `GfElements`
57    pub hi: [u128; 4],
58}
59
60/// Used by all [`Engine`]:s in [`Engine::eval_poly`].
61///
62/// [`Engine`]: crate::engine
63/// [`Engine::eval_poly`]: crate::engine::Engine::eval_poly
64pub type LogWalsh = [GfElement; GF_ORDER];
65
66/// Used by [`NoSimd`] engine for multiplications.
67///
68/// [`NoSimd`]: crate::engine::NoSimd
69pub type Mul16 = [[[GfElement; 16]; 4]; GF_ORDER];
70
71/// Used by all [`Engine`]:s for FFT and IFFT.
72///
73/// [`Engine`]: crate::engine
74pub type Skew = [GfElement; GF_MODULUS as usize];
75
76// ======================================================================
77// ExpLog - PUBLIC
78
79/// Struct holding the [`Exp`] and [`Log`] lookup tables.
80pub struct ExpLog {
81    /// Exponentiation table.
82    pub exp: Box<Exp>,
83    /// Logarithm table.
84    pub log: Box<Log>,
85}
86
87// ======================================================================
88// STATIC - PUBLIC
89
90/// Lazily initialized exponentiation and logarithm tables.
91pub static EXP_LOG: LazyLock<ExpLog> = LazyLock::new(initialize_exp_log);
92
93/// Lazily initialized logarithmic Walsh transform table.
94pub static LOG_WALSH: LazyLock<Box<LogWalsh>> = LazyLock::new(initialize_log_walsh);
95
96/// Lazily initialized multiplication table for the `NoSimd` engine.
97pub static MUL16: LazyLock<Box<Mul16>> = LazyLock::new(initialize_mul16);
98
99/// Lazily initialized multiplication table for SIMD engines.
100pub static MUL128: LazyLock<Box<Mul128>> = LazyLock::new(initialize_mul128);
101
102/// Lazily initialized skew table used in FFT and IFFT operations.
103pub static SKEW: LazyLock<Box<Skew>> = LazyLock::new(initialize_skew);
104
105// ======================================================================
106// FUNCTIONS - PUBLIC - math
107
108/// Calculates `x * log_m` using [`Exp`] and [`Log`] tables.
109#[inline(always)]
110pub fn mul(x: GfElement, log_m: GfElement, exp: &Exp, log: &Log) -> GfElement {
111    if x == 0 {
112        0
113    } else {
114        exp[utils::add_mod(log[x as usize], log_m) as usize]
115    }
116}
117
118// ======================================================================
119// FUNCTIONS - PRIVATE - initialize tables
120
121#[allow(clippy::needless_range_loop)]
122fn initialize_exp_log() -> ExpLog {
123    let mut exp = Box::new([0; GF_ORDER]);
124    let mut log = Box::new([0; GF_ORDER]);
125
126    // GENERATE LFSR TABLE
127
128    let mut state = 1;
129    for i in 0..GF_MODULUS {
130        exp[state] = i;
131        state <<= 1;
132        if state >= GF_ORDER {
133            state ^= GF_POLYNOMIAL;
134        }
135    }
136    exp[0] = GF_MODULUS;
137
138    // CONVERT TO CANTOR BASIS
139
140    log[0] = 0;
141    for i in 0..GF_BITS {
142        let width = 1usize << i;
143        for j in 0..width {
144            log[j + width] = log[j] ^ CANTOR_BASIS[i];
145        }
146    }
147
148    for i in 0..GF_ORDER {
149        log[i] = exp[log[i] as usize];
150    }
151
152    for i in 0..GF_ORDER {
153        exp[log[i] as usize] = i as GfElement;
154    }
155
156    exp[GF_MODULUS as usize] = exp[0];
157
158    ExpLog { exp, log }
159}
160
161fn initialize_log_walsh() -> Box<LogWalsh> {
162    let log = *EXP_LOG.log;
163
164    let mut log_walsh: Box<LogWalsh> = Box::new([0; GF_ORDER]);
165
166    log_walsh.copy_from_slice(log.as_ref());
167    log_walsh[0] = 0;
168    fwht::fwht(log_walsh.as_mut(), GF_ORDER);
169
170    log_walsh
171}
172
173fn initialize_mul16() -> Box<Mul16> {
174    let exp = &*EXP_LOG.exp;
175    let log = &*EXP_LOG.log;
176    let mut mul16 = vec![[[0; 16]; 4]; GF_ORDER];
177
178    for log_m in 0..=GF_MODULUS {
179        let lut = &mut mul16[log_m as usize];
180        for i in 0..16 {
181            lut[0][i] = mul(i as GfElement, log_m, exp, log);
182            lut[1][i] = mul((i << 4) as GfElement, log_m, exp, log);
183            lut[2][i] = mul((i << 8) as GfElement, log_m, exp, log);
184            lut[3][i] = mul((i << 12) as GfElement, log_m, exp, log);
185        }
186    }
187
188    mul16.into_boxed_slice().try_into().unwrap()
189}
190
191fn initialize_mul128() -> Box<Mul128> {
192    // Based on:
193    // https://github.com/catid/leopard/blob/22ddc7804998d31c8f1a2617ee720e063b1fa6cd/LeopardFF16.cpp#L375
194    let exp = &*EXP_LOG.exp;
195    let log = &*EXP_LOG.log;
196
197    let mut mul128 = vec![
198        Multiply128lutT {
199            lo: [0; 4],
200            hi: [0; 4],
201        };
202        GF_ORDER
203    ];
204
205    for log_m in 0..=GF_MODULUS {
206        for i in 0..=3 {
207            let mut prod_lo = [0u8; 16];
208            let mut prod_hi = [0u8; 16];
209            for x in 0..16 {
210                let prod = mul((x << (i * 4)) as GfElement, log_m, exp, log);
211                prod_lo[x] = prod as u8;
212                prod_hi[x] = (prod >> 8) as u8;
213            }
214            mul128[log_m as usize].lo[i] = u128::from_le_bytes(prod_lo);
215            mul128[log_m as usize].hi[i] = u128::from_le_bytes(prod_hi);
216        }
217    }
218
219    mul128.into_boxed_slice().try_into().unwrap()
220}
221
222#[allow(clippy::needless_range_loop)]
223fn initialize_skew() -> Box<Skew> {
224    let exp = &*EXP_LOG.exp;
225    let log = &*EXP_LOG.log;
226
227    let mut skew = Box::new([0; GF_MODULUS as usize]);
228
229    let mut temp = [0; GF_BITS - 1];
230
231    for i in 1..GF_BITS {
232        temp[i - 1] = 1 << i;
233    }
234
235    for m in 0..GF_BITS - 1 {
236        let step: usize = 1 << (m + 1);
237
238        skew[(1 << m) - 1] = 0;
239
240        for i in m..GF_BITS - 1 {
241            let s: usize = 1 << (i + 1);
242            let mut j = (1 << m) - 1;
243            while j < s {
244                skew[j + s] = skew[j] ^ temp[i];
245                j += step;
246            }
247        }
248
249        temp[m] = GF_MODULUS - log[mul(temp[m], log[(temp[m] ^ 1) as usize], exp, log) as usize];
250
251        for i in m + 1..GF_BITS - 1 {
252            let sum = utils::add_mod(log[(temp[i] ^ 1) as usize], temp[m]);
253            temp[i] = mul(temp[i], sum, exp, log);
254        }
255    }
256
257    for i in 0..GF_MODULUS as usize {
258        skew[i] = log[skew[i] as usize];
259    }
260
261    skew
262}