Skip to main content

RingContext

Struct RingContext 

Source
pub struct RingContext { /* private fields */ }
Expand description

Runtime ring context that carries validated Params for operations in R_q = Z_q[x] / (f(x)).

Implementations§

Source§

impl RingContext

Source

pub fn new(params: Params) -> Self

Creates a context from already-validated Params.

Source

pub fn from_parts( max_degree: usize, modulus: u64, modulus_poly_coeffs: &[u64], primitive_root: u64, ) -> Result<Self, ParamsError>

Builds and validates parameters, then returns a context.

Examples found in repository?
examples/rlwe_pke.rs (line 97)
90fn main() {
91    // Ring modulus polynomial `x^32 + 1`.
92    let mut modulus_poly = vec![0_u64; 32 + 1];
93    modulus_poly[0] = 1;
94    modulus_poly[32] = 1;
95    // Standard NTT-friendly prime modulus used elsewhere in the crate examples.
96    let ctx =
97        RingContext::from_parts(32, 998_244_353, &modulus_poly, 3).expect("context should build");
98
99    // Key generation randomness.
100    let mut key_rng = ExampleRng::new(0xDEAD_BEEF_1234_0001);
101    // Generates:
102    // - public key (a, b = a*s + e)
103    // - secret key (s)
104    let (public_key, secret_key) =
105        keygen_example(&ctx, 2, &mut key_rng).expect("keygen should work");
106
107    // Message payload.
108    let message = b"ok";
109    // Encryption randomness must be independent from keygen randomness.
110    let mut enc_rng = ExampleRng::new(0xFACE_CAFE_2222_0002);
111    // Produces ciphertext (u, v).
112    let ciphertext =
113        encrypt_example(&ctx, &public_key, message, 2, &mut enc_rng).expect("encrypt should work");
114
115    // Decrypt and verify.
116    let decrypted = decrypt_example(&ctx, &secret_key, &ciphertext, message.len())
117        .expect("decrypt should work");
118    assert_eq!(decrypted, message);
119
120    // Human-readable output for quick manual check.
121    println!("message={:?}", String::from_utf8_lossy(message));
122    println!("decrypted={:?}", String::from_utf8_lossy(&decrypted));
123}
More examples
Hide additional examples
examples/helpers_codec.rs (line 49)
42fn main() {
43    // Use a power-of-two cyclotomic-style modulus polynomial `x^32 + 1`.
44    let mut modulus_poly = vec![0_u64; 32 + 1];
45    modulus_poly[0] = 1;
46    modulus_poly[32] = 1;
47    // Create validated context (n=32, q=998244353, primitive root=3).
48    let ctx =
49        RingContext::from_parts(32, 998_244_353, &modulus_poly, 3).expect("context should build");
50
51    // Message to encode. Each bit maps into one coefficient slot.
52    let message = b"Rust";
53    // Encode bytes -> ring element with coefficients in `{0, floor((q+1)/2)}`.
54    let encoded =
55        encode_message_scaled_bits_example(&ctx, message).expect("message encoding should work");
56    // Decode ring element -> bytes (threshold decoding in coefficient domain).
57    let decoded = decode_message_scaled_bits_example(&encoded, message.len())
58        .expect("message decoding should work");
59    // End-to-end correctness check.
60    assert_eq!(decoded, message);
61
62    // Quantize coefficients to 11-bit buckets.
63    let compressed = compress_ring_elem_example(&encoded, 11).expect("compression should work");
64    // Lift quantized coefficients back into `Z_q`.
65    let decompressed =
66        decompress_ring_elem_example(&ctx, &compressed, 11).expect("decompression should work");
67
68    // Print summary values to inspect behavior quickly.
69    println!("decoded_message={:?}", String::from_utf8_lossy(&decoded));
70    println!("compressed_coeffs={}", compressed.len());
71    println!("decompressed_degree={:?}", decompressed.degree());
72}
examples/helpers_sampling.rs (line 111)
99fn main() {
100    // Configure modulus polynomial `x^16 + 1`.
101    //
102    // We allocate `max_degree + 1` coefficients and set:
103    // - constant term: 1
104    // - x^16 term: 1
105    // - all intermediate terms: 0
106    let mut modulus_poly = vec![0_u64; 16 + 1];
107    modulus_poly[0] = 1;
108    modulus_poly[16] = 1;
109    // Build validated ring context with NTT-friendly modulus and primitive root.
110    let ctx =
111        RingContext::from_parts(16, 998_244_353, &modulus_poly, 3).expect("context should build");
112
113    // Deterministic seed makes this executable reproducible.
114    let mut rng = ExampleRng::new(0xA11C_E123_0000_0001);
115
116    // Uniform sample in `R_q`: every coefficient is sampled independently in `[0, q)`.
117    let uniform =
118        sample_uniform_poly_example(&ctx, &mut rng).expect("uniform sampling should work");
119    // Centered-binomial sample: each coefficient is small noise around 0.
120    let cbd = sample_cbd_poly_example(&ctx, 3, &mut rng).expect("CBD sampling should work");
121    // Gaussian-like sample from Box-Muller + integer rounding.
122    let gaussian =
123        sample_discrete_gaussian_poly_example(&ctx, 2.5, &mut rng).expect("gaussian should work");
124    // Raw integer rejection sample in `[0, 1000)`.
125    let below = sample_rejection_u64_below_example(1_000, &mut rng).expect("rejection should work");
126
127    // Print concise observable properties so this can be used as a smoke run.
128    println!("uniform_degree={:?}", uniform.degree());
129    println!("cbd_degree={:?}", cbd.degree());
130    println!("gaussian_degree={:?}", gaussian.degree());
131    println!("sample_below_1000={below}");
132}
Source

