You are on page 1of 14

ĐẠI HỌC QUỐC GIA TP.

HỒ CHÍ MINH
TRƯỜNG ĐẠI HỌC BÁCH KHOA

BÁO CÁO BÀI TẬP LỚN MÔN ĐẠI SỐ TUYẾN TÍNH

ĐỀ TÀI MÃ HAMMING (7,4)

LỚP P03, NHÓM 9

STT Tên MSSV


1 Trang Huỳnh Gia Bảo 2310260
2 Trương Gia Hưng 2311364
3 Trịnh Quốc Nam Kha 2352463
4 Tung Võ Diễm My 2352771
5 Trương Hữu Trí 2313629

Giảng viên hướng dẫn: TS. Đặng Văn Vinh

TP. HCM, tháng 12 năm 2023


LỜI CẢM ƠN
Trước hết, nhóm chúng em muốn bày tỏ lòng biết ơn chân thành đến Trường Đại học
Bách Khoa – ĐHQG TP.HCM vì đã tích hợp môn Đại Số Tuyến Tính vào chương trình học.
Đặc biệt, chúng em muốn bày tỏ lòng biết ơn sâu sắc đến giảng viên – Thầy Đặng Văn Vinh,
vì đã chia sẻ những kiến thức quý báu trong thời gian học vừa qua. Tham gia lớp học của
thầy, chúng em đã cảm nhận sự phát triển trong tư duy và nghiêm túc hơn trong quá trình
học tập. Đây là những kiến thức quan trọng, là nền tảng quan trọng cho tương lai của chúng
em. Được sự hướng dẫn của thầy và những kiến thức đã tích lũy, chúng em tự tin trình bày
bài báo cáo bài tập lớn với đề tài được giao.
Qua quá trình thực hiện bài báo cáo này, nhóm 5 chúng em đã học được nhiều kiến thức
mới và hữu ích. Mặc dù đã cố gắng hết sức, nhưng với vốn kiến thức còn hạn chế, không
tránh khỏi những thiếu sót. Chúng em rất mong nhận được sự xem xét và góp ý của thầy để
bài báo cáo của nhóm 5 chúng em trở nên hoàn thiện hơn.
Chân thành cảm ơn thầy.

[1]
MỤC LỤC

1. Richard Wesley Hamming và lịch sử ra đời của mã Hamming .................................2


2, Cơ sở lý thuyết của mã Hamming (7,4) ..........................................................................2
2.1 Ma trận tạo mã và ma trận kiểm tra .......................................................................... 2
2.2 Các loại mã khác ........................................................................................................3
3. Cơ sở lý thuyết của giải thích ma trận tạo mã và ma trận kiểm tra ................................4
3.1 Ma trận tạo mã ...........................................................................................................4
3.2 Ma trận kiểm tra ....................................................................................................... 5
4. Ứng dụng và ví dụ ...........................................................................................................5
4.1. Ứng dụng .................................................................................................................. 5
4.2. Cách sử dụng ............................................................................................................ 6
a. Bằng thuật toán ma trận ............................................................................................6
b. Thuật mã Hamming ................................................................................................. 7
c. Matlab ....................................................................................................................... 8
Một số trang web tham khảo .............................................................................................12

[2]
NỘI DUNG

1. Richard Wesley Hamming và lịch sử ra đời của mã Hamming

