could be going worse
This commit is contained in:
@ -1,8 +1,8 @@
|
|||||||
from util import Object
|
from util import *
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
# XXX: remove this
|
|
||||||
import libnum
|
import libnum
|
||||||
|
import sympy
|
||||||
|
|
||||||
CURVE_LINSPACE_OFFSET = 10
|
CURVE_LINSPACE_OFFSET = 10
|
||||||
CURVE_LINSPACE_NUM = 2000
|
CURVE_LINSPACE_NUM = 2000
|
||||||
@ -15,24 +15,11 @@ DEFAULT_CURVES = {
|
|||||||
'secp256k1 (BTC)': (0, 7, 2**256 - 2**32 - 2**9 - 2**8 - 2**7 - 2**6 - 2**4 - 1),
|
'secp256k1 (BTC)': (0, 7, 2**256 - 2**32 - 2**9 - 2**8 - 2**7 - 2**6 - 2**4 - 1),
|
||||||
}
|
}
|
||||||
|
|
||||||
def line_slope(x1, y1, x2, y2):
|
|
||||||
return (y2 - y1) / (x2 - x1)
|
|
||||||
|
|
||||||
def intersection(x1, y1, x2, y2, x3, y3, x4, y4):
|
|
||||||
s1 = line_slope(x1, y1, x2, y2)
|
|
||||||
s2 = line_slope(x3, y3, x4, y4)
|
|
||||||
if s1 == s2:
|
|
||||||
raise Exception("passed lines are paralel")
|
|
||||||
c1 = y1 - s1 * x1
|
|
||||||
c2 = y3 - s2 * x3
|
|
||||||
x = (c2 - c1) / (s1 - s2)
|
|
||||||
y = s1 * x + c1
|
|
||||||
return x, y
|
|
||||||
|
|
||||||
class EllipticCurve(Object):
|
class EllipticCurve(Object):
|
||||||
def __init__(self, a, b):
|
def __init__(self, a, b):
|
||||||
self.a = a
|
self.a = a
|
||||||
self.b = b
|
self.b = b
|
||||||
|
self.mod = 0
|
||||||
self._points()
|
self._points()
|
||||||
def _points(self):
|
def _points(self):
|
||||||
def iterih_square(x):
|
def iterih_square(x):
|
||||||
@ -73,10 +60,15 @@ class EllipticCurve(Object):
|
|||||||
|
|
||||||
class EllipticCurveOverFiniteField(Object):
|
class EllipticCurveOverFiniteField(Object):
|
||||||
def __init__(self, a, b, mod):
|
def __init__(self, a, b, mod):
|
||||||
|
if mod < 3:
|
||||||
|
raise ValueError("The modulus has to be over 2.")
|
||||||
|
if not sympy.isprime(mod):
|
||||||
|
raise ValueError("The modulus has to be a prime.")
|
||||||
self.a = a
|
self.a = a
|
||||||
self.b = b
|
self.b = b
|
||||||
self.mod = mod
|
self.mod = mod
|
||||||
self._points()
|
self._points()
|
||||||
|
self.inverse_table = [None] + [multiplicative_inverse_over_prime_finite_field(i, self.mod) for i in range(1, self.mod)]
|
||||||
def _points(self):
|
def _points(self):
|
||||||
self.xs = []
|
self.xs = []
|
||||||
self.ys = []
|
self.ys = []
|
||||||
@ -88,26 +80,60 @@ class EllipticCurveOverFiniteField(Object):
|
|||||||
for sr in square_roots:
|
for sr in square_roots:
|
||||||
self.ys.append(sr)
|
self.ys.append(sr)
|
||||||
self.xs.append(x)
|
self.xs.append(x)
|
||||||
|
def _line_slope(self, x1, y1, x2, y2):
|
||||||
|
return (y2 - y1) * self.inverse_table[(x2 - x1) % self.mod]
|
||||||
|
def is_point_out_of_bounds(self, x, y):
|
||||||
|
return x > self.mod or x < 0 or y > self.mod or y < 0
|
||||||
|
def bound_intersections(self, x1, y1, x2, y2):
|
||||||
|
left_border = (0, 0, 0, self.mod)
|
||||||
|
right_border = (self.mod, 0, self.mod, self.mod)
|
||||||
|
bottom_border = (0, 0, self.mod, 0)
|
||||||
|
top_border = (0, self.mod, self.mod, self.mod)
|
||||||
|
if x1 > x2:
|
||||||
|
x1, y1, x2, y2 = x2, y2, x1, y1
|
||||||
|
if y1 > y2:
|
||||||
|
p1 = intersection(x1, y1, x2, y2, *left_border)
|
||||||
|
if self.is_point_out_of_bounds(*p1):
|
||||||
|
p1 = intersection(x1, y1, x2, y2, *top_border)
|
||||||
|
p2 = intersection(x1, y1, x2, y2, *bottom_border)
|
||||||
|
if self.is_point_out_of_bounds(*p2):
|
||||||
|
p2 = intersection(x1, y1, x2, y2, *right_border)
|
||||||
|
else:
|
||||||
|
p1 = intersection(x1, y1, x2, y2, *bottom_border)
|
||||||
|
if self.is_point_out_of_bounds(*p1):
|
||||||
|
p1 = intersection(x1, y1, x2, y2, *left_border)
|
||||||
|
p2 = intersection(x1, y1, x2, y2, *right_border)
|
||||||
|
if self.is_point_out_of_bounds(*p2):
|
||||||
|
p2 = intersection(x1, y1, x2, y2, *top_border)
|
||||||
|
return *p1, *p2
|
||||||
def points(self):
|
def points(self):
|
||||||
return self.xs, self.ys
|
return self.xs, self.ys
|
||||||
def add(self, x1, y1, x2, y2):
|
def add(self, x1, y1, x2, y2):
|
||||||
s = line_slope(x1, y1, x2, y2)
|
if (x1, y1) == (x2, y2):
|
||||||
|
raise ValueError("Adding a point to itself is not allowed, please use double.")
|
||||||
|
s = self._line_slope(x1, y1, x2, y2)
|
||||||
x = (s**2 - x1 - x2) % self.mod
|
x = (s**2 - x1 - x2) % self.mod
|
||||||
y = (s * (x1 - x) - y1) % self.mod
|
y = (s * ((x1 - x) % self.mod) - y1) % self.mod
|
||||||
return (x, y)
|
return (x, y)
|
||||||
def double(self, x, y):
|
def double(self, x, y):
|
||||||
return 0, 0
|
return 0, 0
|
||||||
def scalar_multiply(point, n):
|
def scalar_multiply(point, n):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def elliptic_curve_factory(is_finite, a, b, mod, curve = None):
|
def elliptic_curve_factory(is_finite, a=None, b=None, mod=None, curve=None):
|
||||||
if curve != None:
|
if curve != None:
|
||||||
|
if isinstance(curve, tuple):
|
||||||
|
a, b, mod = curve
|
||||||
|
else:
|
||||||
a = curve.a
|
a = curve.a
|
||||||
b = curve.b
|
b = curve.b
|
||||||
try:
|
try:
|
||||||
mod = curve.mod
|
mod = curve.mod
|
||||||
except:
|
except:
|
||||||
mod = mod
|
mod = mod
|
||||||
|
else:
|
||||||
|
if a == None or b == None or mod == None:
|
||||||
|
raise ValueError("Insufficent information passed")
|
||||||
if is_finite:
|
if is_finite:
|
||||||
return EllipticCurveOverFiniteField(a, b, mod)
|
return EllipticCurveOverFiniteField(a, b, mod)
|
||||||
else:
|
else:
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
from elliptic_curve import *
|
from elliptic_curve import *
|
||||||
|
import matplotlib as mpl
|
||||||
|
from itertools import count
|
||||||
|
|
||||||
plots = []
|
plots = []
|
||||||
|
|
||||||
@ -12,6 +14,10 @@ def display_elliptic_curve(ax, curve):
|
|||||||
def display_elliptic_curve_over_finite_field(ax, curve):
|
def display_elliptic_curve_over_finite_field(ax, curve):
|
||||||
global plots
|
global plots
|
||||||
xs, ys = curve.points()
|
xs, ys = curve.points()
|
||||||
|
plots += [
|
||||||
|
ax.axhline(curve.mod, color='black', linewidth=1.2),
|
||||||
|
ax.axvline(curve.mod, color='black', linewidth=1.2),
|
||||||
|
]
|
||||||
plots += [ax.scatter(
|
plots += [ax.scatter(
|
||||||
xs,
|
xs,
|
||||||
ys,
|
ys,
|
||||||
@ -67,15 +73,27 @@ def addition_elliptic_curve(ax, curve, x1, y1, x2, y2):
|
|||||||
def addition_elliptic_curve_over_finite_field(ax, curve, x1, y1, x2, y2):
|
def addition_elliptic_curve_over_finite_field(ax, curve, x1, y1, x2, y2):
|
||||||
global plots
|
global plots
|
||||||
p = curve.add(x1, y1, x2, y2)
|
p = curve.add(x1, y1, x2, y2)
|
||||||
x_axis_intersection = intersection(x1, y1, x2, y2, 0, 0, 1, 0)
|
plots += ax.plot([x1, x2], [y1, y2], 'x', markersize=12, color='red')
|
||||||
limit_intersection = intersection(x1, y1, x2, y2, 0, curve.mod-1, 1, curve.mod-1)
|
plots += ax.plot(p[0], p[1], 'x', markersize=12, color='blue')
|
||||||
print(x_axis_intersection, limit_intersection)
|
ohdiff = x2 - x1
|
||||||
plots += [ax.plot(
|
ovdiff = y2 - y1
|
||||||
(x_axis_intersection[0], limit_intersection[0]),
|
for i in range(4):
|
||||||
(x_axis_intersection[1], limit_intersection[1]),
|
x1, y1, x2, y2 = curve.bound_intersections(x1, y1, x2, y2)
|
||||||
linestyle='--'
|
plots += ax.plot(
|
||||||
)]
|
(x1, x2),
|
||||||
plots += ax.plot([p[0], p[1]], 'x')
|
(y1, y2),
|
||||||
|
linestyle='--',
|
||||||
|
color = 'yellow',
|
||||||
|
)
|
||||||
|
if y2 == 0:
|
||||||
|
new_x1 = x2
|
||||||
|
new_y1 = curve.mod
|
||||||
|
else:
|
||||||
|
new_x1 = 0
|
||||||
|
new_y1 = y2
|
||||||
|
new_x2 = new_x1 + ohdiff
|
||||||
|
new_y2 = new_y1 + ovdiff
|
||||||
|
x1, y1, x2, y2 = new_x1, new_y1, new_x2, new_y2
|
||||||
|
|
||||||
def addition(ax, curve, x1, y1, x2, y2):
|
def addition(ax, curve, x1, y1, x2, y2):
|
||||||
if isinstance(curve, EllipticCurve):
|
if isinstance(curve, EllipticCurve):
|
||||||
|
117
gui.py
117
gui.py
@ -2,6 +2,8 @@ from util import Object
|
|||||||
from elliptic_curve import *
|
from elliptic_curve import *
|
||||||
import elliptic_curve_display
|
import elliptic_curve_display
|
||||||
|
|
||||||
|
import random
|
||||||
|
import sympy
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
@ -11,46 +13,68 @@ from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg,
|
|||||||
|
|
||||||
state = None
|
state = None
|
||||||
|
|
||||||
|
MAX_FINITE = 100
|
||||||
|
prime_list = [0] + list(sympy.primerange(2, MAX_FINITE))
|
||||||
|
|
||||||
|
def random_point(curve):
|
||||||
|
ps = curve.points()
|
||||||
|
i = random.randint(0, len(ps[0])-1)
|
||||||
|
p = (ps[0][i], ps[1][i])
|
||||||
|
return p
|
||||||
|
|
||||||
|
def new_random_points():
|
||||||
|
global state
|
||||||
|
state.curve.point1 = random_point(state.curve)
|
||||||
|
state.curve.point2 = state.curve.point1
|
||||||
|
while state.curve.point2 == state.curve.point1:
|
||||||
|
state.curve.point2 = random_point(state.curve)
|
||||||
|
|
||||||
def display():
|
def display():
|
||||||
|
global state
|
||||||
elliptic_curve_display.clear()
|
elliptic_curve_display.clear()
|
||||||
elliptic_curve_display.display(state.ax, state.curve)
|
elliptic_curve_display.display(state.ax, state.curve)
|
||||||
ps = state.curve.points()
|
elliptic_curve_display.addition(state.ax, state.curve, *state.curve.point1, *state.curve.point2)
|
||||||
elliptic_curve_display.addition(state.ax, state.curve, ps[0][3], ps[1][3], ps[0][6], ps[1][6])
|
|
||||||
elliptic_curve_display.double(state.ax, state.curve, ps[0][6], ps[1][6])
|
|
||||||
state.canvas.draw()
|
state.canvas.draw()
|
||||||
|
|
||||||
def update_curve(curve=None):
|
def _update_curve():
|
||||||
global state
|
global state
|
||||||
a = int(state.a_input.get())
|
a = int(state.a_strvar.get())
|
||||||
b = int(state.b_input.get())
|
b = int(state.b_strvar.get())
|
||||||
mod = int(state.mod_input.get())
|
mod = int(state.mod_strvar.get())
|
||||||
state.curve = elliptic_curve_factory(state.is_finite, a, b, mod, curve)
|
if mod > MAX_FINITE:
|
||||||
|
state.is_finite.set(0)
|
||||||
|
state.curve = elliptic_curve_factory(state.is_finite.get(), a, b, mod)
|
||||||
|
new_random_points()
|
||||||
state.a_strvar.set(state.curve.a)
|
state.a_strvar.set(state.curve.a)
|
||||||
state.b_strvar.set(state.curve.b)
|
state.b_strvar.set(state.curve.b)
|
||||||
try:
|
state.mod_strvar.set(state.curve.mod)
|
||||||
state.b_strvar.set(state.curve.mod)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def rerender(curve=None):
|
def update_curve():
|
||||||
if curve != None:
|
_update_curve()
|
||||||
update_curve(curve)
|
|
||||||
else:
|
|
||||||
update_curve()
|
|
||||||
display()
|
display()
|
||||||
|
|
||||||
def init(curve):
|
def set_curve(curve : (int, int, int)):
|
||||||
|
global state
|
||||||
|
a, b, mod = curve
|
||||||
|
state.a_strvar.set(a)
|
||||||
|
state.b_strvar.set(b)
|
||||||
|
state.mod_strvar.set(mod)
|
||||||
|
update_curve()
|
||||||
|
|
||||||
|
def init(curve : (int, int, int)):
|
||||||
global state
|
global state
|
||||||
state = Object()
|
state = Object()
|
||||||
state.points = list()
|
|
||||||
|
|
||||||
tk_init()
|
tk_init(curve)
|
||||||
state.curve = elliptic_curve_factory(state.is_finite, *DEFAULT_CURVES['Default'])
|
_update_curve()
|
||||||
tk_fill()
|
tk_fill()
|
||||||
|
display()
|
||||||
|
|
||||||
def tk_init():
|
def tk_init(curve : (int, int, int)):
|
||||||
|
global state
|
||||||
|
a, b, mod = curve
|
||||||
state.root = root = tk.Tk()
|
state.root = root = tk.Tk()
|
||||||
root.wm_title("Eliptic curves")
|
root.wm_title("Elliptic curves")
|
||||||
root.attributes('-zoomed', True)
|
root.attributes('-zoomed', True)
|
||||||
|
|
||||||
state.figure = plt.figure(figsize=(5, 4), dpi=150)
|
state.figure = plt.figure(figsize=(5, 4), dpi=150)
|
||||||
@ -58,17 +82,21 @@ def tk_init():
|
|||||||
state.controls = ttk.Frame(root)
|
state.controls = ttk.Frame(root)
|
||||||
state.toolbar = NavigationToolbar2Tk(state.canvas, root, pack_toolbar=False)
|
state.toolbar = NavigationToolbar2Tk(state.canvas, root, pack_toolbar=False)
|
||||||
state.toolbar.update()
|
state.toolbar.update()
|
||||||
state.a_strvar = tk.StringVar(value=str(0))
|
state.a_strvar = tk.StringVar(value=a)
|
||||||
state.b_strvar = tk.StringVar(value=str(0))
|
state.b_strvar = tk.StringVar(value=b)
|
||||||
state.mod_strvar = tk.StringVar(value=str(0))
|
state.mod_strvar = tk.StringVar(value=mod)
|
||||||
state.is_finite = tk.IntVar(value=0)
|
state.is_finite = tk.IntVar(value=1)
|
||||||
|
|
||||||
def tk_fill():
|
def tk_fill():
|
||||||
global state
|
global state
|
||||||
# Finite field view toggle
|
# Finite field view toggle
|
||||||
tk.Checkbutton(state.controls, text="Finite field view",
|
tk.Checkbutton(state.controls, text="Finite field view",
|
||||||
variable=state.is_finite, command=lambda: rerender(state.curve),
|
variable=state.is_finite, command=update_curve,
|
||||||
).pack()
|
).pack()
|
||||||
|
|
||||||
|
# Horizontal separator
|
||||||
|
tk.Frame(state.controls, height=2, bg="black").pack(fill=tk.X, pady=10)
|
||||||
|
|
||||||
# Equation -- y^2 = x^3 + a * x^2 + b
|
# Equation -- y^2 = x^3 + a * x^2 + b
|
||||||
state.curve_equation = ttk.Frame(state.controls)
|
state.curve_equation = ttk.Frame(state.controls)
|
||||||
equation = [
|
equation = [
|
||||||
@ -84,32 +112,49 @@ def tk_fill():
|
|||||||
for i, ix in enumerate(equation):
|
for i, ix in enumerate(equation):
|
||||||
ix.grid(row=0, column=i)
|
ix.grid(row=0, column=i)
|
||||||
|
|
||||||
# Input --- a b
|
# Input --- a b mod
|
||||||
entry_keys = ("name", "color", "value_var", "label_name")
|
entry_keys = ("name", "color", "value_var", "label_name", "max")
|
||||||
|
a, b = state.a_strvar.get(), state.b_strvar.get()
|
||||||
|
mod = prime_list.index(int(state.mod_strvar.get()))
|
||||||
entry_values = [
|
entry_values = [
|
||||||
("a", "blue", state.a_strvar.get(), "a_input"),
|
("a", "blue", a, "a_input", 100),
|
||||||
("b", "red", state.b_strvar.get(), "b_input"),
|
("b", "red", b, "b_input", 100),
|
||||||
("modulos", "magenta", state.mod_strvar.get(), "mod_input"),
|
("modulos", "magenta", mod, "mod_input", len(prime_list)-1),
|
||||||
]
|
]
|
||||||
(f := ttk.Frame(state.controls)).pack()
|
(f := ttk.Frame(state.controls)).pack()
|
||||||
for i, d in enumerate([{k:v for k,v in zip(entry_keys, t)} for t in entry_values]):
|
for i, d in enumerate([{k:v for k,v in zip(entry_keys, t)} for t in entry_values]):
|
||||||
ttk.Label(f, text=d["name"], foreground=d["color"]).grid(row=i, column=0)
|
ttk.Label(f, text=d["name"], foreground=d["color"]).grid(row=i, column=0)
|
||||||
w = state[d["label_name"]] = tk.Scale(f, from_=0, to=100, orient=tk.HORIZONTAL, length=200)
|
w = state[d["label_name"]] = tk.Scale(f,
|
||||||
|
from_=0, to=d["max"],
|
||||||
|
orient=tk.HORIZONTAL,
|
||||||
|
length=200,
|
||||||
|
showvalue=False
|
||||||
|
)
|
||||||
w.set(d["value_var"])
|
w.set(d["value_var"])
|
||||||
w.config(command=lambda event: rerender())
|
w.config(command=lambda event: update_curve())
|
||||||
w.grid(row=i, column=1)
|
w.grid(row=i, column=1)
|
||||||
|
|
||||||
# Preset buttons
|
# Preset buttons
|
||||||
for name, equation in DEFAULT_CURVES.items():
|
for name, equation in DEFAULT_CURVES.items():
|
||||||
ttk.Button(state.controls, text=name, command=lambda: rerender(equation)).pack()
|
ttk.Button(state.controls, text=name, command=lambda: set_curve(equation)).pack()
|
||||||
|
|
||||||
|
# Horizontal separator
|
||||||
|
tk.Frame(state.controls, height=2, bg="black").pack(fill=tk.X, pady=10)
|
||||||
|
|
||||||
|
# Operator controls
|
||||||
|
ttk.Button(state.controls, text="New random points", command=update_curve).pack()
|
||||||
|
|
||||||
# Matplotlib init
|
# Matplotlib init
|
||||||
state.ax = ax = state.figure.add_subplot()
|
state.ax = ax = state.figure.add_subplot()
|
||||||
ax.grid()
|
ax.grid()
|
||||||
ax.axhline(0, color='black', linewidth=1.5)
|
ax.axhline(0, color='black', linewidth=1.5)
|
||||||
ax.axvline(0, color='black', linewidth=1.5)
|
ax.axvline(0, color='black', linewidth=1.5)
|
||||||
|
ax.set_aspect('equal')
|
||||||
|
|
||||||
# Extra Packing
|
# Extra Packing
|
||||||
state.controls.pack(side=tk.RIGHT)
|
state.controls.pack(side=tk.RIGHT)
|
||||||
state.toolbar.pack(side=tk.BOTTOM, fill=tk.X)
|
state.toolbar.pack(side=tk.BOTTOM, fill=tk.X)
|
||||||
state.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
|
state.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
|
||||||
|
|
||||||
|
def run():
|
||||||
|
state.root.mainloop()
|
||||||
|
3
main.py
3
main.py
@ -7,8 +7,7 @@ import tkinter as tk
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
gui.init(elliptic_curve.DEFAULT_CURVES['Default'])
|
gui.init(elliptic_curve.DEFAULT_CURVES['Default'])
|
||||||
gui.display()
|
gui.run()
|
||||||
tk.mainloop()
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
33
util.py
33
util.py
@ -3,3 +3,36 @@ class Object(dict):
|
|||||||
return self[key]
|
return self[key]
|
||||||
def __setattr__(self, key, value):
|
def __setattr__(self, key, value):
|
||||||
self[key] = value
|
self[key] = value
|
||||||
|
|
||||||
|
# -- Line calculations
|
||||||
|
|
||||||
|
from sympy import Line, Point
|
||||||
|
def intersection(x1, y1, x2, y2, x3, y3, x4, y4):
|
||||||
|
p = Line(Point(x1, y1), Point(x2, y2)).intersection(Line(Point(x3, y3), Point(x4, y4)))[0]
|
||||||
|
x = float(p.x)
|
||||||
|
y = float(p.y)
|
||||||
|
return x, y
|
||||||
|
|
||||||
|
def x_axis_intersection(x1, y1, x2, y2):
|
||||||
|
return intersection(x1, y1, x2, y2, 0, 0, 1, 0)
|
||||||
|
|
||||||
|
def y_axis_intersection(x1, y1, x2, y2):
|
||||||
|
return intersection(x1, y1, x2, y2, 0, 0, 0, 1)
|
||||||
|
|
||||||
|
def multiplicative_inverse_over_prime_finite_field(a, p):
|
||||||
|
"""
|
||||||
|
NOTE: GIGO if not is_prime(p)
|
||||||
|
"""
|
||||||
|
def extended_gcd(a, b):
|
||||||
|
if a == 0:
|
||||||
|
return b, 0, 1
|
||||||
|
else:
|
||||||
|
gcd, x, y = extended_gcd(b % a, a)
|
||||||
|
return gcd, y - (b // a) * x, x
|
||||||
|
gcd, x, _ = extended_gcd(a, p)
|
||||||
|
return x % p
|
||||||
|
|
||||||
|
def print_multiplicative_inverse_over_prime_finite_field_table(p):
|
||||||
|
for i in range(1, p):
|
||||||
|
inverse = multiplicative_inverse_over_prime_finite_field(i, p)
|
||||||
|
print(i, inverse, (i * inverse) % p)
|
||||||
|
Reference in New Issue
Block a user