This commit is contained in:
s2ks
2022-05-27 22:37:32 +02:00
parent bcc53c48a7
commit c88d3bcf85
2 changed files with 121 additions and 122 deletions

View File

@ -1261,13 +1261,13 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
"Value 8 is expressed in binary as '1000', which will be encoded", "Value 8 is expressed in binary as '1000', which will be encoded",
"with the pattern 'pppdpddd', where p is a parity bit and d a data bit,\n", "with the pattern 'pppdpddd', where p is a parity bit and d a data bit,\n",
"or '10101' (Value 21) will result into (pppdpdddpd) '1001101011'.\n", "or '10101' (Value 21) will result into (pppdpdddpd) '1001101011'.\n",
"The answer should be given as a string containing only 1s and 0s.\n", "The answer should be given as a string containing only 1s and 0s.\n",
"NOTE: the endianness of the data bits is reversed in relation to the endianness of the parity bits.\n", "NOTE: the endianness of the data bits is reversed in relation to the endianness of the parity bits.\n",
"NOTE: The bit at index zero is the overall parity bit, this should be set last.\n", "NOTE: The bit at index zero is the overall parity bit, this should be set last.\n",
"NOTE 2: You should watch the Hamming Code video from 3Blue1Brown, which explains the 'rule' of encoding,", "NOTE 2: You should watch the Hamming Code video from 3Blue1Brown, which explains the 'rule' of encoding,",
"including the first index parity bit mentioned in the previous note.\n\n", "including the first index parity bit mentioned in the previous note.\n\n",
"Extra rule for encoding:\n", "Extra rule for encoding:\n",
"There should be no leading zeros in the 'data bit' section", "There should be no leading zeros in the 'data bit' section",
].join(" "); ].join(" ");
}, },
gen: (): number => { gen: (): number => {
@ -1290,7 +1290,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
"Note: The length of the binary string is dynamic, but it's encoding/decoding follows Hamming's 'rule'\n", "Note: The length of the binary string is dynamic, but it's encoding/decoding follows Hamming's 'rule'\n",
"Note 2: Index 0 is an 'overall' parity bit. Watch the Hamming code video from 3Blue1Brown for more information\n", "Note 2: Index 0 is an 'overall' parity bit. Watch the Hamming code video from 3Blue1Brown for more information\n",
"Note 3: There's a ~55% chance for an altered Bit. So... MAYBE there is an altered Bit 😉\n", "Note 3: There's a ~55% chance for an altered Bit. So... MAYBE there is an altered Bit 😉\n",
"Note: The endianness of the \ "Note: The endianness of the \
encoded decimal value is reversed in relation to the endianness of the Hamming code. Where \ encoded decimal value is reversed in relation to the endianness of the Hamming code. Where \
the Hamming code is expressed as little-endian (LSB at index 0), the decimal value encoded in it is expressed as big-endian \ the Hamming code is expressed as little-endian (LSB at index 0), the decimal value encoded in it is expressed as big-endian \
(MSB at index 0)\n", (MSB at index 0)\n",

View File

@ -1,156 +1,155 @@
export function HammingEncode(data: number): string { export function HammingEncode(data: number): string {
const enc: Array<number> = [0];
const data_bits: Array<any> = data.toString(2).split("").reverse();
const enc: Array<number> = [0]; data_bits.forEach((e, i, a) => {
const data_bits: Array<any> = data.toString(2).split("").reverse(); a[i] = parseInt(e);
});
data_bits.forEach((e, i, a) => { let k = data_bits.length;
a[i] = parseInt(e);
});
let k = data_bits.length; /* NOTE: writing the data like this flips the endianness, this is what the
* original implementation by Hedrauta did so I'm keeping it like it was. */
for (let i = 1; k > 0; i++) {
if ((i & (i - 1)) != 0) {
enc[i] = data_bits[--k];
} else {
enc[i] = 0;
}
}
/* NOTE: writing the data like this flips the endianness, this is what the let parity: any = 0;
* original implementation by Hedrauta did so I'm keeping it like it was. */
for(let i = 1; k > 0; i++) {
if((i & (i - 1)) != 0) {
enc[i] = data_bits[--k];
} else {
enc[i] = 0;
}
}
let parity: any = 0; /* Figure out the subsection parities */
for (let i = 0; i < enc.length; i++) {
if (enc[i]) {
parity ^= i;
}
}
/* Figure out the subsection parities */ parity = parity.toString(2).split("").reverse();
for(let i = 0; i < enc.length; i++) { parity.forEach((e: any, i: any, a: any) => {
if(enc[i]) { a[i] = parseInt(e);
parity ^= i; });
}
}
parity = parity.toString(2).split("").reverse(); /* Set the parity bits accordingly */
parity.forEach((e: any, i: any , a: any) => { for (let i = 0; i < parity.length; i++) {
a[i] = parseInt(e); enc[2 ** i] = parity[i] ? 1 : 0;
}); }
/* Set the parity bits accordingly */ parity = 0;
for(let i = 0; i < parity.length; i++) { /* Figure out the overall parity for the entire block */
enc[2 ** i] = parity[i] ? 1 : 0; for (let i = 0; i < enc.length; i++) {
} if (enc[i]) {
parity++;
}
}
parity = 0; /* Finally set the overall parity bit */
/* Figure out the overall parity for the entire block */ enc[0] = parity % 2 == 0 ? 0 : 1;
for(let i = 0; i < enc.length; i++) {
if(enc[i]) {
parity++;
}
}
/* Finally set the overall parity bit */ return enc.join("");
enc[0] = parity % 2 == 0 ? 0 : 1;
return enc.join("");
} }
export function HammingEncodeProperly(data: number): string { export function HammingEncodeProperly(data: number): string {
/* How many bits do we need? /* How many bits do we need?
* n = 2^m * n = 2^m
* k = 2^m - m - 1 * k = 2^m - m - 1
* where k is the number of data bits, m the number * where k is the number of data bits, m the number
* of parity bits and n the number of total bits. */ * of parity bits and n the number of total bits. */
let m = 1; let m = 1;
while((2 ** ((2 ** m) - m - 1)) < data) { while (2 ** (2 ** m - m - 1) < data) {
m++; m++;
} }
const n: number = (2 ** m); const n: number = 2 ** m;
const k: number = (2 ** m) - m - 1; const k: number = 2 ** m - m - 1;
const enc: Array<number> = [0]; const enc: Array<number> = [0];
const data_bits: Array<any> = data.toString(2).split("").reverse(); const data_bits: Array<any> = data.toString(2).split("").reverse();
data_bits.forEach((e, i, a) => { data_bits.forEach((e, i, a) => {
a[i] = parseInt(e); a[i] = parseInt(e);
}); });
/* Flip endianness as in the original implementation by Hedrauta /* Flip endianness as in the original implementation by Hedrauta
* and write the data back to front * and write the data back to front
* XXX why do we do this? */ * XXX why do we do this? */
for(let i = 1, j = k; i < n; i++) { for (let i = 1, j = k; i < n; i++) {
if((i & (i - 1)) != 0) { if ((i & (i - 1)) != 0) {
enc[i] = data_bits[--j] ? data_bits[j] : 0; enc[i] = data_bits[--j] ? data_bits[j] : 0;
} }
} }
let parity: any = 0; let parity: any = 0;
/* Figure out the subsection parities */ /* Figure out the subsection parities */
for(let i = 0; i < n; i++) { for (let i = 0; i < n; i++) {
if(enc[i]) { if (enc[i]) {
parity ^= i; parity ^= i;
} }
} }
parity = parity.toString(2).split("").reverse(); parity = parity.toString(2).split("").reverse();
parity.forEach((e: any, i: any , a: any) => { parity.forEach((e: any, i: any, a: any) => {
a[i] = parseInt(e); a[i] = parseInt(e);
}); });
/* Set the parity bits accordingly */ /* Set the parity bits accordingly */
for(let i = 0; i < m; i++) { for (let i = 0; i < m; i++) {
enc[2 ** i] = parity[i] ? 1 : 0; enc[2 ** i] = parity[i] ? 1 : 0;
} }
parity = 0; parity = 0;
/* Figure out the overall parity for the entire block */ /* Figure out the overall parity for the entire block */
for(let i = 0; i < n; i++) { for (let i = 0; i < n; i++) {
if(enc[i]) { if (enc[i]) {
parity++; parity++;
} }
} }
/* Finally set the overall parity bit */ /* Finally set the overall parity bit */
enc[0] = parity % 2 == 0 ? 0 : 1; enc[0] = parity % 2 == 0 ? 0 : 1;
return enc.join(""); return enc.join("");
} }
export function HammingDecode(data: string): number { export function HammingDecode(data: string): number {
let err = 0; let err = 0;
const bits: Array<number> = []; const bits: Array<number> = [];
/* TODO why not just work with an array of digits from the start? */ /* TODO why not just work with an array of digits from the start? */
for(const i in data.split("")) { for (const i in data.split("")) {
const bit = parseInt(data[i]); const bit = parseInt(data[i]);
bits[i] = bit; bits[i] = bit;
if(bit) { if (bit) {
err ^= +i; err ^= +i;
} }
} }
/* If err != 0 then it spells out the index of the bit that was flipped */ /* If err != 0 then it spells out the index of the bit that was flipped */
if(err) { if (err) {
/* Flip to correct */ /* Flip to correct */
bits[err] = bits[err] ? 0 : 1; bits[err] = bits[err] ? 0 : 1;
} }
/* Now we have to read the message, bit 0 is unused (it's the overall parity bit /* Now we have to read the message, bit 0 is unused (it's the overall parity bit
* which we don't care about). Each bit at an index that is a power of 2 is * which we don't care about). Each bit at an index that is a power of 2 is
* a parity bit and not part of the actual message. */ * a parity bit and not part of the actual message. */
let ans = ''; let ans = "";
for(let i = 1; i < bits.length; i++) { for (let i = 1; i < bits.length; i++) {
/* i is not a power of two so it's not a parity bit */ /* i is not a power of two so it's not a parity bit */
if((i & (i - 1)) != 0) { if ((i & (i - 1)) != 0) {
ans += bits[i]; ans += bits[i];
} }
} }
/* TODO to avoid ambiguity about endianness why not let the player return the extracted (and corrected) /* TODO to avoid ambiguity about endianness why not let the player return the extracted (and corrected)
* data bits, rather than guessing at how to convert it to a decimal string? */ * data bits, rather than guessing at how to convert it to a decimal string? */
return parseInt(ans, 2); return parseInt(ans, 2);
} }