Skip to content

Commit 602ef98

Browse files
committed
Fixed exp and log tables, added horner()
1 parent ce50237 commit 602ef98

5 files changed

Lines changed: 253 additions & 7 deletions

File tree

gems/defaults3.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,18 @@
55
# defaults.py
66

77
import json
8-
import js2pysecrets as secrets
98
import js2pysecrets.node as node
109

11-
from js2pysecrets.settings import Settings
1210
import random
1311

1412
# This does not work
13+
14+
import js2pysecrets as secrets
15+
from js2pysecrets.settings import Settings
1516
settings = Settings()
17+
18+
19+
1620
#config = settings.get_config()
1721
#print(secrets.isSetRNG())
1822

gems/splitNumStringToIntArray.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#! /usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
# vim: set et sw=4 fenc=utf-8:
4+
#
5+
# splitNumStringToIntArray.py.py
6+
7+
import js2pysecrets as secrets
8+
9+
secrets.init()
10+
string = secrets.hex2bin(secrets.str2hex('hello world'))
11+
#print(string)
12+
string = '1' + string
13+
#print(string)
14+
15+
split_string = secrets.splitNumStringToIntArray(string, 8)
16+
17+
print(split_string)
18+
19+
20+
"""
21+
22+
00000000
23+
01100100
24+
00000000
25+
01101100
26+
00000000
27+
01110010
28+
00000000
29+
01101111
30+
00000000
31+
01110111
32+
00000000
33+
00100000
34+
00000000
35+
01101111
36+
00000000
37+
01101100
38+
00000000
39+
01101100
40+
00000000
41+
01100101
42+
00000000
43+
01101000
44+
45+
"""
46+
47+
"""
48+
49+
1100100
50+
00000000
51+
01101100
52+
00000000
53+
01110010
54+
00000000
55+
01101111
56+
00000000
57+
01110111
58+
00000000
59+
00100000
60+
00000000
61+
01101111
62+
00000000
63+
01101100
64+
00000000
65+
01101100
66+
00000000
67+
01100101
68+
00000000
69+
01101000
70+
71+
"""

js2pysecrets/base.py

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,46 @@ def padLeft(string, multipleOfBits=None):
4545

