PQC · Lecture 2 · Florida Atlantic University · Fall 2026

Lecture 2: The Number Theoretic Transform (NTT)

Multiplying two polynomials of 256 coefficients takes 65,536 products the schoolbook way. The NTT needs 3,584: evaluate at roots of unity, multiply point by point, and convert back. Every step runs on the same toy ring as the slides: q = 17, n = 4, ζ = 2.

3,584multiplications per product at n = 256, instead of 65,536
8 / 7NTT stages in ML-DSA / ML-KEM
ζ = 2root of unity of the toy ring mod 17
1753 / 17roots of unity of ML-DSA / ML-KEM
One polynomial product costs n2=65,536n^2 = 65{,}536 multiplications. Can we do better?

The cost problem

Where we stopped

nn4162561024
n2n^21625665,5361,048,576

You have seen this kind of trick before

TaskSlow wayFast wayLecture
multiply big integersschoolbook, n2n^2Karatsuba, n1.58n^{1.58}FF multiplication
reduce mod pplong divisionBarrett, MontgomeryFF reduction
multiply in Rq\Rqschoolbook, n2n^2NTT, nlog⁡2nn \log_2 ntoday

Evaluate, multiply, go back

A polynomial is also its values

Coefficients
a=(1,2,3,4)a = (1, 2, 3, 4)
meaning 1+2x+3x2+4x31 + 2x + 3x^2 + 4x^3
Values at 4 points
a(2)=15a(2) = 15, a(8)=13a(8) = 13,
a(15)=11a(15) = 11, a(9)=16a(9) = 16 (mod 17)

Multiplying values is cheap

The wrap-around needs special points

Choose the points well
Use the nn roots of xn+1x^n + 1, the numbers rr with rn=−1(modq)r^n = -1 \pmod q.
Then c(r)=a(r)⋅b(r)c(r) = a(r) \cdot b(r) holds for the folded product too.

Powers of 2 mod 17

The idea, checked on numbers

point rra(r)a(r)b(r)b(r)a(r)⋅b(r)a(r)\cdot b(r)mod 17c(r)c(r)
215710533
81379166
151188833
9161524022
Try it

Which points work?

Pick any point r mod 17. The product c = a·b is the folded one from Lecture 1. Its values match a(r)·b(r) only at the roots of x4 + 1.

Definition of the NTT

Number Theoretic Transform
NTT(a)\NTT(a) is the list of values of aa at the nn odd powers of ζ\zeta: NTT(a)=(a(ζ1), a(ζ3), a(ζ5), …, a(ζ2n−1))\NTT(a) = \big(a(\zeta^1),\, a(\zeta^3),\, a(\zeta^5),\, \dots,\, a(\zeta^{2n-1})\big)
So far
The idea is right, but direct evaluation saves nothing. We need a fast way to compute all nn values.

A fast NTT

Split the problem in half

Reducing mod x2−cx^2 - c is one line

mod x2−cx^2 - c
(a0+c a2)+(a1+c a3) x(a_0 + c\, a_2) + (a_1 + c\, a_3)\, x
mod x2+cx^2 + c
(a0−c a2)+(a1−c a3) x(a_0 - c\, a_2) + (a_1 - c\, a_3)\, x
Our a=(1,2,3,4)a = (1, 2, 3, 4) with c=4c = 4
mod x2−4x^2 - 4: (1+4⋅3)+(2+4⋅4) x=13+18x≡13+x(1 + 4\cdot 3) + (2 + 4 \cdot 4)\,x = 13 + 18x \equiv \Out{13 + x}
mod x2+4x^2 + 4: (1−4⋅3)+(2−4⋅4) x=−11−14x≡6+3x(1 - 4\cdot 3) + (2 - 4 \cdot 4)\,x = -11 - 14x \equiv \Out{6 + 3x}

The butterfly

Split again until single numbers remain

The same steps on an array

Stage 1, w=4w = 4
pairs (a0,a2)(a_0, a_2) and (a1,a3)(a_1, a_3)
1±4⋅3=13,−11≡13,61 \pm 4 \cdot 3 = 13, -11 \equiv 13, 6
2±4⋅4=18,−14≡1,32 \pm 4 \cdot 4 = 18, -14 \equiv 1, 3
Stage 2, w=2w = 2 and w=8w = 8
(13,1)(13, 1): 13±2⋅1=15,1113 \pm 2 \cdot 1 = 15, 11
(6,3)(6, 3): 6±8⋅3=30,−18≡13,166 \pm 8 \cdot 3 = 30, -18 \equiv 13, 16
Try it

