Coding Theory Series — 07

BCH Codes

Algebraic codes with guaranteed error correction — from finite field theory to flash memory

Mathematics Code History

Roadmap

History

Hocquenghem, Bose & Ray-Chaudhuri — independent discovery

Finite Fields

GF(2m), minimal polynomials, cyclotomic cosets

Code Construction

BCH bound, generator polynomial, designed distance

Encoding

Systematic & non-systematic methods

Decoding

PGZ, Berlekamp-Massey, Chien search

Applications

Flash memory, satellite, connection to RS codes

Historical Origins

History

Alexis Hocquenghem (1959)

French mathematician at the Conservatoire National des Arts et Metiers in Paris.

Published the construction of a class of multiple-error-correcting codes in Comptes Rendus, September 1959.

His work was less well-known initially due to the French publication venue.

Bose & Ray-Chaudhuri (1960)

Raj Chandra Bose and Dwijendra Kumar Ray-Chaudhuri at the University of North Carolina.

Published independently in March 1960 in the Journal of Information and Control.

Provided a more detailed treatment with proofs of the BCH bound.

BCH = Bose-Chaudhuri-Hocquenghem. One of the most important families of cyclic error-correcting codes. Reed-Solomon codes are a special case!

Finite Fields: GF(2m) Review

Mathematics

Construction via Irreducible Polynomials

GF(2m) is constructed as GF(2)[x] / p(x), where p(x) is an irreducible polynomial of degree m over GF(2).

GF(24): p(x) = x4 + x + 1   (irreducible over GF(2))

Elements

GF(2m) has 2m elements:

  • 0 (additive identity)
  • 1, α, α2, ..., α2m-2

α is a root of p(x), called the primitive element.

Key Property

α2m-1 = 1 (cyclic group of order 2m-1).

Every nonzero element is a power of α.

Addition: XOR of polynomial coefficients.

Multiplication: polynomial multiplication mod p(x).

GF(24) Worked Example

Mathematics

p(x) = x4 + x + 1, so α4 = α + 1

PowerPolynomialBinaryDecimal
0000000
α0100011
α1α00102
α2α201004
α3α310008
α4α + 100113
α5α2 + α01106
α6α3 + α2110012
α7α3 + α + 1101111

Table continues for α8 through α14. All 15 nonzero elements are distinct powers of α.

Minimal Polynomials

Mathematics

Definition

The minimal polynomial mi(x) of αi is the smallest degree polynomial over GF(2) that has αi as a root:

mii) = 0,    mi(x) ∈ GF(2)[x],    mi(x) is irreducible

Key Properties

  • mi(x) divides x2m-1 + 1
  • deg(mi) divides m
  • If αi is a root, so is α2i (Frobenius endomorphism)
  • Roots come in conjugate sets: {αi, α2i, α4i, ...}

GF(24) Minimals

m1(x) = x4 + x + 1

Roots: α, α2, α4, α8

m3(x) = x4 + x3 + x2 + x + 1

Roots: α3, α6, α12, α9

m5(x) = x2 + x + 1

Roots: α5, α10

m7(x) = x4 + x3 + 1

Roots: α7, α14, α13, α11

Cyclotomic Cosets

Mathematics

Definition

The cyclotomic coset of i modulo 2m-1 is:

Ci = { i, 2i, 4i, 8i, ... } mod (2m-1)

Elements in the same coset share the same minimal polynomial.

GF(24) Cyclotomic Cosets (mod 15)

CosetElementsSizeMinimal Polynomial
C0{0}1x + 1
C1{1, 2, 4, 8}4x4 + x + 1
C3{3, 6, 12, 9}4x4 + x3 + x2 + x + 1
C5{5, 10}2x2 + x + 1
C7{7, 14, 13, 11}4x4 + x3 + 1
Sizes sum to 15: 1 + 4 + 4 + 2 + 4 = 15 = 24 - 1. The coset structure determines the possible BCH code parameters.

The BCH Bound

Mathematics

Designed Distance

BCH Bound: If the generator polynomial g(x) has αb, αb+1, ..., αb+d-2 as roots (d-1 consecutive powers of α), then the minimum distance of the code is at least d.
dmin ≥ ddesigned = δ

The designed distance δ is a guaranteed lower bound on dmin. Often dmin = δ exactly, but sometimes dmin > δ.

