Skip to content

← Computing Further 9–12

How Does a Scratched QR Code Still Scan?

Topic 19 · Grades 9–12 · Error-correcting codes

You need binary numbers (Grade 9) and lists and functions in Python (Grades 8 and 9).

What this builds on from class
In this unit 8 sections
  1. Try it
  2. How it works
  3. Program it
  4. What your program shows
  5. Going further still
  6. Challenges
  7. Questions
  8. Where to go next

The QR code to pay at a tea shop lives a hard life. It is taped to the counter, splashed with tea, rubbed by sleeves and scratched by keys. One day a corner of it is torn off. You hold up your phone anyway, and it beeps: paid. The phone read a message that was no longer all there.

The code below is a real QR code (it leads to the Science Playground’s Book a Visit page). Drag your finger across it to scratch it, as a key would. After every scratch, a real QR decoder, the same kind of program a phone runs, tries to read it.

Try it

Scratch it until it fails

Scratch with your finger or the mouse, or press Add a scratch. Stop when the decoder gives up, then try the same scratches at the other levels.

Level:
A real decoder says
—
Damaged bytes / can fixDamaged bytes / can fix, per block
—
This level can fix
13 of 70 bytes

Loading the QR decoder… Then drag across the code to scratch it.

Before you scratch: how much of a QR code can be damaged before a phone can’t read it any more?

Pick an answer to see why.

You need binary numbers (Grade 9, Number systems) and lists and functions in Python (Grades 8 and 9). The rest is explained here.

Try it

Scratch the code at level M until the decoder says Doesn’t scan. Then, without cleaning it, tap L, Q and H. The scratches stay where they are; only the code under them changes.

  • At which levels does the same damage still scan?
  • Watch Damaged bytes / can fix. The code doesn’t fail when a certain number of squares is spoiled. It fails when a certain number of bytes is.
  • Press Show the bytes. Each byte is a little patch of 8 squares, outlined. A long scratch crosses many squares but spoils only the few bytes it passes through.
  • Scratch across one of the three big squares in the corners, and nothing else.

You scratch the code until it fails at level M, then tap H. It scans again. What changed?

Pick an answer to see why.

How it works

You may have met the first step in Grades 6–8: in Which Card Was Flipped? an extra row and an extra column of cards, each making its line’s count of dark cards even, show where a single turned card is. Grade 11’s Noticing a flipped bit does the same with bytes. A parity bit is a bit added to make a group’s count of 1s even; one flipped bit makes the count odd.

The rows and columns cost 11 extra cards for 25. Richard Hamming found in 1950 that far fewer will do, if each check bit watches a cleverer group of bits.

Three circles

Here are 7 bits, numbered 1 to 7. Bits 3, 5, 6 and 7 carry a message of 4 bits. Bits 1, 2 and 4 are check bits: each makes the count of 1s inside its own circle even.

Try it

Be the noise

Tap any bit to flip it, as a scratch or electrical noise would. Watch which circles go odd, then press Fix it. Try a check bit too, then two bits at once.

Check 12 ones: evenCheck 24 ones: evenCheck 42 ones: even0bit 10011bit 20101bit 30110bit 41000bit 51011bit 61101bit 7111
Message sent
1011
It reads now
1011
Checks point at
—

Every circle has an even number of 1s. Tap any bit to flip it, as a scratch or noise on the way would.

Flip one bit, any bit, and the circles that hold it go odd. The odd circles always point at exactly one place, and that is the bit that flipped.

Why the odd circles spell the place

Look at the small binary numbers beside the bits. Bit 5 is 101: a 4, no 2, a 1. Now look where bit 5 sits: inside Check 4’s circle and Check 1’s, outside Check 2’s. That is no accident. The circles were drawn so that:

  • Check 1 watches every bit whose number has a 1 in its ones place: 1, 3, 5, 7.
  • Check 2 watches every bit with a 1 in its twos place: 2, 3, 6, 7.
  • Check 4 watches every bit with a 1 in its fours place: 4, 5, 6, 7.