Richard Wesley Hamming (11/2/1915 - 7/1/1998) là một nhà toán học người Mỹ có công
trình nghiên cứu nhiều ý nghĩa về kỹ thuật máy tính và viễn thông. Những đóng góp của ông
bao gồm mã Hamming, cửa sổ Hamming, số Hamming, gói hình cầu, khái niệm đồ thị
Hamming và khoảng cách Hamming.
Trong những năm của thập niên kỷ 1940, Hamming làm việc tại Bell Labs trên máy tính
Bell Model V, một máy điện cơ (electromechanical) dùng rơ-le (relay-based), với tốc độ rất
chậm, mấy giây đồng hồ một chu kỳ máy. Nhập liệu được cho vào máy bằng những cái thẻ đục
lỗ (punch card), và hầu như máy luôn luôn gây lỗi trong khi đọc. Trong những ngày làm việc
trong tuần, những mã đặc biệt được dùng để tìm ra lỗi và mỗi khi tìm được, nó nhấp nháy đèn
báo hiệu, báo cho người điều khiển biết để họ sửa, điều chỉnh máy lại. Trong thời gian ngoài
giờ làm việc hoặc trong những ngày cuối tuần, khi người điều khiển máy không có mặt, mỗi
khi có lỗi xảy ra, máy tính tự động bỏ qua chương trình đang chạy và chuyển sang công việc
khác.
Hamming thường làm việc trong những ngày cuối tuần và ông càng ngày càng trở nên bực
tức mỗi khi ông phải khởi động lại các chương trình ứng dụng từ đầu, do chất lượng kém,
không đáng tin cậy của bộ máy đọc các thẻ đục lỗ. Mấy năm tiếp theo đó, ông dồn tâm lực vào
việc xây dựng hàng loạt các thuật toán có hiệu quả cao để giải quyết vấn đề sửa lỗi.
Năm 1950, ông đã công bố một phương pháp mà hiện nay được biết là Mã Hamming. Một
số chương trình ứng dụng hiện thời vẫn còn sử dụng mã này của ông.

2. Cơ sở lý thuyết của mã Hamming (7,4)

2.1. Ma trận tạo mã và ma trận kiểm tra

Trong viễn thông, mã Hamming là một mã sửa lỗi tuyến tính (linear error-correcting code),
được đặt tên theo tên của người phát minh ra nó. Mã Hamming có thể phát hiện một bit hoặc
hai bit bị lỗi. Mã Hamming còn có thể sửa các lỗi do một bit bị sai gây ra. Ngược lại với mã
của ông, mã chẵn lẻ (parity code) đơn giản vừa không có khả năng phát hiện các lỗi khi 2 bit
cùng một lúc bị hoán vị (0 thành 1 và ngược lại), vừa không thể giúp để sửa được các lỗi mà nó
phát hiện được.
Hiện thời, khi nói đến mã Hamming chúng ta thực ra là muốn nói đến mã (7,4) mà
Hamming công bố năm 1950. Với mỗi nhóm 4 bit dữ liệu, mã Hamming thêm 3 bit kiểm tra
Thuật toán (7,4) của Hamming có thể sửa chữa bất cứ một bit lỗi nào, và phát hiện tất cả lỗi của
1 bit, và các lỗi của 2 bit gây ra. Điều này có nghĩa là đối với tất cả các phương tiện truyền
[3]
thông không có chùm lỗi đột phát (burst errors) xảy ra, mã (7,4) của Hamming rất có hiệu quả
(trừ phi phương tiện truyền thông có độ nhiễu rất cao thì nó mới có thể gây cho 2 bit trong số 7
bit truyền bị đảo lộn).

2.2. Các loại mã khác