pub fn params(&self) -> &Params

Returns validated parameters.

Examples found in repository?
examples/common/pke.rs (line 181)
176fn ensure_element_matches_context(
177    ctx: &RingContext,
178    element: &RingElem,
179) -> Result<(), ExamplePkeError> {
180    // Structural parameter equality check.
181    if element.params() != ctx.params() {
182        return Err(ExamplePkeError::ParameterMismatch);
183    }
184    Ok(())
185}
Source

pub fn max_degree(&self) -> usize

Maximum supported polynomial degree.

Examples found in repository?
examples/common/sampling.rs (line 101)
97pub fn sample_uniform_poly_example<R: CryptoRng>(
98    ctx: &RingContext,
99    rng: &mut R,
100) -> Result<RingElem, SamplingError> {
101    let mut coeffs = vec![0_u64; ctx.max_degree() + 1];
102    for coeff in &mut coeffs {
103        // Independent uniform sampling for each coefficient.
104        *coeff = sample_rejection_u64_below_example(ctx.modulus(), rng)?;
105    }
106    // `ctx.element(...)` canonicalizes modulo the ring polynomial.
107    Ok(ctx.element(&coeffs)?)
108}
109
110/// Samples one polynomial with centered-binomial noise of parameter `eta`.
111///
112/// Each coefficient is generated as `sum(a_i) - sum(b_i)` over `eta` Bernoulli bits,
113/// then mapped into `Z_q`.
114pub fn sample_cbd_poly_example<R: CryptoRng>(
115    ctx: &RingContext,
116    eta: u8,
117    rng: &mut R,
118) -> Result<RingElem, SamplingError> {
119    if eta == 0 {
120        return Err(SamplingError::InvalidEta);
121    }
122
123    let mut coeffs = vec![0_u64; ctx.max_degree() + 1];
124    for coeff in &mut coeffs {
125        // CBD(eta): difference between two eta-bit Hamming weights.
126        let mut a = 0_i64;
127        let mut b = 0_i64;
128        for _ in 0..eta {
129            a += (rng.next_u32() & 1) as i64;
130            b += (rng.next_u32() & 1) as i64;
131        }
132        // Map signed noise into `[0, q)` representation.
133        *coeff = wrap_signed_to_modulus(a - b, ctx.modulus());
134    }
135
136    Ok(ctx.element(&coeffs)?)
137}
138
139/// Samples one polynomial with approximate discrete Gaussian coefficients.
140///
141/// This example helper uses a Box-Muller normal sampler and rounds to nearest integer.
142pub fn sample_discrete_gaussian_poly_example<R: CryptoRng>(
143    ctx: &RingContext,
144    sigma: f64,
145    rng: &mut R,
146) -> Result<RingElem, SamplingError> {
147    if !(sigma.is_finite() && sigma > 0.0) {
148        return Err(SamplingError::InvalidSigma);
149    }
150
151    let mut coeffs = vec![0_u64; ctx.max_degree() + 1];
152    for coeff in &mut coeffs {
153        // Sample one integer noise value and wrap it into `Z_q`.
154        let sampled = sample_box_muller_integer(rng, sigma);
155        *coeff = wrap_signed_to_modulus(sampled, ctx.modulus());
156    }
157
158    Ok(ctx.element(&coeffs)?)
159}
More examples
Hide additional examples
examples/common/codec.rs (line 100)
94pub fn encode_message_scaled_bits_example(
95    ctx: &RingContext,
96    message: &[u8],
97) -> Result<RingElem, CodecError> {
98    // In quotient rings like `Z_q[x]/(x^n+1)`, we treat `n` as the usable bit capacity.
99    // Coefficient index `i` stores bit `i`.
100    let capacity_bits = ctx.max_degree();
101    let message_bits = message.len().saturating_mul(8);
102    if message_bits > capacity_bits {
103        return Err(CodecError::MessageTooLong {
104            message_bits,
105            capacity_bits,
106        });
107    }
108
109    // Allocate full polynomial width (`n + 1` slots in this crate's dense representation).
110    let mut coeffs = vec![0_u64; ctx.max_degree() + 1];
111    // Encoding for bit=1 is midpoint-ish so threshold decoding is robust to moderate noise.
112    let one_value = (ctx.modulus() + 1) / 2;
113
114    // Little-endian bit order per byte:
115    // - bit 0 goes to lower coefficient index,
116    // - bit 7 goes to higher coefficient index.
117    for (byte_idx, &byte) in message.iter().enumerate() {
118        for bit_idx in 0..8 {
119            if (byte >> bit_idx) & 1 == 1 {
120                coeffs[byte_idx * 8 + bit_idx] = one_value;
121            }
122        }
123    }
124
125    // Canonicalize into ring representation.
126    Ok(ctx.element(&coeffs)?)
127}
128
129/// Decodes bytes from a scaled-bit ring element using quarter-range thresholding.
130///
131/// Coefficients in `[q/4, 3q/4)` decode to bit `1`, and the rest decode to `0`.
132pub fn decode_message_scaled_bits_example(
133    element: &RingElem,
134    output_len_bytes: usize,
135) -> Result<Vec<u8>, CodecError> {
136    // Decode limit follows the same bit-capacity convention used at encode time.
137    let capacity_bits = element.params().max_degree();
138    let requested_bits = output_len_bytes.saturating_mul(8);
139    if requested_bits > capacity_bits {
140        return Err(CodecError::DecodeLengthTooLong {
141            requested_bits,
142            capacity_bits,
143        });
144    }
145
146    let q = element.params().modulus();
147    // Threshold window near middle of modulus range.
148    // Values in `[q/4, 3q/4)` decode to 1; others decode to 0.
149    let lower = q / 4;
150    let upper = (3 * q) / 4;
151
152    let mut out = vec![0_u8; output_len_bytes];
153    for bit_index in 0..requested_bits {
154        let coeff = element.coefficients()[bit_index];
155        let bit = coeff >= lower && coeff < upper;
156        if bit {
157            // Restore little-endian bit position.
158            out[bit_index / 8] |= 1_u8 << (bit_index % 8);
159        }
160    }
161
162    Ok(out)
163}
164
165/// Compresses one coefficient into `bits` bits.
166pub fn compress_coefficient_example(value: u64, modulus: u64, bits: u8) -> Result<u16, CodecError> {
167    validate_compression_bits(bits)?;
168
169    // Quantization levels = 2^bits.
170    let levels = 1_u128 << bits;
171    let q = modulus as u128;
172    let v = (value % modulus) as u128;
173
174    // Rounded scaling from `[0, q)` into `[0, 2^bits)`.
175    let compressed = ((v * levels + (q / 2)) / q) % levels;
176    Ok(compressed as u16)
177}
178
179/// Decompresses one `bits`-bit coefficient back into `Z_q`.
180pub fn decompress_coefficient_example(
181    compressed: u16,
182    modulus: u64,
183    bits: u8,
184) -> Result<u64, CodecError> {
185    validate_compression_bits(bits)?;
186
187    let levels = 1_u128 << bits;
188    let q = modulus as u128;
189    let c = (compressed as u128) % levels;
190
191    // Rounded inverse scaling from `[0, 2^bits)` back into `[0, q)`.
192    let decompressed = (c * q + (levels / 2)) / levels;
193    Ok((decompressed % q) as u64)
194}
195
196/// Compresses all coefficients of a ring element into `bits` bits each.
197pub fn compress_ring_elem_example(element: &RingElem, bits: u8) -> Result<Vec<u16>, CodecError> {
198    validate_compression_bits(bits)?;
199
200    let q = element.params().modulus();
201    // Compress each coefficient independently.
202    let mut packed = Vec::with_capacity(element.coefficients().len());
203    for &coeff in element.coefficients() {
204        packed.push(compress_coefficient_example(coeff, q, bits)?);
205    }
206    Ok(packed)
207}
208
209/// Decompresses packed coefficients into a canonical ring element.
210pub fn decompress_ring_elem_example(
211    ctx: &RingContext,
212    packed: &[u16],
213    bits: u8,
214) -> Result<RingElem, CodecError> {
215    validate_compression_bits(bits)?;
216
217    // Packed vector must match dense polynomial width.
218    let expected = ctx.max_degree() + 1;
219    if packed.len() != expected {
220        return Err(CodecError::CompressedLengthMismatch {
221            expected,
222            actual: packed.len(),
223        });
224    }
225
226    // Decompress each coefficient independently and rebuild ring element.
227    let mut coeffs = vec![0_u64; expected];
228    for (index, &coeff) in packed.iter().enumerate() {
229        coeffs[index] = decompress_coefficient_example(coeff, ctx.modulus(), bits)?;
230    }
231
232    Ok(ctx.element(&coeffs)?)
233}
Source