So when bit 5 flips, Checks 4 and 1 go odd and Check 2 stays even: odd, even, odd, which written as binary digits is 101, the number 5. Add the odd checks, 4 + 1, and you get the same answer. The pattern of failed checks is called the syndrome, and in Hamming’s code it is the wrong bit’s number written in binary.

Three checks can each be odd or even: 2 × 2 × 2 = 8 patterns. One means “nothing wrong” (all even), and that leaves exactly 7 patterns to name bits 1 to 7. That is why the code is 7 bits long: 4 for the message, 3 to say where any one mistake is.

In Hamming’s 7-bit code, only Check 2 is odd. Which bit flipped?

Pick an answer to see why.

Where it fails

Flip two bits in the circles above. The circles still go odd, so you know something is wrong, but they point at a third bit, and Fix it makes it worse. Hamming’s code promises to fix one flipped bit in each 7, no more.

That is a problem for a QR code. A scratch doesn’t flip one bit here and one bit there. It spoils squares that sit side by side, a run of neighboring bits called a burst. Two of those in one group of 7, and Hamming’s code is fooled.

Repairing whole bytes

In 1960 Irving Reed and Gustave Solomon found a code that repairs damage a byte at a time. To it, a byte is one number from 0 to 255. If a scratch spoils one bit of a byte or all eight, it is one damaged byte either way. That is exactly what you saw in the widget at the top.

Their idea uses something from maths you already know: two points fix a straight line. Through any two points there is exactly one line.

12342468placevaluemessage: 2, 3extra: 4, 5arrives as 6back to 3
Figure 19.1 A message of two numbers, 2 and 3, as the heights of a line at places 1 and 2. The line’s heights at places 3 and 4, 4 and 5, are sent as well, as extra numbers. The second message number arrives scratched, as 6. The other three still lie on one line, so the odd one out is found and put back on it, at 3.

Send the message as two points, and add two more points on the same line as extra numbers. Now one number can be scratched, any one, message or extra, and the receiver still finds it: three of the four points lie on one line, and the odd one out doesn’t (Figure 19.1). If the receiver knows which number was scratched (a smudge that can be seen), two lost numbers can be rebuilt, since any two good points give the line back. Not knowing where the damage is costs twice as much: each unknown error needs two extra numbers. Denso Wave, which made the QR code, says the same: Reed–Solomon needs twice as many extra bytes as the bytes it is to correct.

A longer message needs a curve instead of a line. Three points fix one curve shaped like a parabola (one with an x²), and in general any 5 points fix exactly one curve with up to an x⁴ in it. So a message of 5 numbers is the heights of one such curve at places 1 to 5; send its heights at places 6, 7, 8 and 9 as well, and any 2 scratched numbers can be repaired.

Program it

You will build Hamming’s code, send a message through noise, then build the byte-repairing code and scratch it.

Step 1: add the check bits