Why Does This Work?

If c(x) is a codeword and g(x)|c(x), then c(αi) = 0 for all roots of g(x). A codeword of weight < d would give a system of equations with a Vandermonde matrix that must have a nontrivial solution — but Vandermonde is always invertible for distinct roots! Contradiction.

Generator Polynomial Construction

Mathematics

Recipe for a t-Error-Correcting BCH Code

Step 1: Choose designed distance δ = 2t + 1 (to correct t errors).

Step 2: The generator must have roots α, α2, ..., α2t.

Step 3: Compute g(x) = LCM(m1(x), m2(x), ..., m2t(x)).

Since conjugate roots share minimal polynomials, many mi's are redundant.

Example: Double-Error-Correcting BCH(15, 7)

t = 2, need roots α, α2, α3, α4 in g(x).

m1(x) = x4 + x + 1   (contains roots α, α2, α4)
m3(x) = x4 + x3 + x2 + x + 1   (contains root α3)
g(x) = m1(x) · m3(x) = x8 + x7 + x6 + x4 + 1

n = 15, deg(g) = 8, so k = 15 - 8 = 7. BCH(15, 7, 5) code.

BCH Code Parameters

Mathematics

Primitive Narrow-Sense BCH Codes over GF(2m)

n = 2m - 1,    k ≥ n - mt,    d ≥ 2t + 1

Example Codes

mntkdminRate
41511130.733
4152750.467
4153570.333
53112630.839
53131670.516
531511110.355
8255223950.937
8255422390.875

Note: The t=1 case for any m gives the Hamming code — BCH generalizes Hamming codes!

BCH Encoding

Mathematics Code

Non-Systematic

Simply multiply:

c(x) = m(x) · g(x)

Message polynomial m(x) of degree < k produces codeword c(x) of degree < n.

Simple but message bits are not directly readable in the codeword.

Systematic

Place message in high-order positions:

c(x) = xn-k·m(x) + r(x)

where r(x) = [xn-k·m(x)] mod g(x)

Codeword = [parity bits | message bits]

First k bits of the codeword are the original message — easy to extract after decoding!

Encoding Example: BCH(15, 7)

Mathematics Code

Systematic Encoding

g(x) = x8 + x7 + x6 + x4 + 1

Message: m = (1 0 1 1 0 0 1) → m(x) = x6 + x4 + x3 + 1

Step 1: Compute x8 · m(x) = x14 + x12 + x11 + x8

Step 2: Divide by g(x):

x14 + x12 + x11 + x8 = q(x)·g(x) + r(x)

Step 3: r(x) = x7 + x6 + x4 + x3 + x2 + x (the remainder)

Step 4: c(x) = x8·m(x) + r(x)

Codeword: (1 1 0 1 1 1 1 0 | 1 0 1 1 0 0 1)
            parity             message

BCH Decoding Pipeline

Mathematics
Received
r(x)
Syndromes
S₁...S₂ₜ
Error Locator
σ(x)
Error Locations
Chien Search
Correct
ĉ(x)

Step 1: Syndrome Computation

Evaluate r(x) at the roots of g(x):

Sj = r(αj) = e(αj)

If all Sj = 0, no errors detected.

Step 2: Error-Locator Polynomial

Find σ(x) = (1 + X1x)(1 + X2x)...(1 + Xvx)

Where Xi = αei marks error positions.

For binary BCH: errors are always in GF(2), so we only need to find error locations (bit positions to flip). Non-binary codes also need error values.

Syndrome Computation

Mathematics

Calculating Syndromes

For a t-error-correcting BCH code, compute 2t syndromes:

Sj = r(αj) = Σi=0n-1 ri · αij,    j = 1, 2, ..., 2t

Syndromes in Terms of Error Positions

If v errors occurred at positions e₁, e₂, ..., ev:

S1 = X1 + X2 + ... + Xv
S2 = X12 + X22 + ... + Xv2
S3 = X13 + X23 + ... + Xv3

S2t = X12t + X22t + ... + Xv2t

where Xi = αei are the error locators.

Property: S2j = Sj2 in binary fields (Frobenius). This halves the independent equations but also the unknowns!

Peterson-Gorenstein-Zierler Decoder

Mathematics

