diff --git a/src/data/codingcontracttypes.ts b/src/data/codingcontracttypes.ts index 6ee30e76d..f977da091 100644 --- a/src/data/codingcontracttypes.ts +++ b/src/data/codingcontracttypes.ts @@ -1261,13 +1261,13 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [ "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", "or '10101' (Value 21) will result into (pppdpdddpd) '1001101011'.\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", + "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 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,", "including the first index parity bit mentioned in the previous note.\n\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(" "); }, 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 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: The endianness of the \ + "Note: The endianness of the \ 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 \ (MSB at index 0)\n", diff --git a/src/utils/HammingCodeTools.ts b/src/utils/HammingCodeTools.ts index dd99e084c..e057f3a0a 100644 --- a/src/utils/HammingCodeTools.ts +++ b/src/utils/HammingCodeTools.ts @@ -1,156 +1,155 @@ export function HammingEncode(data: number): string { + const enc: Array = [0]; + const data_bits: Array = data.toString(2).split("").reverse(); - const enc: Array = [0]; - const data_bits: Array = data.toString(2).split("").reverse(); + data_bits.forEach((e, i, a) => { + a[i] = parseInt(e); + }); - data_bits.forEach((e, i, a) => { - a[i] = parseInt(e); - }); + let k = data_bits.length; - 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 - * 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; - 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 */ - for(let i = 0; i < enc.length; i++) { - if(enc[i]) { - parity ^= i; - } - } + parity = parity.toString(2).split("").reverse(); + parity.forEach((e: any, i: any, a: any) => { + a[i] = parseInt(e); + }); - parity = parity.toString(2).split("").reverse(); - parity.forEach((e: any, i: any , a: any) => { - a[i] = parseInt(e); - }); + /* Set the parity bits accordingly */ + for (let i = 0; i < parity.length; i++) { + enc[2 ** i] = parity[i] ? 1 : 0; + } - /* Set the parity bits accordingly */ - for(let i = 0; i < parity.length; i++) { - enc[2 ** i] = parity[i] ? 1 : 0; - } + parity = 0; + /* Figure out the overall parity for the entire block */ + for (let i = 0; i < enc.length; i++) { + if (enc[i]) { + parity++; + } + } - parity = 0; - /* Figure out the overall parity for the entire block */ - for(let i = 0; i < enc.length; i++) { - if(enc[i]) { - parity++; - } - } + /* Finally set the overall parity bit */ + enc[0] = parity % 2 == 0 ? 0 : 1; - /* Finally set the overall parity bit */ - enc[0] = parity % 2 == 0 ? 0 : 1; - - return enc.join(""); + return enc.join(""); } export function HammingEncodeProperly(data: number): string { - /* How many bits do we need? - * n = 2^m - * k = 2^m - m - 1 - * where k is the number of data bits, m the number - * of parity bits and n the number of total bits. */ + /* How many bits do we need? + * n = 2^m + * k = 2^m - m - 1 + * where k is the number of data bits, m the number + * of parity bits and n the number of total bits. */ - let m = 1; + let m = 1; - while((2 ** ((2 ** m) - m - 1)) < data) { - m++; - } + while (2 ** (2 ** m - m - 1) < data) { + m++; + } - const n: number = (2 ** m); - const k: number = (2 ** m) - m - 1; + const n: number = 2 ** m; + const k: number = 2 ** m - m - 1; - const enc: Array = [0]; - const data_bits: Array = data.toString(2).split("").reverse(); + const enc: Array = [0]; + const data_bits: Array = data.toString(2).split("").reverse(); - data_bits.forEach((e, i, a) => { - a[i] = parseInt(e); - }); + data_bits.forEach((e, i, a) => { + a[i] = parseInt(e); + }); - /* Flip endianness as in the original implementation by Hedrauta - * and write the data back to front - * XXX why do we do this? */ - for(let i = 1, j = k; i < n; i++) { - if((i & (i - 1)) != 0) { - enc[i] = data_bits[--j] ? data_bits[j] : 0; - } - } + /* Flip endianness as in the original implementation by Hedrauta + * and write the data back to front + * XXX why do we do this? */ + for (let i = 1, j = k; i < n; i++) { + if ((i & (i - 1)) != 0) { + enc[i] = data_bits[--j] ? data_bits[j] : 0; + } + } - let parity: any = 0; + let parity: any = 0; - /* Figure out the subsection parities */ - for(let i = 0; i < n; i++) { - if(enc[i]) { - parity ^= i; - } - } + /* Figure out the subsection parities */ + for (let i = 0; i < n; i++) { + if (enc[i]) { + parity ^= i; + } + } - parity = parity.toString(2).split("").reverse(); - parity.forEach((e: any, i: any , a: any) => { - a[i] = parseInt(e); - }); + parity = parity.toString(2).split("").reverse(); + parity.forEach((e: any, i: any, a: any) => { + a[i] = parseInt(e); + }); - /* Set the parity bits accordingly */ - for(let i = 0; i < m; i++) { - enc[2 ** i] = parity[i] ? 1 : 0; - } + /* Set the parity bits accordingly */ + for (let i = 0; i < m; i++) { + enc[2 ** i] = parity[i] ? 1 : 0; + } - parity = 0; - /* Figure out the overall parity for the entire block */ - for(let i = 0; i < n; i++) { - if(enc[i]) { - parity++; - } - } + parity = 0; + /* Figure out the overall parity for the entire block */ + for (let i = 0; i < n; i++) { + if (enc[i]) { + parity++; + } + } - /* Finally set the overall parity bit */ - enc[0] = parity % 2 == 0 ? 0 : 1; + /* Finally set the overall parity bit */ + enc[0] = parity % 2 == 0 ? 0 : 1; - return enc.join(""); + return enc.join(""); } export function HammingDecode(data: string): number { - let err = 0; - const bits: Array = []; + let err = 0; + const bits: Array = []; - /* TODO why not just work with an array of digits from the start? */ - for(const i in data.split("")) { - const bit = parseInt(data[i]); - bits[i] = bit; + /* TODO why not just work with an array of digits from the start? */ + for (const i in data.split("")) { + const bit = parseInt(data[i]); + bits[i] = bit; - if(bit) { - err ^= +i; - } - } + if (bit) { + err ^= +i; + } + } - /* If err != 0 then it spells out the index of the bit that was flipped */ - if(err) { - /* Flip to correct */ - bits[err] = bits[err] ? 0 : 1; - } + /* If err != 0 then it spells out the index of the bit that was flipped */ + if (err) { + /* Flip to correct */ + bits[err] = bits[err] ? 0 : 1; + } - /* 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 - * a parity bit and not part of the actual message. */ + /* 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 + * a parity bit and not part of the actual message. */ - let ans = ''; + let ans = ""; - for(let i = 1; i < bits.length; i++) { - /* i is not a power of two so it's not a parity bit */ - if((i & (i - 1)) != 0) { - ans += bits[i]; - } - } + for (let i = 1; i < bits.length; i++) { + /* i is not a power of two so it's not a parity bit */ + if ((i & (i - 1)) != 0) { + ans += bits[i]; + } + } - /* 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? */ - return parseInt(ans, 2); + /* 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? */ + return parseInt(ans, 2); }