encode takes 4 message bits and gives back the 7-bit code word. It keeps the word as a list w with room for places 1 to 7 (w[0] is never used, so a bit’s place and its index are the same). The test (p // k) % 2 == 1 asks whether place p has a 1 in the ks place of its binary number: p // k drops the smaller places, and % 2 reads the last digit (Grade 9’s Number systems does this by hand).

def encode(data):    w = [0] * 8                  # w[1] to w[7]; w[0] is not used    w[3], w[5], w[6], w[7] = data    for k in [1, 2, 4]:        ones = 0        for p in range(1, 8):            if p != k and (p // k) % 2 == 1:                ones = ones + w[p]        w[k] = ones % 2          # 1 if the count is odd, to make it even    return w[1:] for data in [[1, 0, 1, 1], [0, 0, 0, 0], [1, 1, 1, 1], [0, 1, 1, 0]]:    print(data, '->', encode(data))

Output

[1, 0, 1, 1] -> [0, 1, 1, 0, 0, 1, 1][0, 0, 0, 0] -> [0, 0, 0, 0, 0, 0, 0][1, 1, 1, 1] -> [1, 1, 1, 1, 1, 1, 1][0, 1, 1, 0] -> [1, 1, 0, 0, 1, 1, 0]

Step 2: find the wrong bit, every time

wrong_bit does the checks again on what arrived and adds up the ones that are odd: the syndrome. Then the program tries every mistake: each of the 16 possible messages with each of its 7 bits flipped, then with every pair of bits flipped.

def encode(data):    w = [0] * 8    w[3], w[5], w[6], w[7] = data    for k in [1, 2, 4]:        ones = 0        for p in range(1, 8):            if p != k and (p // k) % 2 == 1:                ones = ones + w[p]        w[k] = ones % 2    return w[1:] def wrong_bit(word):    place = 0    for k in [1, 2, 4]:        ones = 0        for p in range(1, 8):            if (p // k) % 2 == 1:                ones = ones + word[p - 1]        if ones % 2 == 1:            place = place + k    return place messages = []for n in range(16):    messages.append([n // 8 % 2, n // 4 % 2, n // 2 % 2, n % 2]) found = 0tried = 0for data in messages:    for p in range(1, 8):        word = encode(data)        word[p - 1] = 1 - word[p - 1]        tried = tried + 1        if wrong_bit(word) == p:            found = found + 1print('One bit flipped:', found, 'of', tried, 'found') tried = 0noticed = 0right = 0for data in messages:    for p in range(1, 8):        for q in range(p + 1, 8):            word = encode(data)            word[p - 1] = 1 - word[p - 1]            word[q - 1] = 1 - word[q - 1]            tried = tried + 1            place = wrong_bit(word)            if place != 0:                noticed = noticed + 1                word[place - 1] = 1 - word[place - 1]            if word == encode(data):                right = right + 1print('Two bits flipped:', tried, 'tried,', noticed, 'noticed,', right, 'put right')

Output

One bit flipped: 112 of 112 foundTwo bits flipped: 336 tried, 336 noticed, 0 put right

(word[p - 1] because a list counts from 0 and the places from 1.)

Step 3: a message through a noisy line

Now send real text. Each letter is a number (Grade 7: text is numbers), written as 8 bits, and every 4 bits become a 7-bit code word. The send function is the noisy line: each bit has a 2 in 100 chance of flipping. A letter that comes out as no printable letter at all is shown as ?. random.seed(2083) makes the same “random” noise every run, so your output matches this page.

import random def encode(data):    w = [0] * 8    w[3], w[5], w[6], w[7] = data    for k in [1, 2, 4]:        ones = 0        for p in range(1, 8):            if p != k and (p // k) % 2 == 1:                ones = ones + w[p]        w[k] = ones % 2    return w[1:] def wrong_bit(word):    place = 0    for k in [1, 2, 4]:        ones = 0        for p in range(1, 8):            if (p // k) % 2 == 1:                ones = ones + word[p - 1]        if ones % 2 == 1:            place = place + k    return place def to_bits(text):    bits = []    for letter in text:        n = ord(letter)        for value in [128, 64, 32, 16, 8, 4, 2, 1]:            bits.append(n // value % 2)    return bits def to_text(bits):    text = ''    for i in range(0, len(bits), 8):        n = 0        for b in bits[i:i + 8]:            n = n * 2 + b        if 32 <= n <= 126:            text = text + chr(n)        else:            text = text + '?'    return text def send(bits, chance):    out = []    for b in bits:        if random.random() < chance:            out.append(1 - b)        else:            out.append(b)    return out def hamming_send(bits):    sent = []    for i in range(0, len(bits), 4):        sent = sent + encode(bits[i:i + 4])    return sent def hamming_read(received):    bits = []    for i in range(0, len(received), 7):        word = received[i:i + 7]        place = wrong_bit(word)        if place != 0:            word[place - 1] = 1 - word[place - 1]        bits = bits + [word[2], word[4], word[5], word[6]]    return bits message = 'MEET AT THE BHAKTAPUR DURBAR SQUARE AT 4.'random.seed(2083)bits = to_bits(message)print('Plain: ', to_text(send(bits, 0.02)))print('Hamming:', to_text(hamming_read(send(hamming_send(bits), 0.02)))) wrong_plain = 0wrong_hamming = 0letters = 0for trial in range(50):    plain = to_text(send(bits, 0.02))    fixed = to_text(hamming_read(send(hamming_send(bits), 0.02)))    for i in range(len(message)):        letters = letters + 1        if plain[i] != message[i]:            wrong_plain = wrong_plain + 1        if fixed[i] != message[i]:            wrong_hamming = wrong_hamming + 1print('Letters sent:', letters)print('Wrong without a code:', wrong_plain)print('Wrong with Hamming:  ', wrong_hamming)

Output

Plain:  MEUT AT$THE BHAKT@PuR LU?BAR!SQUA?E AT $.Hamming: MEET AT THE BHAKTAPUR DURBAR SQUARE AT 4.Letters sent: 2050Wrong without a code: 300Wrong with Hamming:   33

Change 0.02 to 0.001 or 0.1 and run it again: a code that helps a lot on a slightly noisy line can’t keep up on a very noisy one.

Step 4: a message on a curve

Now Reed and Solomon’s code, on the clock of 251. value_at(points, x) gives the height at x of the one curve through the given points. You can use it without following every line: for each point it builds a piece that is that point’s height at that point and 0 at all the others, and adds the pieces up (this is called Lagrange interpolation, after the mathematician Joseph-Louis Lagrange). Dividing on the clock is pow(bottom, -1, P): the number that, multiplied by bottom, leaves 1.

add_extra puts the message’s 5 numbers at places 1 to 5 and adds the curve’s heights at 6, 7, 8 and 9. repair tries every way of choosing 5 of the 9 numbers it received (combinations from Python’s itertools lists them, 126 ways), draws the curve through them, and counts how many of the 9 lie on it. Two different curves of this kind can share at most 4 points, so if at most 2 numbers were damaged, the true curve passes through at least 7, and no wrong curve can.

from itertools import combinations P = 251 def value_at(points, x):    total = 0    for i in range(len(points)):        xi, yi = points[i]        top = 1        bottom = 1        for j in range(len(points)):            if j != i:                xj = points[j][0]                top = top * (x - xj) % P                bottom = bottom * (xi - xj) % P        total = (total + yi * top * pow(bottom, -1, P)) % P    return total def add_extra(message):    points = []    for x in range(1, 6):        points.append((x, message[x - 1]))    return message + [value_at(points, x) for x in [6, 7, 8, 9]] def repair(received):    for xs in combinations(range(1, 10), 5):        points = [(x, received[x - 1]) for x in xs]        curve = [value_at(points, x) for x in range(1, 10)]        agree = 0        for x in range(9):            if curve[x] == received[x]:                agree = agree + 1        if agree >= 7:            return curve[:5]    return None def text(numbers):    if numbers is None:        return "can't repair"    return ''.join(chr(n) for n in numbers) sent = add_extra([ord(letter) for letter in 'NEPAL'])print('Sent:                 ', sent)damaged = sent[:]damaged[1] = 0damaged[6] = 200print('Two bytes scratched:  ', damaged, '->', text(repair(damaged)))damaged[3] = 7print('Three bytes scratched:', damaged, '->', text(repair(damaged)))

Output

Sent:                  [78, 69, 80, 65, 76, 12, 121, 247, 81]Two bytes scratched:   [78, 0, 80, 65, 76, 12, 200, 247, 81] -> NEPALThree bytes scratched: [78, 0, 80, 7, 76, 12, 200, 247, 81] -> can't repair

The first five numbers are the letters of NEPAL; the last four are the extra numbers. Two scratched numbers, one in the message and one extra, are repaired. Three are too many for four extra numbers, and here the program says so rather than guessing. (With more damage than it can repair, a code can also be fooled: three unlucky scratches might happen to lie on a different curve with four of the true numbers. That is why each level promises only so many.)

Step 5: scratches in a row

Last, the test that matters for a QR code. NEPAL is sent both ways: with Hamming’s code (70 bits) and with the curve’s 9 numbers as bytes (72 bits). The program scratches a run of neighboring bits, flipping every one, at every place it could start, and counts how often each code still gives back NEPAL.

from itertools import combinations P = 251 def encode(data):    w = [0] * 8    w[3], w[5], w[6], w[7] = data    for k in [1, 2, 4]:        ones = 0        for p in range(1, 8):            if p != k and (p // k) % 2 == 1:                ones = ones + w[p]        w[k] = ones % 2    return w[1:] def wrong_bit(word):    place = 0    for k in [1, 2, 4]:        ones = 0        for p in range(1, 8):            if (p // k) % 2 == 1:                ones = ones + word[p - 1]        if ones % 2 == 1:            place = place + k    return place def value_at(points, x):    total = 0    for i in range(len(points)):        xi, yi = points[i]        top = 1        bottom = 1        for j in range(len(points)):            if j != i:                xj = points[j][0]                top = top * (x - xj) % P                bottom = bottom * (xi - xj) % P        total = (total + yi * top * pow(bottom, -1, P)) % P    return total def add_extra(message):    points = [(x, message[x - 1]) for x in range(1, 6)]    return message + [value_at(points, x) for x in [6, 7, 8, 9]] def repair(received):    for xs in combinations(range(1, 10), 5):        points = [(x, received[x - 1]) for x in xs]        curve = [value_at(points, x) for x in range(1, 10)]        agree = 0        for x in range(9):            if curve[x] == received[x]:                agree = agree + 1        if agree >= 7:            return curve[:5]    return None def to_bits(numbers):    bits = []    for n in numbers:        for value in [128, 64, 32, 16, 8, 4, 2, 1]:            bits.append(n // value % 2)    return bits def to_numbers(bits):    numbers = []    for i in range(0, len(bits), 8):        n = 0        for b in bits[i:i + 8]:            n = n * 2 + b        numbers.append(n)    return numbers def hamming_read(received):    bits = []    for i in range(0, len(received), 7):        word = received[i:i + 7]        place = wrong_bit(word)        if place != 0:            word[place - 1] = 1 - word[place - 1]        bits = bits + [word[2], word[4], word[5], word[6]]    return to_numbers(bits) def scratch(bits, start, length):    out = bits[:]    for i in range(start, start + length):        out[i] = 1 - out[i]    return out message = [ord(letter) for letter in 'NEPAL']plain = to_bits(message)hamming = []for i in range(0, len(plain), 4):    hamming = hamming + encode(plain[i:i + 4])as_bytes = to_bits(add_extra(message))print('Bits sent: Hamming', len(hamming), ', bytes', len(as_bytes)) for length in [1, 2, 9, 10]:    ham_ok = 0    for start in range(len(hamming) - length + 1):        if hamming_read(scratch(hamming, start, length)) == message:            ham_ok = ham_ok + 1    byte_ok = 0    for start in range(len(as_bytes) - length + 1):        if repair(to_numbers(scratch(as_bytes, start, length))) == message:            byte_ok = byte_ok + 1    unit = 'bit ' if length == 1 else 'bits'    print(f'{length:2} {unit} in a row: Hamming survives {ham_ok:2} of {len(hamming) - length + 1},',          f'bytes survive {byte_ok:2} of {len(as_bytes) - length + 1}')

Output

Bits sent: Hamming 70 , bytes 72 1 bit  in a row: Hamming survives 70 of 70, bytes survive 72 of 72 2 bits in a row: Hamming survives  9 of 69, bytes survive 71 of 71 9 bits in a row: Hamming survives  0 of 62, bytes survive 64 of 6410 bits in a row: Hamming survives  0 of 61, bytes survive 56 of 63

What your program shows

  • a.Three check bits find any one flipped bit among seven: 112 of 112, every message, every place (Step 2). The odd checks, added up, are the place.
  • b.Two flips are always noticed and never put right: 336 of 336 noticed, 0 repaired. “Fixing” them makes three wrong bits.
  • c.On a noisy line, the code turns 300 wrong letters into 33 (Step 3), for 7 bits sent where 4 would do.
  • d.Against scratches, bytes win (Step 5). Hamming’s code, with almost the same number of bits, is beaten by most scratches only 2 bits long: it survives only the 9 that happen to fall across the border between two of its 7-bit groups. The byte code survives every scratch up to 9 bits long, wherever it lands, because 9 neighboring bits can touch at most 2 bytes. At 10 bits it starts to fail: 7 of the 63 places touch 3 bytes.

That is why QR codes use Reed–Solomon codes. The widget at the top is the same idea, bigger. Its code is 70 bytes, and textbooks/make_scratch_qr.py, the program that made it for this page, reads from the code’s own tables how many of them are extra at each level:

Level Extra bytes Bytes that can be repaired Of all 70 The maker’s figure
L 15 7 10.0% about 7%
M 26 13 18.6% about 15%
Q 2 blocks × 18 2 × 9 = 18 25.7% about 25%
H 2 blocks × 22 2 × 11 = 22 31.4% about 30%

Each level repairs half as many bytes as it has extra ones (rounded down), as your repair does: errors in unknown places cost two extra bytes each. Denso Wave gives the levels as about 7%, 15%, 25% and 30%; for this small code they come out a little higher. At Q and H the code is split into two blocks, each repaired on its own, with their bytes woven between each other across the code (press Show the bytes at Q: blue and green). And each byte’s 8 bits sit together in a compact patch, so a scratch spoils few whole bytes rather than a bit of many. The same program checked all four codes with two real decoders: each one still read when a block had as many damaged bytes as the table says it can repair, and failed with one more.

Going further still

Longer Hamming codes. With 4 checks there are 2 × 2 × 2 × 2 = 16 patterns: “nothing wrong” and 15 places. So 4 check bits, at places 1, 2, 4 and 8, guard 11 message bits, 15 bits in all. Each check watches the places with a 1 in its own binary digit. That is Challenge ★★ below. With 5 checks, 26 message bits in 31; the longer the code, the smaller the share of check bits, and the more likely two flips land in one code word.

Two flips, noticed for sure. Add one more parity bit over all 7 bits. One flip makes the count of all 8 odd; two flips leave it even but still make some circles odd. Now one flip is fixed and two are reported as two, never “fixed” wrongly. Many computers’ memory uses this kind of code, SECDED (single error correction, double error detection). That is Challenge ★★★.

Weaving. Hamming’s code survives a scratch better if its code words are woven together: send the first bit of every word, then the second of every word, and so on. A scratch of 10 neighboring bits then flips at most one bit in each word. Try it: change Step 5 so that hamming is sent woven, and count again.

A question to think about: a QR code at level H can repair 22 of its 70 bytes. Could a cleverer code of 70 bytes carry the same message and repair 40? How would you show that some number is impossible? (Your repair gives a hint: why must the true curve agree with more points than any wrong one?)

Challenges

Three challenges, each checked on the page: write your function in the program above the checker (press Edit), then press Check my program. The examples show what your function should give; there are more tests you can’t see.

★ Read the message

Write decode(word): word is a 7-bit Hamming code word (places 1 to 7, as a list of 7) with at most one bit flipped. Give back the 4 message bits, as they were sent, as a list.

def decode(word):    # word is 7 bits, places 1 to 7; at most one is wrong    return [0, 0, 0, 0] print(decode([0, 1, 1, 0, 1, 1, 1]))

Try it

★ Read the message

  1. Nothing wrong decode([0, 1, 1, 0, 0, 1, 1]) Should give [1, 0, 1, 1] Yours —
  2. Bit 5 flipped decode([0, 1, 1, 0, 1, 1, 1]) Should give [1, 0, 1, 1] Yours —
  3. A check bit flipped decode([1, 0, 0, 0, 0, 0, 0]) Should give [0, 0, 0, 0] Yours —
Tests passed
— / 9
Challenge
★ solved

Write your decode in the program above (Edit), then check it.

A test your function gets wrong, anything it prints, and any error appear here.

Hint
Answer

★★ Fifteen bits

Write wrong_bit_15(word): word is a 15-bit Hamming code word (places 1 to 15) whose check bits are at places 1, 2, 4 and 8, each making the count of 1s even among the places with a 1 in its binary digit. Give back the place of the one flipped bit, or 0 if none.

def wrong_bit_15(word):    # word is 15 bits, places 1 to 15    return 0 print(wrong_bit_15([1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0]))

Try it

★★ Fifteen bits

  1. Nothing wrong wrong_bit_15([0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0]) Should give 0 Yours —
  2. Bit 5 flipped wrong_bit_15([1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0]) Should give 5 Yours —
  3. A check bit, 8, flipped wrong_bit_15([0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]) Should give 8 Yours —
Tests passed
— / 9
Challenge
★★ solved

Write your wrong_bit_15 in the program above (Edit), then check it.

A test your function gets wrong, anything it prints, and any error appear here.

Hint
Answer

★★★ One fixed, two reported

Write check_8(word). word is 8 bits: word[1] to word[7] are a 7-bit Hamming code word (place p at index p), and word[0] is an extra parity bit that makes the count of 1s in all 8 even. Give back 'fine' if nothing is wrong, the index (0 to 7) of the wrong bit if exactly one is wrong, or 'two' if two bits are wrong.

def check_8(word):    # word[0] is the extra parity bit; word[1] to word[7] are places 1 to 7    return 'fine' print(check_8([0, 0, 1, 1, 0, 1, 1, 1]))

Try it

★★★ One fixed, two reported

  1. Nothing wrong check_8([0, 0, 1, 1, 0, 0, 1, 1]) Should give 'fine' Yours —
  2. Bit 5 flipped check_8([0, 0, 1, 1, 0, 1, 1, 1]) Should give 5 Yours —
  3. The extra bit flipped check_8([1, 0, 1, 1, 0, 0, 1, 1]) Should give 0 Yours —
Tests passed
— / 9
Challenge
★★★ solved

Write your check_8 in the program above (Edit), then check it.

A test your function gets wrong, anything it prints, and any error appear here.

Hint
Another hint
Answer

Questions

In Hamming’s 7-bit code, Checks 1 and 2 are odd and Check 4 is even. Which bit flipped?

Pick an answer to see why.

Why do QR codes use a Reed–Solomon code rather than Hamming’s?

Pick an answer to see why.

a.

Explain why the odd checks in Hamming’s code, added up, give the number of the bit that flipped.

Answer
b.

A code adds 4 extra bytes to a message of 5 bytes, Reed–Solomon style. How many damaged bytes can it repair if the reader doesn’t know where they are? How many if it does (smudges it can see)? Explain the difference.

Answer
c.

You are printing QR codes for the plant labels in a school garden, where they will be rained on, muddied and scratched, and the message is short. Which error-correction level would you choose, and what does it cost?

Answer
d.

At level H, a single scratch across one of the three big corner squares can stop a code scanning, though no byte is damaged. Explain why, and say what the extra bytes do and don’t protect.

Answer

Where to go next