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
impl RingContext
Sourcepub fn from_parts(
max_degree: usize,
modulus: u64,
modulus_poly_coeffs: &[u64],
primitive_root: u64,
) -> Result<Self, ParamsError>
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?
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
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}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}Sourcepub fn max_degree(&self) -> usize
pub fn max_degree(&self) -> usize
Maximum supported polynomial degree.
Examples found in repository?
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
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}Sourcepub fn modulus(&self) -> u64
pub fn modulus(&self) -> u64
Coefficient modulus q.
Examples found in repository?
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
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}Sourcepub fn modulus_poly(&self) -> &Polynomial
pub fn modulus_poly(&self) -> &Polynomial
Ring modulus polynomial f(x).
Sourcepub fn primitive_root(&self) -> u64
pub fn primitive_root(&self) -> u64
Primitive root used by NTT-based operations.
Sourcepub fn ntt_length(&self) -> usize
pub fn ntt_length(&self) -> usize
Derived NTT transform length.
Sourcepub fn polynomial(&self, coeffs: &[u64]) -> Result<Polynomial, PolynomialError>
pub fn polynomial(&self, coeffs: &[u64]) -> Result<Polynomial, PolynomialError>
Builds a polynomial compatible with this context.
Sourcepub fn zero(&self) -> Polynomial
pub fn zero(&self) -> Polynomial
Returns additive identity in this ring.
Sourcepub fn one(&self) -> Polynomial
pub fn one(&self) -> Polynomial
Returns multiplicative identity in this ring.
Sourcepub fn element(&self, coeffs: &[u64]) -> Result<RingElem, PolynomialError>
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?
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
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}Sourcepub fn element_from_polynomial(
&self,
poly: Polynomial,
) -> Result<RingElem, PolynomialError>
pub fn element_from_polynomial( &self, poly: Polynomial, ) -> Result<RingElem, PolynomialError>
Converts a compatible polynomial into a canonical ring element.
Sourcepub fn zero_element(&self) -> RingElem
pub fn zero_element(&self) -> RingElem
Returns additive identity as a canonical ring element.
Sourcepub fn one_element(&self) -> RingElem
pub fn one_element(&self) -> RingElem
Returns multiplicative identity as a canonical ring element.
Sourcepub fn reduce(&self, poly: &Polynomial) -> Result<Polynomial, PolynomialError>
pub fn reduce(&self, poly: &Polynomial) -> Result<Polynomial, PolynomialError>
Reduces polynomial modulo the configured ring polynomial.
Sourcepub fn add(
&self,
lhs: &Polynomial,
rhs: &Polynomial,
) -> Result<Polynomial, PolynomialError>
pub fn add( &self, lhs: &Polynomial, rhs: &Polynomial, ) -> Result<Polynomial, PolynomialError>
Adds two ring elements and reduces modulo f(x).
Sourcepub fn sub(
&self,
lhs: &Polynomial,
rhs: &Polynomial,
) -> Result<Polynomial, PolynomialError>
pub fn sub( &self, lhs: &Polynomial, rhs: &Polynomial, ) -> Result<Polynomial, PolynomialError>
Subtracts two ring elements and reduces modulo f(x).
Sourcepub fn mul(
&self,
lhs: &Polynomial,
rhs: &Polynomial,
) -> Result<Polynomial, PolynomialError>
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
impl Clone for RingContext
Source§fn clone(&self) -> RingContext
fn clone(&self) -> RingContext
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more