Direct Matrix Approach

The error-locator polynomial σ(x) = 1 + σ1x + σ2x2 + ... + σvxv satisfies Newton's identities:

S1S2Sv
S2S3Sv+1
SvSv+1S2v−1
σv
σv−1
σ1
=
Sv+1
Sv+2
S2v

Algorithm

  1. Start with v = t (assume maximum errors)
  2. Form the syndrome matrix
  3. If det = 0, reduce v by 1 and retry
  4. Solve the linear system over GF(2m)
  5. Find roots of σ(x) via Chien search

Complexity

Matrix inversion: O(t3) field operations.

Practical for small t, but Berlekamp-Massey is preferred for t > 3 due to O(t2) complexity.

Berlekamp-Massey Algorithm

Mathematics

Iterative Error-Locator Polynomial Construction

The BM algorithm finds the shortest LFSR (Linear Feedback Shift Register) that generates the syndrome sequence S₁, S₂, ..., S₂ₜ.

Algorithm Outline

  1. Initialize: σ(0)(x) = 1, B(x) = 1, L = 0, r = 1
  2. For each r = 1 to 2t:
    • Compute discrepancy: Δr = Sr + σ1Sr-1 + ... + σLSr-L
    • If Δr = 0: do nothing (current LFSR works)
    • If Δr ≠ 0: update σ(x) using correction term
    • If 2L < r: increase LFSR length, update B(x)
  3. Output: σ(x) of degree v ≤ t
Complexity: O(t2) — much better than PGZ's O(t3). This is the standard decoder for practical BCH/RS implementations.

Chien Search

Mathematics

Finding Roots of σ(x)

Named after R.T. Chien (1964). An exhaustive search made efficient by exploiting the structure of finite fields.

Test each α-i for i = 0, 1, ..., n-1:    σ(α-i) = 0?

If σ(α-i) = 0, then position i has an error.

Efficient Implementation

Instead of recomputing from scratch each time, update incrementally:

Tj ← σj · α-j (initially)
Tj ← Tj · α-j (each step)

Only v multiplications per step instead of evaluating the full polynomial.

Hardware Friendly

  • Constant-time operation (n iterations always)
  • Parallelizable — test multiple roots simultaneously
  • Only multipliers and adders in GF(2m)
  • Can be pipelined for high throughput

Binary vs. Non-Binary BCH

Mathematics

Binary BCH Codes

  • Symbols are in GF(2) (bits)
  • Roots of g(x) are in GF(2m)
  • Error values are always 1 (just flip the bit)
  • Only need to find error positions
  • Code length n = 2m - 1

Simpler decoding — no need for Forney's algorithm.

Non-Binary BCH (Reed-Solomon)

  • Symbols are in GF(2m) (m-bit symbols)
  • Each symbol can take 2m values
  • Error values are arbitrary field elements
  • Must find positions AND values
  • Code length n = 2m - 1 symbols

Needs Forney's algorithm to compute error magnitudes.

Reed-Solomon codes are non-binary BCH codes where the symbols and roots are in the same field. This gives them the MDS property: d = n - k + 1.

BCH → Reed-Solomon Connection

Mathematics

The Family Tree

Linear Codes
Cyclic Codes
BCH Codes
Reed-Solomon

BCH Code (Binary)

  • Alphabet: GF(2)
  • Length: n = 2m - 1 bits
  • g(x) has roots in GF(2m)
  • d ≥ 2t + 1 (BCH bound)
  • NOT MDS in general

Reed-Solomon Code

  • Alphabet: GF(2m)
  • Length: n = 2m - 1 symbols
  • g(x) = Π(x - αi), consecutive roots
  • d = n - k + 1 (exactly!)
  • Always MDS!
Subtle difference: In RS codes, minimal polynomials are degree-1 (since roots are in the same field as the coefficients). In binary BCH, minimal polynomials have degree up to m, which is why binary BCH codes are not MDS.

Applications of BCH Codes

Applications

Flash Memory (NAND)

BCH codes are the standard ECC for NAND flash storage.

  • SLC flash: 1-bit BCH sufficient
  • MLC flash: 4-8 bit BCH needed
  • TLC flash: 40-72 bit BCH or LDPC

BCH preferred for its deterministic latency and low power.

Satellite Communications