Nhiều mã phát hiện lỗi đơn giản đã được sử dụng trước khi có mã Hamming, nhưng không
có mã nào hiệu quả bằng mã Hamming với một tổng phí tương đương.
• Mã chẵn lẻ (bit chẵn lẻ) hoặc bit kiểm tra (Parity)
Mã chẵn lẻ thêm một bit vào trong dữ liệu, và bit cho thêm này cho biết số lượng bit có giá
trị 1 của đoạn dữ liệu nằm trước là một số chẵn hay một số lẻ. Nếu một bit bị thay đổi trong
quá trình truyền dữ liệu, giá trị chẵn lẻ trong thông điệp sẽ thay đổi và do đó có thể phát hiện
được lỗi (Chú ý rằng bit bị thay đổi có thể lại chính là bit kiểm tra). Theo quy ước chung, bit
kiểm tra có giá trị bằng 1 nếu số lượng bit có giá trị một trong dữ liệu là một số lẻ, và giá trị
của bit kiểm tra bằng 0 nếu số lượng bit có giá trị một trong dữ liệu là một số chẵn. Nói cách
khác, nếu đoạn dữ liệu và bit kiểm tra được gộp lại cùng với nhau, số lượng bit có giá trị bằng
1 luôn luôn là một số chẵn.
Mã chẵn lẻ không chỉ ra bit nào chứa lỗi ngay cả khi nó có thể phát hiện ra lỗi đó. Dữ liệu
phải được loại bỏ hoàn toàn và truyền lại từ đầu. Trên môi trường truyền nhiễu, việc truyền
thành công có thể mất nhiều thời gian hoặc có thể không bao giờ xảy ra. Tuy nhiên, mặc dù
chất lượng kiểm tra tính chẵn lẻ kém vì nó chỉ sử dụng một bit duy nhất nhưng phương pháp
này lại tiết kiệm chi phí nhất.
• Mã-hai-trong-năm (Two-out-of-five code)
Xuất hiện trong những năm của thập niên kỷ 1940. Mã này đảm bảo mỗi một khối 5 bit
(còn được gọi là khối-5) có chính xác hai bit có giá trị bằng 1. Máy tính có thể nhận ra là dữ
liệu nhập vào có lỗi nếu trong một khối 5 bit không 2 bit có giá trị bằng 1. Tuy thế, mã hai-
trong-năm cũng chỉ có thể phát hiện được một đơn vị bit mà thôi; nếu trong cùng một khối, một
bit bị lộn ngược thành giá trị 1, và một bit khác bị lộn ngược thành giá trị 0, quy luật hai-trong-
năm vẫn cho một giá trị đúng (remained true), và do đó nó không phát hiện là có lỗi xảy ra.
• Tái diễn dữ liệu (Repetition)
Một mã nữa được dùng trong thời gian này là mã hoạt động bằng cách nhắc đi nhắc lại bit
dữ liệu vài lần (tái diễn bit được truyền) để đảm bảo bit dữ liệu được truyền, truyền đến nơi
nhận trọn vẹn.
Chẳng hạn, nếu bit dữ liệu cần được truyền có giá trị bằng 1, một mã tái diễn n=3 sẽ cho
truyền gửi giá trị "111". Nếu ba bit nhận được không giống nhau, thì hiện trạng này báo cho ta
biết rằng, lỗi trong truyền thông đã xảy ra. Nếu kênh truyền không bị nhiễu, tương đối đảm bảo
thì với hầu hết các lần truyền trong nhóm ba bit được gửi, chỉ có một bit là bị thay đổi. Do đó

[4]
các nhóm 001, 010, và 100 đều tương đương cho một bit có giá trị 0, trong khi đó các nhóm
110, 101, và 011 đều tương đương cho một bit có giá trị 1 - lưu ý số lượng bit có giá trị 0 trong
các nhóm được coi là có giá trị 0, là đa số so với tổng số bit trong nhóm, hay 2 trong 3 bit,
tương đương như vậy các nhóm được coi là giá trị 1 có số lượng bit bằng 1 nhiều hơn là các bit
có giá trị 0 trong nhóm. Một mã có khả năng tái dựng lại thông điệp gốc trong một môi trường
nhiễu lỗi được gọi là mã "sửa lỗi" (error-correcting code).
Tuy nhiên, những mã như vậy không thể sửa chữa chính xác tất cả các lỗi. Trong ví dụ
trước đó, nếu kênh lật hai bit và bộ thu nhận được 001, hệ thống sẽ phát hiện lỗi nhưng kết luận
rằng bit gốc là 0, điều này không chính xác. Nếu chúng ta tăng kích thước của chuỗi bit lên bốn,
ta có thể phát hiện tất cả các lỗi hai bit nhưng không thể sửa chúng (số lượng bit chẵn lẻ là
chẵn); ở mức năm bit, chúng ta có thể phát hiện và sửa tất cả các lỗi hai bit, nhưng không thể
sửa tất cả các lỗi ba bit.
Hơn nữa, việc tăng kích thước của chuỗi bit chẵn lẻ là không hiệu quả, làm giảm thông
lượng ba lần trong trường hợp ban đầu và hiệu quả giảm mạnh khi ta tăng số lần mỗi bit được
sao chép để phát hiện và sửa nhiều lỗi hơn.

3. Cơ sở lý thuyết giải thích ma trận tạo mã và ma trận kiểm tra

3.1. Ma trận tạo mã

 Trong lý thuyết mã hóa, ma trận tạo mã là ma trận có các hàng tạo thành cơ sở cho mã
