Created
August 10, 2024 17:57
-
-
Save syminical/86523c07ff1bc336e670bb69d9706dd9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Copyright (c) 2020 syminical. All Rights Reserved. | |
# 2020/3/12 | |
import math | |
class AWeirdTextFormat: | |
""" | |
This class handles encoding and decoding of text | |
using the weird text format. | |
""" | |
# !!! Decode() will strip all null characters !!! | |
# Supporting them was not requested, and requires nasty work-arounds. | |
# Some notes about \x00 (the null character): | |
# 1.) ord(\x00) = 0, binary form is eight 0's in the context of this class. | |
# 2.) Encode cannot accurately encode trailing null characters in a string. | |
# a. Encode("\x00\x00\x00") = [0] = Encode("\x00\x00") = ... | |
# 3.) Decode cannot accurately decode leading null characters in integers. | |
# a. Decode(Encode('\x00W')) = '\x00\x00\x00W' = | |
# Decode(Encode('\x00\x00W')) = Decode(Encode('\x00\x00\x00W')) | |
# These mappings are designed to avoid string reversals while processing | |
# the input string characters in order. | |
# Diagram: EncodingMap.png | |
__kEncodingMap = { | |
0:3, 1:7, 2:11, 3:15, 4:19, 5:23, 6:27, 7:31, | |
8:2, 9:6, 10:10, 11:14, 12:18, 13:22, 14:26, 15:30, | |
16:1, 17:5, 18:9, 19:13, 20:17, 21:21, 22:25, 23:29, | |
24:0, 25:4, 26:8, 27:12, 28:16, 29:20, 30:24, 31:28 | |
} | |
# Diagram: DecodingMap.png | |
__kDecodingMap = { | |
0:0, 1:8, 2:16, 3:24, 4:1, 5:9, 6:17, 7:25, | |
8:2, 9:10, 10:18, 11:26, 12:3, 13:11, 14:19, 15:27, | |
16:4, 17:12, 18:20, 19:28, 20:5, 21:13, 22:21, 23:29, | |
24:6, 25:14, 26:22, 27:30, 28:7, 29:15, 30:23, 31:31 | |
} | |
def Encode(inputString): | |
""" | |
Encodes a string using kEncodingMap. | |
input: String | |
output: List that contains integers, or empty if character bits overflow. | |
""" | |
ret = [0] * math.ceil(len(inputString) / 4) | |
# Each iteration adds a new value to ret. | |
# A list is filled with binary representations of every four characters. | |
# That list is then converted to an integer, and collected in ret. | |
for iRet, iSlice in enumerate(range(0, len(inputString), 4)): | |
#This solves dealing with strings whose lengths != 4k, k is an int. | |
# Also, there are usually less 1's than 0's to move around. | |
encodedBinTray = ['0'] * 32 | |
fourCharChunk = inputString[iSlice:iSlice + 4] | |
# Each iteration processes a char from the 4-char segment above. | |
for iByte, char in enumerate(fourCharChunk): | |
if 255 < ord(char): | |
# char will not be decoded properly. | |
return [] | |
# Each iteration processes a bit from the binary representation of | |
# the char above. | |
# Need :08b instead of :b to properly pad smaller numbers. | |
for offset, bit in enumerate(f'{ord(char):08b}'): | |
if '1' == bit: | |
encodedBinTray[AWeirdTextFormat.__kEncodingMap[8 * iByte + | |
offset]] = '1' | |
ret[iRet] = int(''.join(encodedBinTray), 2) | |
return ret | |
def Decode(inputList): | |
""" | |
Decodes a list of integers using kDecodingMap. | |
input: List of integers. | |
output: String, may be empty if the bits from input integers overflow. | |
Null characters are not included in the result. | |
""" | |
ret = [""] * len(inputList) | |
# Each iteration adds a chunk of four characters to ret. | |
# The bits of the binary form of each number in inputList are inserted | |
# into a list (32bit). The list of bits is decoded, then each byte is | |
# converted into a unicode character. The characters are collected | |
# in ret as chunks of four, and returned at the end as a single string. | |
for iRet, encodedNum in enumerate(inputList): | |
if 4294967295 < encodedNum: | |
# encodedNum cannot be decoded. | |
return '' | |
decodedBinTray = ['0'] * 32 | |
#Each iteration decodes a bit from the binary form of encodedNum. | |
#Use :032b to properly fill in the tray for nums with fewer bits. | |
for iMap, bit in enumerate(f'{encodedNum:032b}'): | |
if '1' == bit: | |
decodedBinTray[AWeirdTextFormat.__kDecodingMap[iMap]] = '1' | |
decodedBinStr = ''.join(decodedBinTray) | |
# Converts each byte into a character; reads decodedBinStr backwards | |
# to preserve the correct character order. | |
fourCharChunks = [] | |
for i in range(24, -1, -8): | |
char = chr(int(decodedBinStr[i:i + 8], 2)) | |
# Do not include null characters. | |
if '\x00' != char: | |
fourCharChunks.append(char) | |
ret[iRet] = ''.join(fourCharChunks) | |
return ''.join(ret) | |
# I/O program | |
def AWTF(_): | |
return AWeirdTextFormat.Decode(AWeirdTextFormat.Encode(_)) | |
# Reads input.txt as a string, encrypts it, decrypts that, then prints result. | |
with open('input.txt', 'r') as inputFile: | |
print(AWTF(inputFile.read())) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment