Skip to content

Commit 8b087dd

Browse files
authored
feat: add evaluate_polynomial method (#46)
1 parent 54e2101 commit 8b087dd

5 files changed

Lines changed: 99 additions & 7 deletions

File tree

.github/workflows/examples.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ on:
99
- main
1010

1111
jobs:
12-
test:
12+
examples:
1313
runs-on: ubuntu-latest
1414

1515
steps:
@@ -28,5 +28,6 @@ jobs:
2828
- name: Run all Examples
2929
run: |
3030
for example in examples/*.py; do
31+
echo "=== Running $example"
3132
python3 "$example"
3233
done

examples/example_jsonrpc_get_blockchain_info.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
from pactus_jsonrpc.client import PactusOpenRPCClient
2-
import pactus_jsonrpc.client
32
import asyncio
43

54

65
async def main() -> None:
7-
pactus_jsonrpc.client.CLIENT_URL = "https://testnet1.pactus.org/jsonrpc"
8-
client = PactusOpenRPCClient([])
6+
client_url = "https://testnet1.pactus.org/jsonrpc"
7+
client = PactusOpenRPCClient(
8+
headers={},
9+
client_url=client_url)
910

1011
res = await client.pactus.blockchain.get_blockchain_info()
1112

examples/example_jsonrpc_get_node_info.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
from pactus_jsonrpc.client import PactusOpenRPCClient
2-
import pactus_jsonrpc.client
32
import asyncio
43

54

65
async def main() -> None:
7-
pactus_jsonrpc.client.CLIENT_URL = "https://testnet1.pactus.org/jsonrpc"
8-
client = PactusOpenRPCClient([])
6+
client_url = "https://testnet1.pactus.org/jsonrpc"
7+
client = PactusOpenRPCClient(
8+
headers={},
9+
client_url=client_url)
910

1011
res = await client.pactus.network.get_node_info()
1112

pactus/utils/utils.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,29 @@ def encode_from_base256_with_type(hrp: str, typ: str, data: bytes) -> str:
1717
converted = bech32m.convertbits(list(data), 8, 5, pad=True)
1818
converted = [typ, *converted]
1919
return bech32m.bech32_encode(hrp, converted, bech32m.Encoding.BECH32M)
20+
21+
22+
def evaluate_polynomial(c: list[int], x: int, mod: int) -> int | None:
23+
"""
24+
Evaluate the polynomial f(x) = c[0] + c[1] * x + c[2] * x^2 + ... + c[n-1] * x^(n-1).
25+
26+
Args:
27+
c: List of polynomial coefficients (c[0] is the constant term)
28+
x: The value at which to evaluate the polynomial
29+
mod: The modulus to use for the evaluation
30+
31+
Returns:
32+
The computed value f(x) if success, None otherwise
33+
34+
"""
35+
if not c:
36+
return None
37+
38+
if len(c) == 1:
39+
return c[0]
40+
41+
y = c[-1]
42+
for i in range(len(c) - 2, -1, -1):
43+
y = (y * x + c[i]) % mod
44+
45+
return y

tests/test_utils.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import unittest
2+
from pactus.utils import utils
3+
4+
5+
class TestEvaluatePolynomial(unittest.TestCase):
6+
def test_empty_coefficients(self):
7+
self.assertIsNone(utils.evaluate_polynomial([], 2, 7))
8+
9+
def test_single_coefficient(self):
10+
self.assertEqual(utils.evaluate_polynomial([5], 10, 7), 5)
11+
12+
def test_x_zero(self):
13+
# f(0) = c[0] % mod
14+
self.assertEqual(utils.evaluate_polynomial([3, 2, 1], 0, 5), 3 % 5)
15+
16+
def test_x_one(self):
17+
# f(1) = sum(c) % mod
18+
self.assertEqual(utils.evaluate_polynomial([1, 2, 3], 1, 7), (1 + 2 + 3) % 7)
19+
20+
def test_multiple_coefficients(self):
21+
# f(2) = 1 + 2*2 + 3*2^2 = 1 + 4 + 12 = 17 % 5 = 2
22+
self.assertEqual(utils.evaluate_polynomial([1, 2, 3], 2, 5), 2)
23+
24+
def test_negative_coefficients(self):
25+
# f(2) = -1 + 2*2 + (-3)*2^2 = -1 + 4 - 12 = -9 % 7 = 5
26+
self.assertEqual(utils.evaluate_polynomial([-1, 2, -3], 2, 7), 5)
27+
28+
def test_negative_x(self):
29+
# f(-1) = 2 + 3*(-1) + 4*(-1)^2 = 2 - 3 + 4 = 3 % 6 = 3
30+
self.assertEqual(utils.evaluate_polynomial([2, 3, 4], -1, 6), 3)
31+
32+
def test_large_modulus(self):
33+
# f(3) = 2 + 4*3 + 5*9 = 2 + 12 + 45 = 59 % 1000 = 59
34+
self.assertEqual(utils.evaluate_polynomial([2, 4, 5], 3, 1000), 59)
35+
36+
def test_modulus_one(self):
37+
# Any value mod 1 is 0
38+
self.assertEqual(utils.evaluate_polynomial([1, 2, 3], 5, 1), 0)
39+
40+
def test_wikipedia_example(self):
41+
# https://en.wikipedia.org/wiki/Shamir%27s_secret_sharing
42+
self.assertEqual(
43+
utils.evaluate_polynomial([1234, 166, 94], 1, 2**127 - 1), 1494
44+
)
45+
self.assertEqual(
46+
utils.evaluate_polynomial([1234, 166, 94], 2, 2**127 - 1), 1942
47+
)
48+
self.assertEqual(
49+
utils.evaluate_polynomial([1234, 166, 94], 3, 2**127 - 1), 2578
50+
)
51+
self.assertEqual(
52+
utils.evaluate_polynomial([1234, 166, 94], 4, 2**127 - 1), 3402
53+
)
54+
self.assertEqual(
55+
utils.evaluate_polynomial([1234, 166, 94], 5, 2**127 - 1), 4414
56+
)
57+
self.assertEqual(
58+
utils.evaluate_polynomial([1234, 166, 94], 6, 2**127 - 1), 5614
59+
)
60+
61+
62+
if __name__ == "__main__":
63+
unittest.main()

0 commit comments

Comments
 (0)