tuyến tính. Các từ mã là tất cả các tổ hợp tuyến tính của các hàng của ma trận này, nghĩa là
mã tuyến tính là không gian hàng của ma trận tạo mã.
 Nếu G là ma trận, nó tạo ra các từ mã của mã tuyến tính C bằng công thức: w = sG
 w là từ mã của mã tuyến tính C
 s là vectơ đầu vào bất kỳ.
 Cả w và s đều được coi là vectơ hàng.
 Ma trận tạo mã tuyến tính [n, k, d]q có định dạng k ×n
 n là độ dài của từ mã
 k là số bit thông tin (chiều của C dưới dạng không gian con vectơ)
 d là khoảng cách tối thiểu của mã
 q là kích thước của trường hữu hạn, nghĩa là số lượng ký hiệu trong bảng chữ cái (do
đó, q = 2 biểu thị mã nhị phân, v.v.)
 Số lượng bit dư thừa được ký hiệu là: r = n – k
Dạng của ma trận tạo mã
Dạng chuẩn của ma trận tạo mã là: G = [Ik | P]
Với I là ma trận k × k đơn vị và P là ma trận k × [n – k]. Khi ma trận tạo ở dạng
chuẩn, mã C là mã có hệ thống ở k vị trí tọa độ đầu tiên của nó
[5]
3.2. Ma trận kiểm tra

 Trong lý thuyết mã hóa, ma trận kiểm tra tính chẵn lẻ của mã khối tuyến tính C là ma trận
mô tả các quan hệ tuyến tính mà các thành phần của từ mã phải thỏa mãn. Nó có thể được
sử dụng để quyết định xem một vectơ cụ thể có phải là từ mã hay không và cũng được sử
dụng trong các thuật toán giải mã.
 Về mặt hình thức, ma trận kiểm tra chẵn lẻ H của mã tuyến tính C là ma trận tạo của mã kép,
C⊥. Điều này có nghĩa là từ mã c thuộc C khi và chỉ khi tích vectơ ma trận Hc⊤ = 0
Dạng của ma trận kiểm tra
Các hàng của ma trận kiểm tra tính chẵn lẻ là các hệ số của phương trình kiểm tra tính chẵn
lẻ. Nghĩa là, chúng cho thấy sự kết hợp tuyến tính của các chữ số (thành phần) nhất định của
mỗi từ mã bằng 0 như thế nào.
Ví dụ

Biểu diễn gọn gàng các phương trình kiểm tra tính chẵn lẻ:
c3 + c4 = 0
c1 + c2 = 0
Điều đó phải được thỏa mãn đối với vectơ (c1, c2, c3, c4) là từ mã của C.
Từ định nghĩa của ma trận kiểm tra chẵn lẻ, nó theo trực tiếp khoảng cách tối thiểu
của mã là số d tối thiểu sao cho mọi d – 1 cột của ma trận kiểm tra chẵn lẻ H độc
lập tuyến tính trong khi tồn tại d cột của H phụ thuộc tuyến tính

4. Ứng dụng và ví dụ

4.1. Ứng dụng

Mã hóa kiểm tra lỗi ( Error Correction Coding – ECC ) được ứng dụng để làm tăng độ tin
cậy trong lưu trữ dữ liệu nhờ khả năng phát hiện và sửa lỗi dữ liệu bị hỏng xảy ra trong bộ nhớ.
Có nhiều thuật toán ECC được sử dụng với những ưu điểm khác nhau như mã Hamming,
Reed-Solomon, BCH, LDPC. Trong đó, mã Hamming phù hợp với việc kiểm soát lỗi trong bộ
nhớ và ưu điểm nổi bật là thuật toán mã hóa và giải mã đơn giản nên tốc độ mã hóa và giải mã
cao.

[6]
4.2. Cách sử dụng

a. Bằng thuật toán ma trận

 Xét trên trường cơ số 2 ( hệ nhị phân ), không gian véctơ gồm tất cả 7 bộ số 0 hoặc 1 trên
trường số.
 Xét ma trận H có các cột là véctơ khác không của �32 :

1 1 0 1 1 0 0
�= 1 0 1 1 0 1 0
0 1 1 1 0 1 1

 N(H) là không gian nghiệm của hệ phương trình tuyến tính HX = 0. Gọi ma trận G là ma
trận có họ véctơ hàng là một cơ sở của N(H)

1 1 0 1 1 0 0 0 1 1. 0 1 1 0 0 0
1 0 1 1 0 1 0 0 0 1 1 0 1 1 0 0
0 1 1 1 0 1 1 0 0 0 0 1 1 1 1 0

 Đặt x3 = a, x5 = b, x6 = c, x7 = d.
 X = a (1;1;1;0;0;0;0 ) + b (1;1;0;1;1;0;0) + c (0;1;0;1;0;1;0) + d ( 1;0;0;1;0;0;1)

 Khi đó :

1 1 1 0 0 0 0
1 1 0 1 1 0 0
�=
0 1 0 1 0 1 0
1 0 0 1 0 0 1

 Gọi E = { ℯ1 = (1;0;…;0); ℯ2 = (0;1;…;0); … ; ℯ7 = (0;…;0;1) } là cơ sở chính tắc của �72 và


{ c1;c2;…;c7} là họ véctơ cột của H.

 Ta có một vài nhận xét sau:


1. Hℯi = ci ≠0, ∀i ∈ { 1;…;7 }. . Suy ra ∀i, ℯi ∉ N(H).
( vì họ các véctơ cột của H khác không )
2. Nếu v ∈ N(H) , thì v + ℯi ∉ N(H).
( vì H(v+ℯi) = Hv + H ℯi = 0 + ci = ci ≠ 0 ).
3. Nếu u là một véctơ của Z2 thỏa Hu = ci => (u + ℯi ) ∈ N(H).
( vì H(u+ℯi) = Hu + Hℯi = ci + ci = 2ci = 0ci = 0 )
[7]
Ngoài ra ∀j ≠ i, (u + ej ) ∉ N(H).

b. Thuật mã Hamming

Ý tưởng thuật mã của Hamming rất đơn giản: thay vì truyền thông điệp a đi, người ta truyền
đi thông điệp v = aG ∈ N(H) đi và người nhận chỉ cần kiểm tra véctơ thu được có nằm trong
không gian nghiệm của ma trận H hay không để biết lỗi có xảy ra hay không.
 Cho đoạn mã a gồm 4 data bit, a = { a1; a2; a3; a4 }
 Thực hiện phép toán v = aG để mã hóa a => v thuộc N(H).
 Vì cột 3, 5, 6, 7 của G là các véctơ đơn vị của cơ sở chính tắc của v thuộc R4 => Các phần
tử của v ở các vị trí đó sẽ tạo nên véctơ a, các vị trí còn lại đóng vai trò như những check bit.
 Sau đó gửi véctơ v cho người nhận, thu được véctơ w( có thể khác véctơ v do sai sót trong
quá trình truyền dữ liệu).
 Nếu Hw = 0, tức w thuộc N(H) => Không có lỗi xảy ra, người nhận sẽ có được thông tin
cần chuyển a tạo nên từ các phần tử ở các vị trí 3, 5, 6, 7 của véctơ w.
 Nếu Hw ≠ 0 thì có lỗi xảy ra ở vị trí thứ i của w ( w= v + ℯ i ). Khi đó Hw=Hv + Hℯi = 0 +
Hℯi= ci. Dựa vào đây ta nhận biết lỗi nằm ở vị trí thứ i và sửa lỗi bằng cách đổi 0  1 tại vị
trí i của w.
 Giả sử thông tin cần chuyển đi là a = ( 1; 0; 1; 1 ).
Khi đó v = aG = ( 0; 0; 1; 0; 0; 1; 1 ).

Giả sử người nhận thu được véctơ w = ( 0; 0; 1; 0; 0; 1; 0 ). Khi đó Hw = ( 0; 0; 1)T = c7 . Như


vậy lỗi xảy ra ở vị trí thứ 7 của véctơ w. Ta sửa lỗi bằng cách thay phần tử thứ 7 của véctơ w từ
0 thành 1 và ta có véctơ w’ = ( 0; 0; 1; 0; 0; 1; 1), sau đó rút các phần tử ở vị trí thứ 3, 5, 6, 7
thu được data bit truyền đi ban đầu là a = ( 1; 0; 1; 1 ).

Lưu ý: Ta có thể xếp các phần tử khác không của �32 một cách tùy ý để tạo ra ma trận H. Khi
đó họ véctơ cột của G sẽ chứa 4 véctơ (1; 0; 0; 0)T , (0; 1; 0; 0)T , (0; 0; 1; 0)T , ( 0; 0; 0; 1)T.
Tùy vào vị trí của các véctơ này trong ma trận G mà ta biết được vị trí của các phần tử véctơ a
trong véctơ v = aG.

[8]
c. Matlab

clc;
H = [0 0 0 1 1 1 1;
0 1 1 0 0 1 1;
1 0 1 0 1 0 1];
G = [1 1 1 0 0 0 0;
1 0 0 1 1 0 0;
0 1 0 1 0 1 0;
1 1 0 1 0 0 1];
str = input('Nhập tin nhắn chữ: ');
a = reshape(dec2bin(str, 8).'-'0',[],4);
disp('Chuyển chữ về dạng nhị phân và tạo ma trận nhị phân với số cột
= 4:')
disp(num2str(a));
disp('-----------------------------');
disp('Chuyển thành ma trận mã Hamming:');
v = [];
for i = 1:height(a)
v = [v;mod(a(i,:)*G, 2)];
end
disp(num2str(v));
disp('-----------------------------');
x = input('Chọn vị trí muốn mã Hamming bị sai: ');
for i = 1:height(x)
v(x(i,1),x(i,2)) = mod(v(x(i,1),x(i,2))+1, 2);
end
disp('Mã Hamming bị sai: ');
disp(num2str(v));
disp('-----------------------------');
p = v;
disp('Kiểm tra mã Hamming ... ');
for i = 1:height(v)
u = v(i,:);
c = mod(H*u', 2);

[9]
y = c(1,1)*4 + c(2,1)*2 + c(3,1)*1;
if y ~= 0
msg = ['Có lỗi tại hàng ', num2str(i), ' cột ', num2str(y)];
disp(msg);
v(i,y) = mod(v(i,y)+1, 2);
end
end
disp('Mã Hamming sau khi sửa sai: ');
disp(num2str(v));
disp('-----------------------------');
b = [];
b1 = [];
for i = 1:height(v)
k = [v(i,3) v(i,5) v(i,6) v(i,7)];
b = [b;k];
k1 = [p(i,3) p(i,5) p(i,6) p(i,7)];
b1 = [b1;k1];
end
c = char(bin2dec(reshape(char(b+'0'), 8,[]).'));
c1 = char(bin2dec(reshape(char(b1+'0'), 8,[]).'));
disp('Chuyển lại dữ liệu chữ: ');
disp(c')
disp('Nếu không phát hiện và sửa lỗi dữ liệu chữ sẽ là: ');
disp(c1')

[10]
Figure 1 Nhập chữ admin vào code

Figure 2 Chọn vị trí muốn sai, ví dụ hàng 1 cột 4, hàng 4 cột 6 và hàng 5 cột 6

[11]
Figure 3 Phát hiện lỗi sai và sửa

Các câu lệnh dùng trong matlab

Lệnh Tác dụng

Num2str Chuyển dữ liệu từ kiểu số sang kiểu


chuỗi
Disp Hiển thị giá trị chuỗi

If…else…end Nếu…Thì…Nếu không thì

Mod(số/biến chia , số bị chia ) Toán tử modulo ( lấy phần dư )

For<biến>=<giá trị đầu> : <mức Vòng lặp khi biết số lần lặp
tăng> : < giá trị cuối >
<biến> = [A ; B ; C ] Lập ma trận có các hàng A , B,C các
phần tử trên mỗi hàng cách nhau bởi
dấu cách
~ Toán tử logic : không ( not )

[12]
Một số trang web tham khảo
1. Đặng Văn Vinh (2022). Giáo Trình Đại Số Tuyến Tính. Nhà Xuất Bản Đại Học
Quốc Gia TP.HCM.
2. https://www.sciencedirect.com/topics/computer-science/generator-matrix
3. https://www.sciencedirect.com/topics/engineering/parity-check-matrix
4. https://math.libretexts.org/Bookshelves/Abstract_and_Geometric_Algebra/Abstra
ct_Algebra%3A_Theory_and_Applications_(Judson)/08%3A_Algebraic_Coding
_Theory/8.04%3A_Parity-Check_and_Generator_Matrices
5. https://www.techtarget.com/whatis/definition/Hamming-code
6. https://fr.wikipedia.org/wiki/Code_de_Hamming_(7,4)
7. https://www.researchgate.net/publication/298710950_Implementing_7_4_hammi
ng_code_with_extra_one_parity_bit_and_bit_reverse_scheme_to_correct_errors_
at_the_receiver_side

[13]

You might also like