forked from mana-ethereum/bn
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfq.ex
More file actions
148 lines (113 loc) · 4.01 KB
/
Copy pathfq.ex
File metadata and controls
148 lines (113 loc) · 4.01 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
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
defmodule BN.FQ do
defstruct [:value, :modulus]
@type t :: %__MODULE__{
value: integer(),
modulus: integer()
}
@default_modulus 21_888_242_871_839_275_222_246_405_745_257_275_088_696_311_157_297_823_662_689_037_894_645_226_208_583
alias BN.FQ.ExtendedEuclideanAlgorithm
@spec new(integer() | t(), keyword()) :: t()
def new(number, params \\ [])
def new(number = %__MODULE__{}, _), do: number
def new(number, params) do
modulus = Keyword.get(params, :modulus, @default_modulus)
value =
number
|> rem(modulus)
|> make_positive(modulus)
%__MODULE__{value: value, modulus: modulus}
end
@spec one() :: t()
def one do
new(1)
end
@spec zero() :: t()
def zero do
new(0)
end
@spec add(t(), t()) :: t() | no_return
def add(%__MODULE__{modulus: modulus1}, %__MODULE__{modulus: modulus2})
when modulus1 != modulus2 do
raise(ArgumentError, message: "Numbers calculated with different modulus")
end
def add(number1 = %__MODULE__{}, number2 = %__MODULE__{}) do
new(number1.value + number2.value, modulus: number1.modulus)
end
def add(_, _) do
raise ArgumentError, message: "#{__MODULE__}.add/2 can only add #{__MODULE__} structs"
end
@spec sub(t(), t()) :: t() | no_return
def sub(%__MODULE__{modulus: modulus1}, %__MODULE__{modulus: modulus2})
when modulus1 != modulus2 do
raise(ArgumentError, message: "Numbers calculated with different modulus")
end
def sub(number1 = %__MODULE__{}, number2 = %__MODULE__{}) do
new(number1.value - number2.value, modulus: number1.modulus)
end
def sub(_, _) do
raise ArgumentError, message: "#{__MODULE__}.sub/2 can only substract #{__MODULE__} structs"
end
@spec mult(t(), t() | integer()) :: t() | no_return
def mult(%__MODULE__{modulus: modulus1}, %__MODULE__{modulus: modulus2})
when modulus1 != modulus2 do
raise(ArgumentError, message: "Numbers calculated with different modulus")
end
def mult(number1 = %__MODULE__{}, number2 = %__MODULE__{}) do
new(number1.value * number2.value, modulus: number1.modulus)
end
def mult(number1 = %__MODULE__{}, number2) do
new(number1.value * number2, modulus: number1.modulus)
end
def mult(_, _) do
raise ArgumentError,
message: "#{__MODULE__}.sub/2 can only multiplicate #{__MODULE__} structs"
end
@spec divide(t(), t()) :: t() | no_return
def divide(%__MODULE__{modulus: modulus1}, %__MODULE__{modulus: modulus2})
when modulus1 != modulus2 do
raise(ArgumentError, message: "Numbers calculated with different modulus")
end
def divide(number1 = %__MODULE__{}, number2 = %__MODULE__{}) do
divide(number1, number2.value)
end
def divide(number1 = %__MODULE__{}, number2) when is_integer(number2) do
{1, inverse} = ExtendedEuclideanAlgorithm.extended_gcd(number2, number1.modulus)
mult(number1, inverse)
end
def divide(number1, number2) when is_integer(number2) and is_integer(number1) do
{1, inverse} = ExtendedEuclideanAlgorithm.extended_gcd(number2, default_modulus())
number1
|> new()
|> mult(inverse)
end
def divide(_, _) do
raise ArgumentError,
message: "#{__MODULE__}.div/2 can only divide #{__MODULE__} structs"
end
@spec pow(t(), integer()) :: t() | no_return
def pow(base = %__MODULE__{}, exponent) do
case exponent do
0 ->
new(1, modulus: base.modulus)
1 ->
base
_ ->
base.value
|> :crypto.mod_pow(exponent, base.modulus)
|> :binary.decode_unsigned()
|> new(modulus: base.modulus)
end
end
def pow(_, _) do
raise ArgumentError,
message: "#{__MODULE__}.pow/2 can only exponent #{__MODULE__} structs"
end
@spec default_modulus() :: integer()
def default_modulus, do: @default_modulus
@spec make_positive(integer(), integer()) :: integer()
defp make_positive(number, _) when number >= 0, do: number
defp make_positive(number, modulus) do
updated_number = number + modulus
make_positive(updated_number, modulus)
end
end