-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathirradix.py
executable file
·229 lines (171 loc) · 6.53 KB
/
irradix.py
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
#!/usr/bin/env python
from mpmath import mp, mpf, sqrt, floor, ceil, log, log10
# Initialize global variables for BigPHI
current_bigphi_dps = 100
mp.dps = current_bigphi_dps
_bigphi_value = (mpf(1) + sqrt(mpf(5))) / 2
delim = '1010101'
# Getter for BigPHI in VALS
def get_bigphi():
return _bigphi_value
# Constants dictionary with dynamic getter
VALS = {
'BigPHI': get_bigphi,
}
# Debug flag
DEBUG = False
# API to set the precision (dps) dynamically
def set_precision(dps):
global current_bigphi_dps, _bigphi_value
current_bigphi_dps = dps
mp.dps = current_bigphi_dps
_bigphi_value = (mpf(1) + sqrt(mpf(5))) / 2 # Recalculate BigPHI with new precision
def irradix(num):
num = mpf(num)
if num == 0:
return "0"
if not num % 1 == 0:
raise TypeError("Sorry cannot convert non-integer numbers.")
S = mp.sign(num)
if S == -1:
num = num * S
w = [num, 0]
r = []
lastW = list(w)
bigphi = VALS['BigPHI']()
quanta = bigphi ** -(log(num) / log(bigphi))
epsilon = mpf(2) ** -mp.prec
thresh = min(quanta, epsilon)
while True:
if abs(w[0]) <= thresh:
break
w[1] = w[0] % bigphi
if w[1] < 0:
w[1] = w[1] - bigphi
w[0] = (w[0] - w[1]) / bigphi
if abs(w[0]) >= abs(lastW[0]):
if mp.sign(bigphi) == -1:
if w[0] == 0:
break
else:
break
unit = floor(abs(w[1]))
r.insert(0, str(int(unit)))
lastW = list(w)
if S == -1:
r.insert(0, '-')
return ''.join(r)
def derradix(rep):
S = -1 if rep[0] == '-' else 1
if S == -1:
rep = rep[1:]
bigphi = VALS['BigPHI']()
# Ensure the rep string is correctly split into integers
rep = [mpf(int(u, 10)) for u in rep]
num = mpf(0)
for u in rep:
num = num * bigphi
num = ceil(num + u)
return int(num * S)
def encode(nums, bits=False):
encoded_list = []
for num in nums:
binary_rep = irradix((num + 1) * 2)
if DEBUG:
print(f"Initial Binary Representation for {num}: {binary_rep}")
if binary_rep.endswith('10'):
binary_rep += '0101'
if DEBUG:
print(f"Appended 0101 for {num} because it ends with 10: {binary_rep}")
encoded_list.append(binary_rep)
concatenated = '101'.join(encoded_list)
if DEBUG:
print(f"Concatenated Sequence Before Padding: {concatenated}")
if bits:
return concatenated
padding_length = (8 - len(concatenated) % 8) % 8
padded_sequence = '0' * padding_length + concatenated
if DEBUG:
print(f"Padded Sequence (left padding with zeros): {padded_sequence} (Padding Length: {padding_length})")
chunks = [padded_sequence[i:i + 8] for i in range(0, len(padded_sequence), 8)]
if DEBUG:
print(f"8-bit Chunks: {chunks}")
chunk_values = [int(chunk, 2) for chunk in chunks]
if DEBUG:
print(f"Encoded Chunk Values: {chunk_values}")
return chunk_values
def decode(chunks, bits=False):
if not bits:
binary_chunks = [bin(chunk)[2:].zfill(8) for chunk in chunks]
if DEBUG:
print(f"Binary Chunks: {binary_chunks}")
reconstructed = ''.join(binary_chunks)
if DEBUG:
print(f"Reconstructed Sequence Before Removing Padding: {reconstructed}")
leading_zeros = len(reconstructed) - len(reconstructed.lstrip('0'))
if DEBUG:
print(f"Identified Leading Zeros (Padding Length): {leading_zeros}")
reconstructed = reconstructed[leading_zeros:]
if DEBUG:
print(f"Reconstructed Sequence (after removing leading zeros): {reconstructed}")
else:
reconstructed = chunks
rep_strings = reconstructed.split('101')
if DEBUG:
print(f"Split Representation Strings: {rep_strings}")
numbers = []
for i in range(len(rep_strings)):
if rep_strings[i]:
if i < len(rep_strings) - 1 and not rep_strings[i+1]:
if DEBUG:
print(f"\n\nEmpty spot found, adjusting representation: {rep_strings[i]}")
rep_strings[i] = rep_strings[i][:-1]
if DEBUG:
print(f"Empty spot found, adjusting representation: {rep_strings[i]}\n\n")
num = (derradix(rep_strings[i]) // 2) - 1
numbers.append(num)
if DEBUG:
print(f"Decoded Numbers: {numbers}")
return numbers
# online variants, suitable for streaming
# online encode
def olencode(nums, bits):
return encode(nums, bits)
# online decode
def oldecode(chunks, bits):
return decode(chunks, bits)
# l1 - length first variants, suitable when numbers are known ahead of time
# length first encode and decode
def l1encode(nums):
# Step 1: Calculate the bit lengths of the numbers in base-2
lengths = [len(bin(num)[2:]) for num in nums]
# Step 2: Encode the lengths using olencode
encoded_lengths_bits = olencode(lengths, bits=True)
# Step 3: Concatenate the bit strings of the numbers themselves
concatenated_bits = ''.join(bin(num)[2:] for num in nums)
# Step 4: Append the triple '101' sequence and the concatenated bits
full_bit_sequence = encoded_lengths_bits + delim + concatenated_bits
# Step 5: Convert the full bit sequence into 8-bit chunks
pad_amount = (8- (len(full_bit_sequence)%8))%8
padded_sequence = full_bit_sequence + '0' * pad_amount
chunked_sequence = [padded_sequence[i:i+8] for i in range(0, len(padded_sequence), 8)]
# Step 6: Convert the 8-bit chunks to integers and concatenate with encoded lengths
final_sequence = [int(chunk, 2) for chunk in chunked_sequence]
return final_sequence
def l1decode(chunks):
# Step 1: Convert chunks to binary strings
binary_chunks = [bin(chunk)[2:].zfill(8) for chunk in chunks]
full_sequence = ''.join(binary_chunks)
# Step 2: Split by delim delimiter to separate the encoded lengths and concatenated bits
encoded_lengths_bits, concatenated_bits = full_sequence.split(delim, 1)
# Step 5: Decode the length chunks using oldecode to get the lengths of the original numbers
lengths = oldecode(encoded_lengths_bits, bits=True)
# Step 6: Reconstruct the original numbers using the decoded lengths
numbers = []
pos = 0
for length in lengths:
num_bits = concatenated_bits[pos:pos + length]
num = int(num_bits, 2) # Convert the binary string back to an integer
numbers.append(num)
pos += length
return numbers