DVB-S2 uses BCH outer code + LDPC inner code.

  • BCH cleans up LDPC error floor
  • Typical: BCH(65535, 65343, t=12)
  • Near Shannon limit performance

Barcodes & QR Codes

QR codes use RS (a BCH generalization) for error correction at multiple levels.

L: 7%, M: 15%, Q: 25%, H: 30% correction capability.

Disk Drives

Hard drives use BCH/RS codes in the read channel alongside other signal processing.

Typical: t = 10-20 error correction.

Python: BCH Encoder

Code
def gf2_poly_mod(dividend, divisor):
    """Polynomial division over GF(2), return remainder."""
    dividend = list(dividend)  # copy
    for i in range(len(dividend) - len(divisor) + 1):
        if dividend[i] == 1:
            for j in range(len(divisor)):
                dividend[i + j] ^= divisor[j]
    return dividend[-(len(divisor)-1):]

def gf2_poly_mult(a, b):
    """Polynomial multiplication over GF(2)."""
    result = [0] * (len(a) + len(b) - 1)
    for i, ai in enumerate(a):
        if ai:
            for j, bj in enumerate(b):
                result[i + j] ^= bj
    return result

class BCHCode:
    def __init__(self, n, g_coeffs):
        """
        n: code length (2^m - 1)
        g_coeffs: generator polynomial as list [high...low]
        """
        self.n = n
        self.g = g_coeffs
        self.n_minus_k = len(g_coeffs) - 1
        self.k = n - self.n_minus_k

    def encode_systematic(self, message):
        """Systematic BCH encoding."""
        assert len(message) == self.k
        # x^(n-k) * m(x)
        shifted = message + [0] * self.n_minus_k
        # Compute remainder
        remainder = gf2_poly_mod(shifted, self.g)
        # Codeword = message | parity
        codeword = message + remainder
        return codeword

    def compute_syndrome(self, received, alpha_powers):
        """Compute syndromes by evaluating r(x) at roots."""
        # This requires GF(2^m) arithmetic - simplified here
        pass  # Full implementation needs GF arithmetic

Python: BCH(15,7) Demo

Code
# BCH(15, 7, 5) generator: x^8 + x^7 + x^6 + x^4 + 1
g = [1, 1, 1, 0, 1, 0, 0, 0, 1]  # coefficients high to low

bch = BCHCode(15, g)

# Encode a message
message = [1, 0, 1, 1, 0, 0, 1]
codeword = bch.encode_systematic(message)
print(f"Message:  {message}")
print(f"Codeword: {codeword}")
# Output: [1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0]
#          |--- message ---|  |--- parity ---|

# Verify: codeword should be divisible by g(x)
remainder = gf2_poly_mod(codeword, g)
print(f"Remainder (should be all zeros): {remainder}")
# [0, 0, 0, 0, 0, 0, 0, 0]

# Introduce 2 errors (max correctable)
received = codeword.copy()
received[3] ^= 1  # flip bit 3
received[10] ^= 1  # flip bit 10
print(f"Received: {received}")

# Check syndrome (non-zero = errors detected)
rem = gf2_poly_mod(received, g)
print(f"Syndrome check: {rem}")
print(f"Errors detected: {any(rem)}")  # True

# Encode all 2^7 = 128 messages to verify code properties
min_weight = 15
for i in range(1, 128):
    m = [(i >> j) & 1 for j in range(6, -1, -1)]
    c = bch.encode_systematic(m)
    w = sum(c)
    min_weight = min(min_weight, w)
print(f"Minimum weight (= d_min): {min_weight}")  # 5

Summary

BCH Code Essentials

  • Cyclic codes with algebraic decoding
  • n = 2m-1, guaranteed d ≥ 2t+1
  • Generator = LCM of minimal polynomials
  • Generalize Hamming codes (t=1 case)

Decoding Pipeline

  • Syndromes → Error-locator → Roots → Correct
  • PGZ: O(t3), small t
  • Berlekamp-Massey: O(t2), standard
  • Chien search: O(n) root finding

The Big Picture

Hamming
t=1
BCH
t-error binary
Reed-Solomon
non-binary BCH, MDS
Next up: Reed-Solomon codes — the "most widely deployed error correction code in history." Everything from CDs to QR codes to deep space.