4646
def bin2hex(binary_string: str) -> str:
4747
hex_string = hex(int(binary_string, 2))[2:]
48+
49+
# Need to respect any leading zeros in the binary string when converting
50+
# to hex. Because 0b0011 = 0x3, while 0x03 is desired
4851
hex_string = hex_string.zfill((len(padLeft(binary_string, 4)) // 4))
52+
4953
return hex_string
5054

5155

52-
def hex2bin(hex_string: str) -> str:
53-
return bin(int(hex_string, 16))[2:]
56+
def hex2bin(hex_str):
57+
bin_str = ""
58+
59+
# Can NOT use `bin(int(hex_str, 16))[2:]` for this conversion. Need to
60+
# use this unconventional method to match the JavaScript, specifically
61+
# to achieve the correct zero padding in the binary string output.
62+
for char in reversed(hex_str):
63+
try:
64+
num = int(char, 16)
65+
except ValueError:
66+
raise ValueError(f"Invalid hex character: {char}")
67+
68+
bin_str = bin(num)[2:].zfill(4) + bin_str
69+
70+
return bin_str
71+
72+
73+
# def hex2bin(hex_string: str) -> str:
74+
# binary_string = bin(int(hex_string, 16))[2:]
75+
#
76+
# # Need to respect any leading zeros in the binary string when converting
77+
# # to hex. Because 0b0011 = 0x3, while 0x03 is desired
78+
# binary_string = binary_string.zfill((len(binary_string) // 8))
79+
#
80+
# #binary_string = bin(int(hex_string, 16))[2:]
81+
#
82+
# # Need to respect any leading zeros in the binary string when converting
83+
# # to hex. Because 0b0011 = 0x3, while 0x03 is desired
84+
# #binary_string = binary_string.zfill(int(hex_string, 2))
85+
#
86+
# # return bin(int(hex_string, 16))[2:]
87+
# return binary_string
5488

5589

5690
"""
@@ -59,8 +93,8 @@ def hex2bin(hex_string: str) -> str:
5993

6094

6195
def init(bits=None, rngType=None):
62-
logs = []
6396
exps = []
97+
logs = [None]
6498
x = 1
6599
primitive = None
66100

@@ -88,14 +122,21 @@ def init(bits=None, rngType=None):
88122
# Construct the exp and log tables for multiplication.
89123
primitive = settings.primitive_polynomials[settings.bits]
90124

125+
temp_logs = {} # Temporary Dict to hold logs
91126
for i in range(settings.size):
92-
exps.append(x)
93-
logs.append(i)
127+
# this works with loop below
128+
exps.insert(i, x)
129+
temp_logs[x] = i
130+
94131
x = x << 1 # Left shift assignment
95132
if x >= settings.size:
96133
x = x ^ primitive # Bitwise XOR assignment
97134
x = x & settings.maxShares # Bitwise AND assignment
98135

136+
# Fill the logs List in the proper order
137+
for i in range(1, settings.size):
138+
logs.append(temp_logs[i])
139+
99140
settings.update_defaults(logs=logs)
100141
settings.update_defaults(exps=exps)
101142

@@ -178,6 +219,38 @@ def getRNG():
178219
return settings.rng
179220

180221

222+
def splitNumStringToIntArray(string, pad_length=None):
223+
parts = []
224+
225+
if pad_length:
226+
string = padLeft(string, pad_length)
227+
228+
# Reverse the string to facilitate right-to-left splitting
229+
string = string[::-1]
230+
231+
for i in range(0, len(string), settings.bits):
232+
print(string[i : i + settings.bits][::-1])
233+
parts.append(int(string[i : i + settings.bits][::-1], 2))
234+
235+
return parts
236+
237+
238+
def horner(x, coeffs):
239+
logx = settings.logs[x]
240+
fx = 0
241+
242+
for i in range(len(coeffs) - 1, -1, -1):
243+
if fx != 0:
244+
fx = (
245+
settings.exps[(logx + settings.logs[fx]) % settings.maxShares]
246+
^ coeffs[i]
247+
)
248+
else:
249+
fx = coeffs[i]
250+
251+
return fx
252+
253+
181254
# lambda bits: bin(1+random.getrandbits(bits))[2:].zfill(bits)
182255
# lambda bits: bin(1+secrets.randbits(bits))[2:].zfill(bits)
183256
# lambda bits: (bin(123456789)[2:].zfill(32) * -(-bits // 32))[-bits:]

js2pysecrets/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
@dataclass
88
class Defaults:
99
rng = lambda self, bits: bin(1 + random.getrandbits(bits))[2:].zfill(bits)
10+
dithering = None
1011
bits: int = 8 # default number of bits
1112
radix: int = 16 # work with HEX by default
1213
min_bits: int = 3

tests/test_basePrivate.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import pytest
77
import random
88
import warnings
9+
from js2pysecrets.wrapper import wrapper, chain
910

1011

1112
settings = Settings()
@@ -535,3 +536,99 @@ def test_py_js_random(input_range):
535536
py_string = secrets.random(bits)
536537
js_string = node.random(bits, test=True)
537538
assert py_string == js_string
539+
540+
541+
@pytest.mark.parametrize("input_range", range(300))
542+
def test_py_js_hex2bin(input_range):
543+
# bits = random_range(input_range)
544+
bits = int(input_range * 188) + 2
545+
secrets.init()
546+
secrets.setRNG()
547+
hex_string = secrets.random(bits)
548+
py_string = secrets.hex2bin(hex_string)
549+
js_string = node._hex2bin(hex_string)
550+
assert py_string == js_string
551+
552+
553+
def test_hex2bin_Fail():
554+
match = "Invalid hex character:"
555+
with pytest.raises(ValueError, match=match):
556+
secrets.hex2bin("0123efgh")
557+
# Check if any warnings were raised
558+
assert len(caught_warnings) == 1
559+
assert issubclass(caught_warnings[0].category, Warning)
560+
assert match in str(caught_warnings[0].message)
561+
562+
563+
@pytest.fixture(params=[None, 128, 256, 384, 512, 640, 768, 896, 1024])
564+
def multiple_of_bits(request):
565+
return request.param
566+
567+
568+
# Define your test function
569+
def test_array_split(random_string, multiple_of_bits):
570+
hex_string = secrets.str2hex(random_string)
571+
bin_string = "1" + secrets.hex2bin(hex_string)
572+
573+
py_array = secrets.splitNumStringToIntArray(bin_string, multiple_of_bits)
574+
js_array = node._splitNumStringToIntArray(bin_string, multiple_of_bits)
575+
576+
# Iterate over the elements of one array
577+
for i in range(len(py_array)):
578+
assert py_array[i] == js_array[i]
579+
580+
581+
@pytest.fixture(
582+
params=[None, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
583+
)
584+
def set_init_bits(request):
585+
return request.param
586+
587+
588+
# Define test cases
589+
@pytest.mark.parametrize(
590+
"x, coeffs",
591+
[
592+
(1, [21, 47, 205]),
593+
(2, [21, 47, 205]),
594+
(3, [21, 47, 205]),
595+
(4, [21, 47, 205]),
596+
(5, [21, 47, 205]),
597+
(6, [21, 47, 205]),
598+
(1, [0, 215, 162, 173, 194, 32, 53]),
599+
(2, [0, 215, 162, 173, 194, 32, 53]),
600+
(3, [0, 215, 162, 173, 194, 32, 53]),
601+
(4, [0, 215, 162, 173, 194, 32, 53]),
602+
(5, [0, 215, 162, 173, 194, 32, 53]),
603+
(6, [0, 215, 162, 173, 194, 32, 53]),
604+
(7, [0, 215, 162, 173, 194, 32, 53]),
605+
(8, [0, 215, 162, 173, 194, 32, 53]),
606+
(9, [0, 215, 162, 173, 194, 32, 53]),
607+
(10, [0, 215, 162, 173, 194, 32, 53]),
608+
(11, [0, 215, 162, 173, 194, 32, 53]),
609+
(12, [0, 215, 162, 173, 194, 32, 53]),
610+
(13, [0, 215, 162, 173, 194, 32, 53]),
611+
(14, [0, 215, 162, 173, 194, 32, 53]),
612+
(15, [0, 215, 162, 173, 194, 32, 53]),
613+
(16, [0, 215, 162, 173, 194, 32, 53]),
614+
(17, [0, 215, 162, 173, 194, 32, 53]),
615+
(18, [0, 215, 162, 173, 194, 32, 53]),
616+
(19, [0, 215, 162, 173, 194, 32, 53]),
617+
(20, [0, 215, 162, 173, 194, 32, 53]),
618+
(21, [0, 215, 162, 173, 194, 32, 53]),
619+
# Add more test cases as needed
620+
],
621+
)
622+
623+
624+
# Define the test function
625+
def test_horner(x, coeffs, set_init_bits):
626+
secrets.init(set_init_bits)
627+
# assert secrets.horner(x, coeffs) == node._horner(x, coeffs)
628+
629+
node_data = []
630+
node_data.append(node.init(set_init_bits, list=True))
631+
node_data.append(node._horner(x, coeffs, list=True))
632+
js_results = chain(node_data)
633+
634+
assert secrets.horner(x, coeffs) == js_results[-1]

0 commit comments

Comments
 (0)