pub fn modulus(&self) -> u64

Coefficient modulus q.

Examples found in repository?
examples/common/sampling.rs (line 104)
97pub fn sample_uniform_poly_example<R: CryptoRng>(
98    ctx: &RingContext,
99    rng: &mut R,
100) -> Result<RingElem, SamplingError> {
101    let mut coeffs = vec![0_u64; ctx.max_degree() + 1];
102    for coeff in &mut coeffs {
103        // Independent uniform sampling for each coefficient.
104        *coeff = sample_rejection_u64_below_example(ctx.modulus(), rng)?;
105    }
106    // `ctx.element(...)` canonicalizes modulo the ring polynomial.
107    Ok(ctx.element(&coeffs)?)
108}
109
110/// Samples one polynomial with centered-binomial noise of parameter `eta`.
111///
112/// Each coefficient is generated as `sum(a_i) - sum(b_i)` over `eta` Bernoulli bits,
113/// then mapped into `Z_q`.
114pub fn sample_cbd_poly_example<R: CryptoRng>(
115    ctx: &RingContext,
116    eta: u8,
117    rng: &mut R,
118) -> Result<RingElem, SamplingError> {
119    if eta == 0 {
120        return Err(SamplingError::InvalidEta);
121    }
122
123    let mut coeffs = vec![0_u64; ctx.max_degree() + 1];
124    for coeff in &mut coeffs {
125        // CBD(eta): difference between two eta-bit Hamming weights.
126        let mut a = 0_i64;
127        let mut b = 0_i64;
128        for _ in 0..eta {
129            a += (rng.next_u32() & 1) as i64;
130            b += (rng.next_u32() & 1) as i64;
131        }
132        // Map signed noise into `[0, q)` representation.
133        *coeff = wrap_signed_to_modulus(a - b, ctx.modulus());
134    }
135
136    Ok(ctx.element(&coeffs)?)
137}
138
139/// Samples one polynomial with approximate discrete Gaussian coefficients.
140///
141/// This example helper uses a Box-Muller normal sampler and rounds to nearest integer.
142pub fn sample_discrete_gaussian_poly_example<R: CryptoRng>(
143    ctx: &RingContext,
144    sigma: f64,
145    rng: &mut R,
146) -> Result<RingElem, SamplingError> {
147    if !(sigma.is_finite() && sigma > 0.0) {
148        return Err(SamplingError::InvalidSigma);
149    }
150
151    let mut coeffs = vec![0_u64; ctx.max_degree() + 1];
152    for coeff in &mut coeffs {
153        // Sample one integer noise value and wrap it into `Z_q`.
154        let sampled = sample_box_muller_integer(rng, sigma);
155        *coeff = wrap_signed_to_modulus(sampled, ctx.modulus());
156    }
157
158    Ok(ctx.element(&coeffs)?)
159}
More examples
Hide additional examples
examples/common/codec.rs (line 112)
94pub fn encode_message_scaled_bits_example(
95    ctx: &RingContext,
96    message: &[u8],
97) -> Result<RingElem, CodecError> {
98    // In quotient rings like `Z_q[x]/(x^n+1)`, we treat `n` as the usable bit capacity.
99    // Coefficient index `i` stores bit `i`.
100    let capacity_bits = ctx.max_degree();
101    let message_bits = message.len().saturating_mul(8);
102    if message_bits > capacity_bits {
103        return Err(CodecError::MessageTooLong {
104            message_bits,
105            capacity_bits,
106        });
107    }
108
109    // Allocate full polynomial width (`n + 1` slots in this crate's dense representation).
110    let mut coeffs = vec![0_u64; ctx.max_degree() + 1];
111    // Encoding for bit=1 is midpoint-ish so threshold decoding is robust to moderate noise.
112    let one_value = (ctx.modulus() + 1) / 2;
113
114    // Little-endian bit order per byte:
115    // - bit 0 goes to lower coefficient index,
116    // - bit 7 goes to higher coefficient index.
117    for (byte_idx, &byte) in message.iter().enumerate() {
118        for bit_idx in 0..8 {
119            if (byte >> bit_idx) & 1 == 1 {
120                coeffs[byte_idx * 8 + bit_idx] = one_value;
121            }
122        }
123    }
124
125    // Canonicalize into ring representation.
126    Ok(ctx.element(&coeffs)?)
127}
128
129/// Decodes bytes from a scaled-bit ring element using quarter-range thresholding.
130///
131/// Coefficients in `[q/4, 3q/4)` decode to bit `1`, and the rest decode to `0`.
132pub fn decode_message_scaled_bits_example(
133    element: &RingElem,
134    output_len_bytes: usize,
135) -> Result<Vec<u8>, CodecError> {
136    // Decode limit follows the same bit-capacity convention used at encode time.
137    let capacity_bits = element.params().max_degree();
138    let requested_bits = output_len_bytes.saturating_mul(8);
139    if requested_bits > capacity_bits {
140        return Err(CodecError::DecodeLengthTooLong {
141            requested_bits,
142            capacity_bits,
143        });
144    }
145
146    let q = element.params().modulus();
147    // Threshold window near middle of modulus range.
148    // Values in `[q/4, 3q/4)` decode to 1; others decode to 0.
149    let lower = q / 4;
150    let upper = (3 * q) / 4;
151
152    let mut out = vec![0_u8; output_len_bytes];
153    for bit_index in 0..requested_bits {
154        let coeff = element.coefficients()[bit_index];
155        let bit = coeff >= lower && coeff < upper;
156        if bit {
157            // Restore little-endian bit position.
158            out[bit_index / 8] |= 1_u8 << (bit_index % 8);
159        }
160    }
161
162    Ok(out)
163}
164
165/// Compresses one coefficient into `bits` bits.
166pub fn compress_coefficient_example(value: u64, modulus: u64, bits: u8) -> Result<u16, CodecError> {
167    validate_compression_bits(bits)?;
168
169    // Quantization levels = 2^bits.
170    let levels = 1_u128 << bits;
171    let q = modulus as u128;
172    let v = (value % modulus) as u128;
173
174    // Rounded scaling from `[0, q)` into `[0, 2^bits)`.
175    let compressed = ((v * levels + (q / 2)) / q) % levels;
176    Ok(compressed as u16)
177}
178
179/// Decompresses one `bits`-bit coefficient back into `Z_q`.
180pub fn decompress_coefficient_example(
181    compressed: u16,
182    modulus: u64,
183    bits: u8,
184) -> Result<u64, CodecError> {
185    validate_compression_bits(bits)?;
186
187    let levels = 1_u128 << bits;
188    let q = modulus as u128;
189    let c = (compressed as u128) % levels;
190
191    // Rounded inverse scaling from `[0, 2^bits)` back into `[0, q)`.
192    let decompressed = (c * q + (levels / 2)) / levels;
193    Ok((decompressed % q) as u64)
194}
195
196/// Compresses all coefficients of a ring element into `bits` bits each.
197pub fn compress_ring_elem_example(element: &RingElem, bits: u8) -> Result<Vec<u16>, CodecError> {
198    validate_compression_bits(bits)?;
199
200    let q = element.params().modulus();
201    // Compress each coefficient independently.
202    let mut packed = Vec::with_capacity(element.coefficients().len());
203    for &coeff in element.coefficients() {
204        packed.push(compress_coefficient_example(coeff, q, bits)?);
205    }
206    Ok(packed)
207}
208
209/// Decompresses packed coefficients into a canonical ring element.
210pub fn decompress_ring_elem_example(
211    ctx: &RingContext,
212    packed: &[u16],
213    bits: u8,
214) -> Result<RingElem, CodecError> {
215    validate_compression_bits(bits)?;
216
217    // Packed vector must match dense polynomial width.
218    let expected = ctx.max_degree() + 1;
219    if packed.len() != expected {
220        return Err(CodecError::CompressedLengthMismatch {
221            expected,
222            actual: packed.len(),
223        });
224    }
225
226    // Decompress each coefficient independently and rebuild ring element.
227    let mut coeffs = vec![0_u64; expected];
228    for (index, &coeff) in packed.iter().enumerate() {
229        coeffs[index] = decompress_coefficient_example(coeff, ctx.modulus(), bits)?;
230    }
231
232    Ok(ctx.element(&coeffs)?)
233}
Source

