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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4ade0ad51689f11d1f36d2ac3e62c40a6ec61fef5142f11371e5403e195ad70a
4
- data.tar.gz: 0f7288540b1000124be29836ef664ea31d2670a9f0390c24b075e90febba6e42
3
+ metadata.gz: 3141783b56aaeef35545764e8016b975917e6da6a4000c741c8a4974741181a3
4
+ data.tar.gz: f45277dbbcef5551ca0ee0e756a6494dc26ade17906e5cc9692b197b1a597a57
5
5
  SHA512:
6
- metadata.gz: 457aef94ccb7bf0d107e54e85731d26c8463d24f6a176f5769f55737adb602af753441e7964ea8c5b1ccd53e16357a54eada4221ba62e14b696204f72f76942c
7
- data.tar.gz: 4604cc97de514dbb23cecf574a727e83510947158b6565909c6f556d621fab36b972cd2bbe128b89c693e715fc19d2123dcd44681b1bfe860b016eb93b7091eb
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
- ## Usage
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,7 +2,7 @@
2
2
 
3
3
  module HenselCode
4
4
  # verifications pre-evaluation of hensel codes
5
- module TFPEVerifier
5
+ module PAdicVerifier
6
6
  def valid?(other)
7
7
  incompatible_operand_type?(other)
8
8
  different_prime_and_same_exponent?(other)
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HenselCode
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
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, "hensel_code/tools"
18
- autoload :TFPEVerifier, "hensel_code/tfpe_verifier"
19
- autoload :TruncatedFinitePadicExpansion, "hensel_code/truncated_finite_padic_expansion"
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.1.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-03 00:00:00.000000000 Z
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/tfpe_verifier.rb
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