-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathans.py
More file actions
62 lines (51 loc) · 2.24 KB
/
ans.py
File metadata and controls
62 lines (51 loc) · 2.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
''' J. Duda 2007 ANS/ABC (see: http://mattmahoney.net/dc/dce.html#Section_33)
You should still be careful - the conversion from ints to floats has
its limitations (if enc > 2⁵³, it will not be converted correctly since
float(9007199254740992 + n) - 9007199254740992 = 0, for any non-negative n.
Using Decimals helps a little bit (or two, or even more ;)
to alleviate the limitation, though...
https://docs.python.org/3/library/decimal.html
Note that ANS (ABC?) coding amounts to the standard binary system
(with 0 and 1 inverted) when p = 2⁻¹
https://en.wikipedia.org/wiki/Asymmetric_numeral_systems
'''
from decimal import Decimal as DeX, getcontext as gctx
from math import floor, ceil, log2 as lg2
import sys
gctx().prec = 0x1000
# A probability p = P(X = 1) can be quite arbitrarily
p = 1/2 if len(sys.argv) < 0b10 else eval(sys.argv[1])
# The entropy is (as always) just (an) average information
# 'H = ∑ᵢpᵢ⋅I(pᵢ) = -∑ᵢpᵢ⋅lg₂pᵢ'
H = (p - 1) * lg2(1 - p) - p * lg2(p) #[bit/symbol]
print('—' * 0o100, f'\nP(X = 1) = {p}',
f' → Entropy = {round(H, 4)} [bit/symbol]')
while True:
## 0. Acquire
msg = input()
if msg in ('EOC', ''): break
# For format's sake!
L = len(msg)
print('—' * 0o100, f'\nEncoding: {msg}')
## I. Encode
p, enc = DeX(p), DeX(0o0)
for n in msg:
enc = ceil((enc + 1)/(1 - p)) - 1 if n == '0' else floor(enc/p)
# Enlightening I
print(f'{n} → {bin(enc):{L}}')
print('—' * 0o100, f'\nDecoding: {bin(enc)} ({enc})')
## II. Decode
code, dec = enc, ''
l = ceil(lg2(enc)) + 0b10 if enc else 0o1
for _ in msg:
# Enlightening II.I
print(' ' * 0b11, f'{bin(enc):{l}}', end = ' → ')
z = ceil((enc + 1) * p) - ceil(enc * p)
enc = enc - ceil(enc * p) if z == 0 else ceil(enc * p)
dec = str(z) + dec
# Enlightening II.II
print(f'{dec}')
## III. Reveal!
ly, lc = len(msg), floor(lg2(code) + 1) if code else 1
print('—' * 0o100, f'\nMessage: {msg}\nEncoded: {bin(code)}')
print(f'Decoded: {dec}\n\nRatio: {ly} → {lc} ({lc/ly:,.0%})')