pub fn modulus_poly(&self) -> &Polynomial

Ring modulus polynomial f(x).

Source

pub fn primitive_root(&self) -> u64

Primitive root used by NTT-based operations.

Source

pub fn ntt_length(&self) -> usize

Derived NTT transform length.

Source

pub fn polynomial(&self, coeffs: &[u64]) -> Result<Polynomial, PolynomialError>

Builds a polynomial compatible with this context.

Source

pub fn zero(&self) -> Polynomial

Returns additive identity in this ring.

Source

pub fn one(&self) -> Polynomial

Returns multiplicative identity in this ring.

Source

pub fn element(&self, coeffs: &[u64]) -> Result<RingElem, PolynomialError>

Builds a canonical ring element from coefficients by reducing modulo f(x).

Examples found in repository?
examples/common/sampling.rs (line 107)
97pub fn sample_uniform_poly_example<R: CryptoRng>(
98    ctx: &RingContext,
99    rng: &mut R,
100) -> Result<RingElem, SamplingError> {
101    let mut coeffs = vec![0_u64; ctx.max_degree() + 1];
102    for coeff in &mut coeffs {
103        // Independent uniform sampling for each coefficient.
104        *coeff = sample_rejection_u64_below_example(ctx.modulus(), rng)?;
105    }
106    // `ctx.element(...)` canonicalizes modulo the ring polynomial.
107    Ok(ctx.element(&coeffs)?)
108}
109
110/// Samples one polynomial with centered-binomial noise of parameter `eta`.
111///
112/// Each coefficient is generated as `sum(a_i) - sum(b_i)` over `eta` Bernoulli bits,
113/// then mapped into `Z_q`.
114pub fn sample_cbd_poly_example<R: CryptoRng>(
115    ctx: &RingContext,
116    eta: u8,
117    rng: &mut R,
118) -> Result<RingElem, SamplingError> {
119    if eta == 0 {
120        return Err(SamplingError::InvalidEta);
121    }
122
123    let mut coeffs = vec![0_u64; ctx.max_degree() + 1];
124    for coeff in &mut coeffs {
125        // CBD(eta): difference between two eta-bit Hamming weights.
126        let mut a = 0_i64;
127        let mut b = 0_i64;
128        for _ in 0..eta {
129            a += (rng.next_u32() & 1) as i64;
130            b += (rng.next_u32() & 1) as i64;
131        }
132        // Map signed noise into `[0, q)` representation.
133        *coeff = wrap_signed_to_modulus(a - b, ctx.modulus());
134    }
135
136    Ok(ctx.element(&coeffs)?)
137}
138
139/// Samples one polynomial with approximate discrete Gaussian coefficients.
140///
141/// This example helper uses a Box-Muller normal sampler and rounds to nearest integer.
142pub fn sample_discrete_gaussian_poly_example<R: CryptoRng>(
143    ctx: &RingContext,
144    sigma: f64,
145    rng: &mut R,
146) -> Result<RingElem, SamplingError> {
147    if !(sigma.is_finite() && sigma > 0.0) {
148        return Err(SamplingError::InvalidSigma);
149    }
150
151    let mut coeffs = vec![0_u64; ctx.max_degree() + 1];
152    for coeff in &mut coeffs {
153        // Sample one integer noise value and wrap it into `Z_q`.
154        let sampled = sample_box_muller_integer(rng, sigma);
155        *coeff = wrap_signed_to_modulus(sampled, ctx.modulus());
156    }
157
158    Ok(ctx.element(&coeffs)?)
159}
More examples
Hide additional examples
examples/common/codec.rs (line 126)
94pub fn encode_message_scaled_bits_example(
95    ctx: &RingContext,
96    message: &[u8],
97) -> Result<RingElem, CodecError> {
98    // In quotient rings like `Z_q[x]/(x^n+1)`, we treat `n` as the usable bit capacity.
99    // Coefficient index `i` stores bit `i`.
100    let capacity_bits = ctx.max_degree();
101    let message_bits = message.len().saturating_mul(8);
102    if message_bits > capacity_bits {
103        return Err(CodecError::MessageTooLong {
104            message_bits,
105            capacity_bits,
106        });
107    }
108
109    // Allocate full polynomial width (`n + 1` slots in this crate's dense representation).
110    let mut coeffs = vec![0_u64; ctx.max_degree() + 1];
111    // Encoding for bit=1 is midpoint-ish so threshold decoding is robust to moderate noise.
112    let one_value = (ctx.modulus() + 1) / 2;
113
114    // Little-endian bit order per byte:
115    // - bit 0 goes to lower coefficient index,
116    // - bit 7 goes to higher coefficient index.
117    for (byte_idx, &byte) in message.iter().enumerate() {
118        for bit_idx in 0..8 {
119            if (byte >> bit_idx) & 1 == 1 {
120                coeffs[byte_idx * 8 + bit_idx] = one_value;
121            }
122        }
123    }
124
125    // Canonicalize into ring representation.
126    Ok(ctx.element(&coeffs)?)
127}
128
129/// Decodes bytes from a scaled-bit ring element using quarter-range thresholding.
130///
131/// Coefficients in `[q/4, 3q/4)` decode to bit `1`, and the rest decode to `0`.
132pub fn decode_message_scaled_bits_example(
133    element: &RingElem,
134    output_len_bytes: usize,
135) -> Result<Vec<u8>, CodecError> {
136    // Decode limit follows the same bit-capacity convention used at encode time.
137    let capacity_bits = element.params().max_degree();
138    let requested_bits = output_len_bytes.saturating_mul(8);
139    if requested_bits > capacity_bits {
140        return Err(CodecError::DecodeLengthTooLong {
141            requested_bits,
142            capacity_bits,
143        });
144    }
145
146    let q = element.params().modulus();
147    // Threshold window near middle of modulus range.
148    // Values in `[q/4, 3q/4)` decode to 1; others decode to 0.
149    let lower = q / 4;
150    let upper = (3 * q) / 4;
151
152    let mut out = vec![0_u8; output_len_bytes];
153    for bit_index in 0..requested_bits {
154        let coeff = element.coefficients()[bit_index];
155        let bit = coeff >= lower && coeff < upper;
156        if bit {
157            // Restore little-endian bit position.
158            out[bit_index / 8] |= 1_u8 << (bit_index % 8);
159        }
160    }
161
162    Ok(out)
163}
164
165/// Compresses one coefficient into `bits` bits.
166pub fn compress_coefficient_example(value: u64, modulus: u64, bits: u8) -> Result<u16, CodecError> {
167    validate_compression_bits(bits)?;
168
169    // Quantization levels = 2^bits.
170    let levels = 1_u128 << bits;
171    let q = modulus as u128;
172    let v = (value % modulus) as u128;
173
174    // Rounded scaling from `[0, q)` into `[0, 2^bits)`.
175    let compressed = ((v * levels + (q / 2)) / q) % levels;
176    Ok(compressed as u16)
177}
178
179/// Decompresses one `bits`-bit coefficient back into `Z_q`.
180pub fn decompress_coefficient_example(
181    compressed: u16,
182    modulus: u64,
183    bits: u8,
184) -> Result<u64, CodecError> {
185    validate_compression_bits(bits)?;
186
187    let levels = 1_u128 << bits;
188    let q = modulus as u128;
189    let c = (compressed as u128) % levels;
190
191    // Rounded inverse scaling from `[0, 2^bits)` back into `[0, q)`.
192    let decompressed = (c * q + (levels / 2)) / levels;
193    Ok((decompressed % q) as u64)
194}
195
196/// Compresses all coefficients of a ring element into `bits` bits each.
197pub fn compress_ring_elem_example(element: &RingElem, bits: u8) -> Result<Vec<u16>, CodecError> {
198    validate_compression_bits(bits)?;
199
200    let q = element.params().modulus();
201    // Compress each coefficient independently.
202    let mut packed = Vec::with_capacity(element.coefficients().len());
203    for &coeff in element.coefficients() {
204        packed.push(compress_coefficient_example(coeff, q, bits)?);
205    }
206    Ok(packed)
207}
208
209/// Decompresses packed coefficients into a canonical ring element.
210pub fn decompress_ring_elem_example(
211    ctx: &RingContext,
212    packed: &[u16],
213    bits: u8,
214) -> Result<RingElem, CodecError> {
215    validate_compression_bits(bits)?;
216
217    // Packed vector must match dense polynomial width.
218    let expected = ctx.max_degree() + 1;
219    if packed.len() != expected {
220        return Err(CodecError::CompressedLengthMismatch {
221            expected,
222            actual: packed.len(),
223        });
224    }
225
226    // Decompress each coefficient independently and rebuild ring element.
227    let mut coeffs = vec![0_u64; expected];
228    for (index, &coeff) in packed.iter().enumerate() {
229        coeffs[index] = decompress_coefficient_example(coeff, ctx.modulus(), bits)?;
230    }
231
232    Ok(ctx.element(&coeffs)?)
233}
Source

pub fn element_from_polynomial( &self, poly: Polynomial, ) -> Result<RingElem, PolynomialError>

Converts a compatible polynomial into a canonical ring element.

Source

pub fn zero_element(&self) -> RingElem

Returns additive identity as a canonical ring element.

Source

pub fn one_element(&self) -> RingElem

Returns multiplicative identity as a canonical ring element.

Source

pub fn reduce(&self, poly: &Polynomial) -> Result<Polynomial, PolynomialError>

Reduces polynomial modulo the configured ring polynomial.

Source

pub fn add( &self, lhs: &Polynomial, rhs: &Polynomial, ) -> Result<Polynomial, PolynomialError>

Adds two ring elements and reduces modulo f(x).

Source

pub fn sub( &self, lhs: &Polynomial, rhs: &Polynomial, ) -> Result<Polynomial, PolynomialError>

Subtracts two ring elements and reduces modulo f(x).

Source

pub fn mul( &self, lhs: &Polynomial, rhs: &Polynomial, ) -> Result<Polynomial, PolynomialError>

Multiplies two ring elements and reduces modulo f(x).

Trait Implementations§

Source§

impl Clone for RingContext

Source§

fn clone(&self) -> RingContext

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for RingContext

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for RingContext

Source§

fn eq(&self, other: &RingContext) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Eq for RingContext

Source§

impl StructuralPartialEq for RingContext

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.