Butterfly stepper

Step through the NTT one butterfly at a time, for the toy ring or for n = 8 with ζ = 3. Switch to the inverse to undo it. You can type your own input.

Your turn: NTT(b)\NTT(b)

Stage 1, w=4w = 4
5±4⋅7=33,−23≡16,115 \pm 4 \cdot 7 = 33, -23 \equiv 16, 11 6±4⋅8=38,−26≡4,86 \pm 4 \cdot 8 = 38, -26 \equiv 4, 8
array: (16,4,11,8)(16, 4, 11, 8)
Stage 2
(16,4)(16, 4), w=2w = 2: 16±8=24,8≡7,816 \pm 8 = 24, 8 \equiv 7, 8
(11,8)(11, 8), w=8w = 8: 11±64=75,−53≡7,1511 \pm 64 = 75, -53 \equiv 7, 15
NTT(b)=(7,8,7,15)=(b(2),b(15),b(8),b(9))\NTT(b) = \Out{(7, 8, 7, 15)} = (b(2), b(15), b(8), b(9))

Bigger nn, deeper tree

Counting the work

Cost of a fast NTT
log⁡2n\log_2 n stages ×\times n/2n/2 butterflies ×\times 1 multiplication =n2log⁡2n= \dfrac{n}{2}\log_2 n
nnstagesfast NTTdirect evaluation n2n^2
42416
831264
25681,02465,536

Why the output order looks shuffled

ii01234567
ii in binary000001010011100101110111
reversed000100010110001101011111
brv(i)\mathrm{brv}(i)04261537

Twiddle factors are a fixed table

RingTwiddles usedFirst entries
toy, q=17q = 17, ζ=2\zeta = 234,2,84, 2, 8 (that is ζ2,ζ1,ζ3\zeta^2, \zeta^1, \zeta^3)
ML-DSA, ζ=1753\zeta = 17532554808194,3765607,…4808194, 3765607, \dots
ML-KEM, ζ=17\zeta = 171271729,2580,3289,…1729, 2580, 3289, \dots

The whole forward NTT in code

def ntt(a, q, zetas):
    n = len(a)
    k = 1
    length = n // 2
    while length >= 1:
        for start in range(0, n, 2*length):
            w = zetas[k]
            k += 1
            for j in range(start, start+length):
                t = w * a[j+length] 
                a[j+length] = (a[j] - t) 
                a[j] = (a[j] + t) 
        length //= 2
    return a
  • length: distance between the two inputs of a butterfly. n/2n/2, then n/4n/4, down to 1.
  • start: one block per twiddle.
  • Inner loop: the butterfly.
  • Same shape as Algorithm 41 in FIPS 204. You write it in Assignment 7.

Going back: the inverse NTT

Undoing one butterfly

The inverse NTT on numbers

Stage 1, w−1=9w^{-1} = 9 and w−1=15w^{-1} = 15
(3,3)(3, 3): (3+3,  0⋅9)=(6,0)(3 + 3,\; 0 \cdot 9) = (6, 0)
(6,2)(6, 2): (6+2,  4⋅15)=(8,60)≡(8,9)(6 + 2,\; 4 \cdot 15) = (8, 60) \equiv (8, 9)
Stage 2, w−1=13w^{-1} = 13
(6,8)(6, 8): (14,  −2⋅13)≡(14,8)(14,\; -2 \cdot 13) \equiv (14, 8)
(0,9)(0, 9): (9,  −9⋅13)≡(9,2)(9,\; -9 \cdot 13) \equiv (9, 2)
Scale by 4−1=134^{-1} = 13
(14,9,8,2)⋅13≡(12,15,2,9)(14, 9, 8, 2) \cdot 13 \equiv \Out{(12, 15, 2, 9)}, the schoolbook result

The full pipeline

