Appendix D: Solutions for Chapter 9
This page collects solutions and editorial notes for the exercises in Chapter 9: Ring-LWE and Module-LWE. Compute and derivation exercises have worked solutions; open-ended exercises have an editorial note describing what a strong answer addresses.
The fuller versions of these routines are in the ring_lwe package under solutions/ch09-ring-lwe. From a clone of the companion repository, pytest tests/ch09 runs its suite. Appendix C has the setup.
Exercise 1
Section titled “Exercise 1”The exercise walks the calculation. Key checks: , and . The negacyclic minus sign is the difference between this ring and the cyclic ring . In the cyclic ring rather than .
def ring_mul(f, g, n=8, q=17): out = [0] * n for i in range(n): for j in range(n): k = i + j sign = 1 if k < n else -1 out[k % n] = (out[k % n] + sign * f[i] * g[j]) % q return out
f = [0] * 8; f[5] = 1 # x^5g4 = [0] * 8; g4[4] = 1 # x^4g3 = [0] * 8; g3[3] = 1 # x^3print(ring_mul(f, g4))print(ring_mul(f, g3))# ==> [0, 16, 0, 0, 0, 0, 0, 0]# ==> [16, 0, 0, 0, 0, 0, 0, 0]Exercise 2
Section titled “Exercise 2”The negacyclic NTT evaluates at the odd powers of , where is a primitive -th root of unity in . For , the evaluation is , which gives the sequence modulo . With : , , , .
print([pow(2, 2 * k + 1, 17) for k in range(4)])# ==> [2, 8, 15, 9]Exercise 3
Section titled “Exercise 3”The verification is a transcription of the Module-LWE definition. For each output row , recompute in the ring , then assert termwise equality with the package-returned . The test in tests/ch09/test_module_collapses_to_ring.py does this for and is the reference. Writing it yourself reinforces where the two indices go. The rows are the samples; the columns are the components of the secret. At each row is a single ring product, so the rows are Ring-LWE samples sharing one secret. At each row sums ring products against a rank- secret, and those terms are never observed separately. At this exercise’s that is four samples, not three.
Exercise 4
Section titled “Exercise 4”The condition is the existence requirement for a primitive -th root of unity in , which has order . The primes below 200 satisfying this are . Once is found, the order check rules out roots of order 1, 2, 4, or 8. The order must divide 16 and be larger than 8, so the order is 16.
def is_prime(n): return n > 1 and all(n % i for i in range(2, int(n ** 0.5) + 1))
primes = [q for q in range(3, 200) if is_prime(q) and (q - 1) % 16 == 0]print(primes)# ==> [17, 97, 113, 193]