hensel_code 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +211 -3
- data/lib/hensel_code/finite_padic_expansion.rb +79 -0
- data/lib/hensel_code/padic_base.rb +78 -0
- data/lib/hensel_code/{tfpe_verifier.rb → padic_verifier.rb} +1 -1
- data/lib/hensel_code/truncated_finite_padic_expansion.rb +17 -78
- data/lib/hensel_code/version.rb +1 -1
- data/lib/hensel_code.rb +6 -3
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3141783b56aaeef35545764e8016b975917e6da6a4000c741c8a4974741181a3
|
4
|
+
data.tar.gz: f45277dbbcef5551ca0ee0e756a6494dc26ade17906e5cc9692b197b1a597a57
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e96d107ee37379ab95ff1daa3cea566dbc0383f2513db5c6054bbf93c8e74c82ce30b994325713085289b39337b8993639322b059bc26ddd180f3b367b510336
|
7
|
+
data.tar.gz: 7dfe989626ceb077f9b9115e94a6f8cf6f8aa4816bc255822edb1aaba81f258b48cd9642c6b3662f4b1ee9cf913b050bd098e92313ebfb738f90f5b0d7298310
|
data/README.md
CHANGED
@@ -1,9 +1,13 @@
|
|
1
1
|
# HenselCode
|
2
2
|
|
3
|
-
![example workflow](https://github.com/davidwilliam/hensel_code/actions/workflows/main.yml/badge.svg) [![codecov](https://codecov.io/gh/davidwilliam/hensel_code/branch/main/graph/badge.svg?token=XJ0C0U7P2M)](https://codecov.io/gh/davidwilliam/hensel_code) [![Ruby Style Guide](https://img.shields.io/badge/code_style-rubocop-brightgreen.svg)](https://github.com/rubocop/rubocop) ![GitHub](https://img.shields.io/github/license/davidwilliam/hensel_code)
|
3
|
+
![example workflow](https://github.com/davidwilliam/hensel_code/actions/workflows/main.yml/badge.svg) [![codecov](https://codecov.io/gh/davidwilliam/hensel_code/branch/main/graph/badge.svg?token=XJ0C0U7P2M)](https://codecov.io/gh/davidwilliam/hensel_code) [![Ruby Style Guide](https://img.shields.io/badge/code_style-rubocop-brightgreen.svg)](https://github.com/rubocop/rubocop) ![GitHub](https://img.shields.io/github/license/davidwilliam/hensel_code) ![Gem](https://img.shields.io/gem/v/hensel_code) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/davidwilliam/hensel_code)
|
4
4
|
|
5
5
|
Hensel Code allows you to homomorphically encode rational numbers as integers using the finite-segment p-adic arithmetic, also known as Hensel codes.
|
6
6
|
|
7
|
+
T. M. Rao describes the use of finite-segment p-adic arithmetic as a practical method for performing error-free computation and the term *Hensel code* as the first `r` digits of the infinite p-adic expansion of a rational number `x/y`. The use of Hensel codes allows us to replace the arithmetic opertations on rational numbers by corresponding aritmetic operation over the integers under certain conditions.
|
8
|
+
|
9
|
+
Rao also remarks that the theory of Hensel codes, although lifted from the p-adic number theory, can be introduced without the need of a complete undertanding of the theoretical aspects of p-adic numbers if the goal is to work with Hensel codes alone. This is due to the fact that the finite-segment p-adic arithmetic is well-defined, self-contained, and it has immediate pratical applications in a wide range of real-world scenarios.
|
10
|
+
|
7
11
|
## Mathematical Background
|
8
12
|
|
9
13
|
In our Wiki, you can find a brief [introduction to the mathematical background on Hensel codes](https://github.com/davidwilliam/hensel_code/wiki/Mathematical-Background). We will continue to update that area as we update the gem.
|
@@ -28,7 +32,14 @@ Or install it yourself as:
|
|
28
32
|
|
29
33
|
$ gem install hensel_code
|
30
34
|
|
31
|
-
|
35
|
+
# Usage
|
36
|
+
|
37
|
+
There are several types of Hensel codes. There are currently two of the available in the gem HenselCode:
|
38
|
+
|
39
|
+
1. Truncated finite-segment p-adic Hensel codes (added in v0.1.0)
|
40
|
+
2. Finite-segment p-adic Hensel codes (added in v0.2.0)
|
41
|
+
|
42
|
+
## Truncated finite-segment p-adic Hensel codes
|
32
43
|
|
33
44
|
Let `p=257` and `r=3`. Given two rational numbers `rat1 = Rational(3,5)` and `rat2 = Rational(4,3)`, we encode `rat1` and `rat2` as follows:
|
34
45
|
|
@@ -80,8 +91,101 @@ rat1 - rat2
|
|
80
91
|
# => (-11/15)
|
81
92
|
rat1 * rat2
|
82
93
|
# => (4/5)
|
94
|
+
rat1 / rat2
|
95
|
+
# => (9/20)
|
96
|
+
```
|
97
|
+
|
98
|
+
### Which Prime Should I Use?
|
99
|
+
|
100
|
+
The choice of the prime you should use for encoding rationals into integers depends on two factors:
|
101
|
+
|
102
|
+
1. What rationals you want to encode,
|
103
|
+
2. What computations you want to perform over the encoded rationals.
|
104
|
+
|
105
|
+
To help you in this decision, given any Hensel code object `h`, you can check `h.n`:
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
h = HenselCode::TruncatedFinitePadicExpansion.new p, r, Rational(2,3)
|
109
|
+
# => [HenselCode: 11316396, prime: 257, exponent: 3, modulus: 16974593]
|
110
|
+
h.n
|
111
|
+
# => 2913
|
83
112
|
```
|
84
113
|
|
114
|
+
In the mathematical background provided on our Wiki, you will see that the fractions associated with any choise of `p` and `r` are bounded by a value of `N`. In the HenselCode gem we refer to `n`, since capital letters are reserved to constants in Ruby. In the above example, we can encode fractions with numerator and denominator bounded in absolute value to `n` and we can perform computations on Hensel codes until a result that encodes a rational number with numerator and denominator bounded in absolute value to `n`.
|
115
|
+
|
116
|
+
### Correctness Depends on the Choices for `p` and `r`
|
117
|
+
|
118
|
+
If `p = 7` and `r = 2`, then `n = 4`. If I try to encode a rational number `rat = Rational(11,23)` with my choices of `p` and `r` I will not obtain the intended result:
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
rat = Rational(11,23)
|
122
|
+
h = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat
|
123
|
+
# => [HenselCode: 9, prime: 7, exponent: 2, modulus: 49]
|
124
|
+
h.to_r
|
125
|
+
# => (-4/5)
|
126
|
+
```
|
127
|
+
|
128
|
+
So, instead of `11/23`, we obtain `-4/5` and this happens because `n = 4` and the rational number `11/23` does not have numerator and denominator bounded in absolute value to `n`. Therefore correctness fails.
|
129
|
+
|
130
|
+
The same occurs with computation on Hensel codes. If `rat1 = Rational(2,3)` and `rat2 = Rational(3,4)`, both `rat1` and `rat2` can be correctly encoded and decoded with our choice of `p` and `r` since they both have numerators and denominators bounded in absolute value to `n = 4`. However, computation addition of `rat1` and `rat2` will fail correctness since the result violates the bound imposed by `n`:
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
h1 = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat1
|
134
|
+
# => [HenselCode: 17, prime: 7, exponent: 2, modulus: 49]
|
135
|
+
h2 = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat2
|
136
|
+
# => [HenselCode: 13, prime: 7, exponent: 2, modulus: 49]
|
137
|
+
h1_plus_h2 = h1 + h2
|
138
|
+
# => [HenselCode: 30, prime: 7, exponent: 2, modulus: 49]
|
139
|
+
h1_plus_h2.to_r
|
140
|
+
# => (3/5)
|
141
|
+
```
|
142
|
+
|
143
|
+
We obtain the incorrect result because `2/3 + 2/4 = 17/12` and this result is not supported by `p = 7` and `r = 2`.
|
144
|
+
|
145
|
+
If instead we define `p = 56807` and `r = 3`, we have:
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
h1 = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat1
|
149
|
+
=> [HenselCode: 122212127593296, prime: 56807, exponent: 3, modulus: 183318191389943]
|
150
|
+
h2 = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat2
|
151
|
+
=> [HenselCode: 137488643542458, prime: 56807, exponent: 3, modulus: 183318191389943]
|
152
|
+
h1_plus_h2 = h1 + h2
|
153
|
+
=> [HenselCode: 76382579745811, prime: 56807, exponent: 3, modulus: 183318191389943]
|
154
|
+
h1_plus_h2.to_r
|
155
|
+
# => (17/12)
|
156
|
+
```
|
157
|
+
|
158
|
+
This time we obtain the correct result because `h1.n = 9573875`, and therefore correctness is guaranteed to any result associated with a numerator and a denominator bounded in absolute value to `9573875`.
|
159
|
+
|
160
|
+
Thefore, the larger `p` and `r` are, the larger the rationals one can correctly encode and decode and the more computations on Hensel codes will be perfomed and correctly decoded.
|
161
|
+
|
162
|
+
### It Could Be Any Integer, But...
|
163
|
+
|
164
|
+
The p-adic number theory establishes that `p` is a prime number. The fact that a prime number does not share any common divisor (other than `1`) with any other number smaller than itself makes it a very special number.
|
165
|
+
|
166
|
+
However, as an example, one could decided that `p = 25` and `r = 3`. Notice that `25` is not prime, yet, will work for some cases:
|
167
|
+
|
168
|
+
```ruby
|
169
|
+
p = 25
|
170
|
+
r = 3
|
171
|
+
rat1 = Rational(1,2)
|
172
|
+
h1 = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat1
|
173
|
+
# => [HenselCode: 7813, prime: 25, exponent: 3, modulus: 15625]
|
174
|
+
h1.to_r
|
175
|
+
# => (1/2)
|
176
|
+
```
|
177
|
+
|
178
|
+
However, it will fail in the following case:
|
179
|
+
|
180
|
+
```ruby
|
181
|
+
rat2 = Rational(2,5)
|
182
|
+
h2 = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat2
|
183
|
+
# => 5 has no inverse modulo 15625 (ZeroDivisionError)
|
184
|
+
```
|
185
|
+
|
186
|
+
We can't compute a Hensel code for `2/5` with `p = 25` and `r = 3` because `gcd(5,25) = 5` and therefore there is no modular multiplicative inverse of `5` modulo `25`.
|
187
|
+
|
188
|
+
Therefore it is important that `p` is a prime (preferred), or a prime power power (less interesting since we already have `r`), or a prime composite (it can be useful for larger prime factors).
|
85
189
|
### Constraints
|
86
190
|
|
87
191
|
In order to operate on two or more Hensel codes, they all must be of the same object type and have the same prime and exponent, otherwise HenselCode will raise an exception. Again, let `rat1 = Rational(3,5)` and `rat2 = Rational(4,3)`, and `p1 = 241`, `p2 = 251`, `r1 = 3`, and `r2 = 4`:
|
@@ -94,7 +198,7 @@ h2 = HenselCode::TruncatedFinitePadicExpansion.new(p1, r2, rat1)
|
|
94
198
|
h3 = HenselCode::TruncatedFinitePadicExpansion.new(p2, r1, rat1)
|
95
199
|
# => [HenselCode: 6325301, prime: 251, exponent: 3, modulus: 15813251]
|
96
200
|
h4 = HenselCode::TruncatedFinitePadicExpansion.new(p2, r2, rat1)
|
97
|
-
=> [HenselCode: 1587650401, prime: 251, exponent: 4, modulus: 3969126001]
|
201
|
+
# => [HenselCode: 1587650401, prime: 251, exponent: 4, modulus: 3969126001]
|
98
202
|
```
|
99
203
|
|
100
204
|
The following operations will raise exceptions:
|
@@ -168,6 +272,110 @@ h.replace_hensel_code(38769823656)
|
|
168
272
|
# => (-685859/94809)
|
169
273
|
```
|
170
274
|
|
275
|
+
## Finite-segment p-adic Hensel codes
|
276
|
+
|
277
|
+
The truncated finite-segment p-adic Hensel codes can be described as a polynomial truncated to its leading coefficient/monomial in non-standard form (increasing degree order). The finite-segment p-adic Hensel codes are expressed with a polynomial of degree `r-1`.
|
278
|
+
|
279
|
+
Let `p = 359`, `r = 3`, and `rat = Rational(2,3)`:
|
280
|
+
|
281
|
+
```ruby
|
282
|
+
h1 = HenselCode::FinitePadicExpansion.new p, r, rat
|
283
|
+
# => [HenselCode: 240 + 119p + 239p^2, prime: 359, exponent: 3, modulus: 359]
|
284
|
+
puts h1
|
285
|
+
# => 240 + 119p + 239p^2
|
286
|
+
```
|
287
|
+
|
288
|
+
We say that `h` is a p-adic number with `r` digits. We clearly see the correspondence of p-adic digits if we compute a truncated Hensel code with the same `p` but `r=1`:
|
289
|
+
|
290
|
+
```ruby
|
291
|
+
h2 = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat
|
292
|
+
# => [HenselCode: 240, prime: 359, exponent: 1, modulus: 359]
|
293
|
+
```
|
294
|
+
|
295
|
+
Notice that the truncated Hensel code `h2` equals the first digit of the finite-segment Hensel code `h1`, which is also the same of computing
|
296
|
+
|
297
|
+
```ruby
|
298
|
+
HenselCode::FinitePadicExpansion.new p, 1, rat
|
299
|
+
# => [HenselCode: 240, prime: 359, exponent: 1, modulus: 359]
|
300
|
+
```
|
301
|
+
|
302
|
+
The following expressions are equivalent:
|
303
|
+
|
304
|
+
```ruby
|
305
|
+
r = 3
|
306
|
+
h1 = HenselCode::FinitePadicExpansion.new p, r, rat
|
307
|
+
# => [HenselCode: 240 + 119p + 239p^2, prime: 359, exponent: 3, modulus: 359]
|
308
|
+
h1.to_r
|
309
|
+
# => (2/3)
|
310
|
+
h2 = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat
|
311
|
+
# => [HenselCode: 30845520, prime: 359, exponent: 3, modulus: 46268279]
|
312
|
+
h2.to_r
|
313
|
+
# => (2/3)
|
314
|
+
```
|
315
|
+
|
316
|
+
that is, they not only represent the same rational but also `30845520` is just the evaluation of the polynomial `240 + 119p + 239p^2`.
|
317
|
+
|
318
|
+
We can obtain just the coefficients of `h1` as follows:
|
319
|
+
|
320
|
+
```ruby
|
321
|
+
h1.to_a
|
322
|
+
# => => [240, 119, 239]
|
323
|
+
```
|
324
|
+
|
325
|
+
We can obtain the truncated version of `h1` as follows:
|
326
|
+
|
327
|
+
```ruby
|
328
|
+
h1.to_truncated
|
329
|
+
# => [HenselCode: 30845520, prime: 359, exponent: 3, modulus: 46268279]
|
330
|
+
h1.to_truncated.class
|
331
|
+
# => HenselCode::TruncatedFinitePadicExpansion
|
332
|
+
```
|
333
|
+
|
334
|
+
#### Arithmetic
|
335
|
+
|
336
|
+
Let `p = 409`, `r = 5`, `rat1 = Rational(2,3)` and `rat2 = Rational(11,7)` such that
|
337
|
+
|
338
|
+
```ruby
|
339
|
+
h1 = HenselCode::FinitePadicExpansion.new p, r, rat1
|
340
|
+
# => [HenselCode: 137 + 136p + 136p^2 + 136p^3 + 136p^4, prime: 409, exponent: 5, modulus: 409]
|
341
|
+
h2 = HenselCode::FinitePadicExpansion.new p, r, rat2
|
342
|
+
# => [HenselCode: 60 + 292p + 233p^2 + 350p^3 + 116p^4, prime: 409, exponent: 5, modulus: 409]
|
343
|
+
```
|
344
|
+
|
345
|
+
We compute addition, subtraction, multiplication, and division as follows:
|
346
|
+
|
347
|
+
```ruby
|
348
|
+
h1_plus_h2 =h1 + h2
|
349
|
+
# => [HenselCode: 197 + 19p + 370p^2 + 77p^3 + 253p^4, prime: 409, exponent: 5, modulus: 409]
|
350
|
+
h1_minus_h2 = h1 - h2
|
351
|
+
# => [HenselCode: 77 + 253p + 311p^2 + 194p^3 + 19p^4, prime: 409, exponent: 5, modulus: 409]
|
352
|
+
h1_times_h2 = h1 * h2
|
353
|
+
# => [HenselCode: 40 + 331p + 155p^2 + 97p^3 + 214p^4, prime: 409, exponent: 5, modulus: 409]
|
354
|
+
h1_div_h2 = h1 / h2
|
355
|
+
# => [HenselCode: 50 + 161p + 12p^2 + 347p^3 + 309p^4, prime: 409, exponent: 5, modulus: 409]
|
356
|
+
```
|
357
|
+
|
358
|
+
And we can verify that
|
359
|
+
|
360
|
+
```ruby
|
361
|
+
h1_plus_h2.to_r
|
362
|
+
# => (47/21)
|
363
|
+
rat1 + rat2
|
364
|
+
# => (47/21)
|
365
|
+
rat1 - rat2
|
366
|
+
# => (-19/21)
|
367
|
+
h1_minus_h2.to_r
|
368
|
+
# => (-19/21)
|
369
|
+
h1_times_h2.to_r
|
370
|
+
# => (22/21)
|
371
|
+
rat1 * rat2
|
372
|
+
# => (22/21)
|
373
|
+
h1_div_h2.to_r
|
374
|
+
# => (14/33)
|
375
|
+
rat1 / rat2
|
376
|
+
# => (14/33)
|
377
|
+
```
|
378
|
+
|
171
379
|
### Class Alias
|
172
380
|
|
173
381
|
Since `HenselCode::TruncatedFinitePadicExpansion` is a bit long, the alias `HenselCode::TFPE` can be used instead.
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HenselCode
|
4
|
+
# finite p-adic expansion hensel code class
|
5
|
+
class FinitePadicExpansion < PAdicBase
|
6
|
+
def modulus
|
7
|
+
prime
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_a
|
11
|
+
hensel_code
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_truncated
|
15
|
+
TruncatedFinitePadicExpansion.new(prime, exponent, rational)
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
hensel_code.map.with_index do |h, i|
|
20
|
+
"#{h}#{polynomial_variable(i)}"
|
21
|
+
end.join(" + ")
|
22
|
+
end
|
23
|
+
|
24
|
+
def inspect
|
25
|
+
"[HenselCode: #{polynomial_form}, prime: #{prime}, exponent: #{exponent}, modulus: #{modulus}]"
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def evaluate(operation, other)
|
31
|
+
h_ = to_truncated.send(operation, other.to_truncated).hensel_code
|
32
|
+
new_hensel_code = (0..exponent - 1).map { |i| h_ / (prime**i) % prime }
|
33
|
+
self.class.new prime, exponent, new_hensel_code
|
34
|
+
end
|
35
|
+
|
36
|
+
def polynomial_variable(index)
|
37
|
+
i = index > 1 ? 2 : index
|
38
|
+
["", "p", "p^#{index}"][i]
|
39
|
+
end
|
40
|
+
|
41
|
+
def polynomial_form
|
42
|
+
to_s
|
43
|
+
end
|
44
|
+
|
45
|
+
def valid_number?(number)
|
46
|
+
if number.is_a?(Rational)
|
47
|
+
@rational = number
|
48
|
+
elsif number.is_a?(Array) && number.map(&:class).uniq == [Integer] && number.size == exponent
|
49
|
+
@hensel_code = number
|
50
|
+
decode
|
51
|
+
else
|
52
|
+
message = "number must be a Rational or an\
|
53
|
+
Array of integers of size #{exponent}"
|
54
|
+
raise WrongHenselCodeInputType, message
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def valid_hensel_code?(new_hensel_code)
|
59
|
+
conditions = [
|
60
|
+
new_hensel_code.is_a?(Array),
|
61
|
+
new_hensel_code.map(&:class).uniq == [Integer],
|
62
|
+
new_hensel_code.size == exponent
|
63
|
+
]
|
64
|
+
message = "must be an array of integers of size #{exponent}"
|
65
|
+
raise WrongHenselCodeInputType, message unless conditions.uniq == [true]
|
66
|
+
end
|
67
|
+
|
68
|
+
def encode
|
69
|
+
h_ = TruncatedFinitePadicExpansion.new(prime, exponent, rational).hensel_code
|
70
|
+
@hensel_code = (0..exponent - 1).map { |i| h_ / (prime**i) % prime }
|
71
|
+
end
|
72
|
+
|
73
|
+
def decode
|
74
|
+
number = 0
|
75
|
+
hensel_code.each_with_index { |d, i| number += d * (prime**i) }
|
76
|
+
@rational = TruncatedFinitePadicExpansion.new(prime, exponent, number).to_r
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HenselCode
|
4
|
+
# base hensel code class
|
5
|
+
class PAdicBase
|
6
|
+
include Tools
|
7
|
+
include PAdicVerifier
|
8
|
+
|
9
|
+
attr_accessor :prime, :exponent, :rational, :hensel_code, :n
|
10
|
+
private :prime=, :exponent=, :rational=, :hensel_code=
|
11
|
+
|
12
|
+
def initialize(prime, exponent, number)
|
13
|
+
@prime = prime
|
14
|
+
@exponent = exponent
|
15
|
+
@n = Integer.sqrt(((prime**exponent) - 1) / 2)
|
16
|
+
valid_number?(number)
|
17
|
+
encode
|
18
|
+
end
|
19
|
+
|
20
|
+
def numerator
|
21
|
+
rational.numerator
|
22
|
+
end
|
23
|
+
|
24
|
+
def denominator
|
25
|
+
rational.denominator
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_r
|
29
|
+
decode
|
30
|
+
rational
|
31
|
+
end
|
32
|
+
|
33
|
+
def +(other)
|
34
|
+
valid?(other)
|
35
|
+
evaluate("+", other)
|
36
|
+
end
|
37
|
+
|
38
|
+
def -(other)
|
39
|
+
valid?(other)
|
40
|
+
evaluate("-", other)
|
41
|
+
end
|
42
|
+
|
43
|
+
def *(other)
|
44
|
+
valid?(other)
|
45
|
+
evaluate("*", other)
|
46
|
+
end
|
47
|
+
|
48
|
+
def /(other)
|
49
|
+
valid?(other)
|
50
|
+
evaluate("/", other)
|
51
|
+
end
|
52
|
+
|
53
|
+
def replace_prime(new_prime)
|
54
|
+
replace_attribute("prime=", new_prime, 0)
|
55
|
+
end
|
56
|
+
|
57
|
+
def replace_exponent(new_exponent)
|
58
|
+
replace_attribute("exponent=", new_exponent, 0)
|
59
|
+
end
|
60
|
+
|
61
|
+
def replace_rational(new_rational)
|
62
|
+
replace_attribute("rational=", new_rational, 0)
|
63
|
+
end
|
64
|
+
|
65
|
+
def replace_hensel_code(new_hensel_code)
|
66
|
+
valid_hensel_code?(new_hensel_code)
|
67
|
+
replace_attribute("hensel_code=", new_hensel_code, 1)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def replace_attribute(attribute, new_value, order)
|
73
|
+
send(attribute, new_value)
|
74
|
+
order.zero? ? [encode, decode] : [decode, encode]
|
75
|
+
self
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -2,39 +2,11 @@
|
|
2
2
|
|
3
3
|
module HenselCode
|
4
4
|
# truncated finite p-adic expansion hensel code class
|
5
|
-
class TruncatedFinitePadicExpansion
|
6
|
-
include Tools
|
7
|
-
include TFPEVerifier
|
8
|
-
|
9
|
-
attr_accessor :prime, :exponent, :rational, :hensel_code, :n
|
10
|
-
private :prime=, :exponent=, :rational=, :hensel_code=
|
11
|
-
|
12
|
-
def initialize(prime, exponent, number)
|
13
|
-
@prime = prime
|
14
|
-
@exponent = exponent
|
15
|
-
@n = Integer.sqrt((modulus - 1) / 2)
|
16
|
-
|
17
|
-
valid_number?(number)
|
18
|
-
encode
|
19
|
-
end
|
20
|
-
|
5
|
+
class TruncatedFinitePadicExpansion < PAdicBase
|
21
6
|
def modulus
|
22
7
|
prime**exponent
|
23
8
|
end
|
24
9
|
|
25
|
-
def numerator
|
26
|
-
rational.numerator
|
27
|
-
end
|
28
|
-
|
29
|
-
def denominator
|
30
|
-
rational.denominator
|
31
|
-
end
|
32
|
-
|
33
|
-
def to_r
|
34
|
-
decode
|
35
|
-
rational
|
36
|
-
end
|
37
|
-
|
38
10
|
def to_i
|
39
11
|
hensel_code
|
40
12
|
end
|
@@ -43,61 +15,23 @@ module HenselCode
|
|
43
15
|
hensel_code.to_s
|
44
16
|
end
|
45
17
|
|
46
|
-
def replace_prime(new_prime)
|
47
|
-
self.prime = new_prime
|
48
|
-
encode
|
49
|
-
decode
|
50
|
-
self
|
51
|
-
end
|
52
|
-
|
53
|
-
def replace_exponent(new_exponent)
|
54
|
-
self.exponent = new_exponent
|
55
|
-
encode
|
56
|
-
decode
|
57
|
-
self
|
58
|
-
end
|
59
|
-
|
60
|
-
def replace_rational(new_rational)
|
61
|
-
self.rational = new_rational
|
62
|
-
encode
|
63
|
-
decode
|
64
|
-
self
|
65
|
-
end
|
66
|
-
|
67
|
-
def replace_hensel_code(new_hensel_code)
|
68
|
-
self.hensel_code = new_hensel_code
|
69
|
-
decode
|
70
|
-
encode
|
71
|
-
self
|
72
|
-
end
|
73
|
-
|
74
|
-
def +(other)
|
75
|
-
valid?(other)
|
76
|
-
self.class.new prime, exponent, (hensel_code + other.hensel_code) % modulus
|
77
|
-
end
|
78
|
-
|
79
|
-
def -(other)
|
80
|
-
valid?(other)
|
81
|
-
self.class.new prime, exponent, (hensel_code - other.hensel_code) % modulus
|
82
|
-
end
|
83
|
-
|
84
|
-
def *(other)
|
85
|
-
valid?(other)
|
86
|
-
self.class.new prime, exponent, (hensel_code * other.hensel_code) % modulus
|
87
|
-
end
|
88
|
-
|
89
|
-
def /(other)
|
90
|
-
valid?(other)
|
91
|
-
h2_hensel_code_inverse = mod_inverse(other.hensel_code, modulus)
|
92
|
-
self.class.new prime, exponent, (hensel_code * h2_hensel_code_inverse) % modulus
|
93
|
-
end
|
94
|
-
|
95
18
|
def inspect
|
96
19
|
"[HenselCode: #{hensel_code}, prime: #{prime}, exponent: #{exponent}, modulus: #{modulus}]"
|
97
20
|
end
|
98
21
|
|
99
22
|
private
|
100
23
|
|
24
|
+
def evaluate(operation, other)
|
25
|
+
if operation == "/"
|
26
|
+
other_hensel_code = mod_inverse(other.hensel_code, modulus)
|
27
|
+
op = "*"
|
28
|
+
else
|
29
|
+
other_hensel_code = other.hensel_code
|
30
|
+
op = operation
|
31
|
+
end
|
32
|
+
self.class.new prime, exponent, hensel_code.send(op, other_hensel_code) % modulus
|
33
|
+
end
|
34
|
+
|
101
35
|
def valid_number?(number)
|
102
36
|
case number
|
103
37
|
when Rational
|
@@ -111,6 +45,11 @@ module HenselCode
|
|
111
45
|
end
|
112
46
|
end
|
113
47
|
|
48
|
+
def valid_hensel_code?(new_hensel_code)
|
49
|
+
message = "must be an integer up to #{modulus - 1}"
|
50
|
+
raise WrongHenselCodeInputType, message unless new_hensel_code < modulus
|
51
|
+
end
|
52
|
+
|
114
53
|
def encode
|
115
54
|
denominator_inverse = mod_inverse(denominator, modulus)
|
116
55
|
@hensel_code = (numerator * denominator_inverse) % modulus
|
data/lib/hensel_code/version.rb
CHANGED
data/lib/hensel_code.rb
CHANGED
@@ -14,11 +14,14 @@ module HenselCode
|
|
14
14
|
class HenselCodesWithDifferentExponents < StandardError; end
|
15
15
|
class IncompatibleOperandTypes < StandardError; end
|
16
16
|
|
17
|
-
autoload :Tools,
|
18
|
-
autoload :
|
19
|
-
autoload :
|
17
|
+
autoload :Tools, "hensel_code/tools"
|
18
|
+
autoload :PAdicBase, "hensel_code/padic_base"
|
19
|
+
autoload :PAdicVerifier, "hensel_code/padic_verifier"
|
20
|
+
autoload :FinitePadicExpansion, "hensel_code/finite_padic_expansion"
|
21
|
+
autoload :TruncatedFinitePadicExpansion, "hensel_code/truncated_finite_padic_expansion"
|
20
22
|
|
21
23
|
# aliases for classes with long names
|
22
24
|
TFPE = TruncatedFinitePadicExpansion
|
23
25
|
HCWDPAE = HenselCodesWithDifferentPrimesAndExponents
|
26
|
+
WHIT = WrongHenselCodeInputType
|
24
27
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hensel_code
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David William Silva
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-03-
|
11
|
+
date: 2022-03-05 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A Ruby library for homomorphically representing rational numbers as integers.
|
14
14
|
email:
|
@@ -26,7 +26,9 @@ files:
|
|
26
26
|
- Rakefile
|
27
27
|
- codecov
|
28
28
|
- lib/hensel_code.rb
|
29
|
-
- lib/hensel_code/
|
29
|
+
- lib/hensel_code/finite_padic_expansion.rb
|
30
|
+
- lib/hensel_code/padic_base.rb
|
31
|
+
- lib/hensel_code/padic_verifier.rb
|
30
32
|
- lib/hensel_code/tools.rb
|
31
33
|
- lib/hensel_code/truncated_finite_padic_expansion.rb
|
32
34
|
- lib/hensel_code/version.rb
|