Pointwise step
15⋅7=105≡315 \cdot 7 = 105 \equiv 3, 11⋅8=88≡311 \cdot 8 = 88 \equiv 3
13⋅7=91≡613 \cdot 7 = 91 \equiv 6, 16⋅15=240≡216 \cdot 15 = 240 \equiv 2
Cost for one product
2 forward NTTs, nn pointwise products,
1 inverse NTT, nn scalings

Is it really faster?

nnschoolbookNTT way
41620NTT is slower
162561282×\times faster
644,0967046×\times faster
25665,5363,58418×\times faster
10241,048,57617,40860×\times faster
Try it

Schoolbook against the NTT

Slide n from 4 to 4096. The NTT count is 3·(n/2)·log2n + 2n.

The NTT in ML-DSA and ML-KEM

When does a good ζ\zeta exist?

Rule
A primitive 2n2n-th root of unity mod a prime qq exists exactly when 2n2n divides q−1q - 1.
Ringq−1q - 12n2nζ\zeta of order 2n2n?
toy16=2416 = 2^48yes, ζ=2\zeta = 2
ML-DSA8380416=213⋅10238380416 = 2^{13} \cdot 1023512yes, ζ=1753\zeta = 1753
ML-KEM3328=28⋅133328 = 2^{8} \cdot 13512no: only order 256, ζ=17\zeta = 17

ML-DSA: the full NTT

StepMultiplications
2 forward NTTs2⋅1024=20482 \cdot 1024 = 2048
pointwise256
inverse NTT and scaling1024+256=12801024 + 256 = 1280
total3,584 (schoolbook: 65,536)

ML-KEM: stop one stage early

Multiplying two degree-1 pieces

BaseCaseMultiply (mod x2−γx^2 - \gamma)
(a0+a1x)(b0+b1x)=(a0b0+γ a1b1)+(a0b1+a1b0) x(a_0 + a_1 x)(b_0 + b_1 x) = (a_0 b_0 + \gamma\, a_1 b_1) + (a_0 b_1 + a_1 b_0)\, x
Toy, after stage 1: a^=(13,1,6,3)\hat a = (13, 1, 6, 3), b^=(16,4,11,8)\hat b = (16, 4, 11, 8)
γ=4\gamma = 4: (13+x)(16+4x)→(208+4⋅4,  52+16)=(224,68)≡(3,0)(13 + x)(16 + 4x) \rightarrow (208 + 4\cdot 4,\; 52 + 16) = (224, 68) \equiv (3, 0)
γ=−4≡13\gamma = -4 \equiv 13: (6+3x)(11+8x)→(66+13⋅24,  48+33)=(378,81)≡(4,13)(6 + 3x)(11 + 8x) \rightarrow (66 + 13 \cdot 24,\; 48 + 33) = (378, 81) \equiv (4, 13)

ML-KEM in numbers

StepMultiplications
2 forward NTTs, 7 stages each2⋅896=17922 \cdot 896 = 1792
128 base-case products128⋅5=640128 \cdot 5 = 640
inverse NTT and scaling896+256=1152896 + 256 = 1152
total3,584 (schoolbook: 65,536)

Stay in the NTT domain

ML-KEM-768: computing A⋅s\mathbf{A}\cdot\mathbf{s}
schoolbook: 9⋅65,536=589,8249 \cdot 65{,}536 = 589{,}824 multiplications
NTT way: 33 NTTs of s\mathbf{s} ++ 9⋅6409 \cdot 640 base cases ++ 33 inverse NTTs =11,904= \Out{11{,}904}, about 50 times fewer

Summary

Summary

The NTT in five lines
  1. Store a polynomial by its values at the nn roots of xn+1x^n + 1 (the odd powers of ζ\zeta).
  2. In that form, multiplication in Rq\Rq is pointwise: nn multiplications.
  3. The fast NTT splits xn+1x^n + 1 in half log⁡2n\log_2 n times, with n/2n/2 butterflies per split.
  4. The inverse NTT undoes the splits with Gentleman-Sande butterflies and a final n−1n^{-1}.
  5. ML-DSA uses the full NTT. ML-KEM stops one stage early and multiplies degree-1 pieces.
Assignment 7
Build all of this in Python: schoolbook, slow NTT, butterflies, fast NTT and inverse, then ML-DSA size.
Now we can afford the arithmetic. Next: ML-KEM and ML-DSA.