hensel_code 0.1.0 → 0.3.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: 50f6a13e6cba426fca07fa46a610b85ef7fae043a4a380481cb20ab6df14de3a
4
+ data.tar.gz: 89ac88e608472e67fe00238d1e974462747870bf13aa6f85d2391c559656cfcf
5
5
  SHA512:
6
- metadata.gz: 457aef94ccb7bf0d107e54e85731d26c8463d24f6a176f5769f55737adb602af753441e7964ea8c5b1ccd53e16357a54eada4221ba62e14b696204f72f76942c
7
- data.tar.gz: 4604cc97de514dbb23cecf574a727e83510947158b6565909c6f556d621fab36b972cd2bbe128b89c693e715fc19d2123dcd44681b1bfe860b016eb93b7091eb
6
+ metadata.gz: d67e6bdb32a0a1c70fecc6b00a6d5a3e7b7a6ca426446b02c9b44e3c84f059dccf79e17d6567013ec61f2ddb2641fdc41f34b0887dc36bc287cba89452329398
7
+ data.tar.gz: 7f4c9d9deb9edb968e99ad1c67e7cb597789169af91c6c83c9c1288990c531d7fcd732065cd71ce4a7f3cb6fd364667d2cd92932a5c20a4b0dad89bdf0b088d7
data/.codecov.yml ADDED
@@ -0,0 +1,6 @@
1
+ coverage:
2
+ status:
3
+ project:
4
+ default:
5
+ target: 99% # the required coverage value
6
+ threshold: 1% # the leniency in hitting the target
data/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
- ## [Unreleased]
1
+ ## [0.3.0] - 2022-03-12
2
2
 
3
- ## [0.1.0] - 2022-03-01
3
+ - Add the `Polynomial` class for arithmetic with fixed-length polynomials
4
+ - Change the class `TruncatedFinitePadicExpansion` for carrying all computaitons over fixed-length polynomials reduced modulo `p`
5
+ - Add the third type of supported Hensel code: the truncated finite-segment g-adic Hensel code.
6
+ - Add `GAdicBase` as the parent class of `TruncatedFiniteGadicExpansion`.
7
+ - Allow converting a finite-segment p-adic Hensel code into a truncated p-adic Hensel code.
8
+ - Add the Chinese Remainder Theorem algorithm (CRT) to the module `Tools`
9
+ - Improve helpers for generating distinct random numbers (integers, primes, and rationals)
10
+ - Add the inverse function for all three currently supported types of Hensel codes
11
+ - Overall improvements in the code
12
+
13
+ ## [0.2.1] - 2022-03-05
14
+ - Fix version build
15
+
16
+ ## [0.2.0] - 2022-03-05
17
+
18
+ - Add the second type of supported Hensel code: the finite-segment p-adic Hensel code.
19
+ - Allow converting a finite-segment p-adic Hensel code into a truncated p-adic Hensel code.
20
+ - Support all four arithemtic operations.
21
+ - Add `PAdicBase` as the parent class of `FinitePadicExpansion` and `TruncatedFinitePadicExpansion`.
22
+
23
+
24
+ ## [0.1.0] - 2022-03-03
4
25
 
5
26
  - Initial release
27
+ - Contain general tools for integer manipulation such us random number generation, random integer generation, extended gcd, and modular multiplicative inverse.
28
+ - Add the first type of supported Hensel code: the truncated finite-segment p-adic expansion Hensel code or simply truncated p-adic Hensel code.
29
+ - Allow encoding rational numbers with the classs `TruncatedFinitePadicExpansion` and perform all four basic arithmetic operations on truncated p-adic Hensel codes: addition, subtraction, multiplication, and division.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- hensel_code (0.1.0)
4
+ hensel_code (0.3.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -1,9 +1,19 @@
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
+
5
+ ***NOTICE:*** this README is beign constantly updated. I am currently focused on coding since I want to release as many different types of Hensel codes as possible. At the same time, I want this README to be as informative as possible even for those completely unfamiliar with p-adic numbers and Hensel codes. Therefore, even not in the pace I would want, I will continue to add information to the README to facilitate the use of the gem and to give some practical ideas of the computational possibilities enabled by it.
4
6
 
5
7
  Hensel Code allows you to homomorphically encode rational numbers as integers using the finite-segment p-adic arithmetic, also known as Hensel codes.
6
8
 
9
+ 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.
10
+
11
+ 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.
12
+
13
+ Ç. K. Koç remarks that the p-adic arithmetic allows error-free representation of fractions and error-ree arithmetic using fractions where infinite-precision p-adic arithemtic is more suitable for software implementation and finite-precision p-adic arithmetic is more suitable for hardware implementations.
14
+
15
+ A p-adic number can be uniquely written as a inifite p-adic expansion, for `p` prime, where the associated coefficients are integers between `0` and `p - 1`. When this p-adic expansion is finite in length, then we have a finite-segment p-adic expansion. When we only consider the constant term of a p-adic expansion, then we have a truncated finite-segment p-adic expansion. There many types of representations of rationals lifted from the p-adic number theory, and therefore many types of Hensel codes.
16
+
7
17
  ## Mathematical Background
8
18
 
9
19
  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,15 +38,43 @@ Or install it yourself as:
28
38
 
29
39
  $ gem install hensel_code
30
40
 
31
- ## Usage
41
+ # HenselCode
42
+
43
+ There are several types of Hensel codes in the finite-segment p-adic number theory. There are currently three of them available in the gem HenselCode:
44
+
45
+ 1. Truncated finite-segment p-adic Hensel codes
46
+ 2. Finite-segment p-adic Hensel codes
47
+ 3. Truncated finite-segment g-adic Hensel codes
48
+
49
+ For each type of supported Hensel code I will briefly discuss their properties and capabilities as well as unique features that make each type of Hensel code distinct from each other.
50
+
51
+ ## Truncated finite-segment p-adic Hensel codes
52
+
53
+ ### Description
54
+
55
+ The truncated finite-segment p-adic Hensel codes are integer representations of rationals with respect to a prime `p` and a positive exponent `r`. This integer representation of any given rational is equivalent to a constant term of the finite-segment p-adic expansion that represents that rational.
56
+
57
+ ### Unique Benefits
58
+
59
+ The truncated finite-segment p-adic Hensel codes are the simplest type of Hensel codes and the easiest ones to perform computations on it. Given a prime `p` and an exponent `r`, Hensel codes are integers between `0` and `p^r - 1`. In Ruby, as in many other modern scripting languages, integers can be arbitarily large, and therefore `p` can be as large as computationally affordable. Addition, subtraction, multiplication, and division on Hensel codes are simply these operations modulo `p^r`. Encoding and decoding are also straightforward. Its use is ideal for applications with very large numbers and/or many consecutive homomorphic computations on Hensel codes, which can be achieved with the efficincy of computations over the integers.
60
+
61
+ ### Usage
32
62
 
33
63
  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
64
 
35
65
  ```ruby
36
66
  h1 = HenselCode::TruncatedFinitePadicExpansion.new(p, r, rat1)
37
- # => [HenselCode: 13579675, prime: 257, exponent: 3, modulus: 16974593]
67
+ # => <HenselCode: 13579675>
38
68
  h2 = HenselCode::TruncatedFinitePadicExpansion.new(p, r, rat1)
39
- # => [HenselCode: 5658199, prime: 257, exponent: 3, modulus: 16974593]
69
+ # => <HenselCode: 5658199>
70
+ h1.n
71
+ # => 2913
72
+ h1.prime
73
+ # => 257
74
+ h1.exponent
75
+ # => 3
76
+ h1.modulus
77
+ # => 16974593
40
78
  h1.class
41
79
  # => HenselCode::TruncatedFinitePadicExpansion
42
80
  h2.class
@@ -51,9 +89,19 @@ Now we can carry arithmetic computations on the `h1` and `h2` objects as if we w
51
89
 
52
90
  ```ruby
53
91
  h1_plus_h2 = h1 + h2
92
+ # => <HenselCode: 2263281>
54
93
  h1_minus_h2 = h1 - h2
94
+ # => <HenselCode: 7921476>
55
95
  h1_times_h2 = h1 * h2
96
+ # => <HenselCode: 6789838>
56
97
  h1_div_h2 = h1 / h2
98
+ # => <HenselCode: 5941108>
99
+ h2.inverse
100
+ # => <HenselCode: 4243649>
101
+ h1 * h2.inverse
102
+ # => <HenselCode: 5941108>
103
+ h2 * h2.inverse
104
+ # => <HenselCode: 1>
57
105
  ```
58
106
 
59
107
  All the computations are reduced modulo `p^r`.
@@ -80,21 +128,114 @@ rat1 - rat2
80
128
  # => (-11/15)
81
129
  rat1 * rat2
82
130
  # => (4/5)
131
+ rat1 / rat2
132
+ # => (9/20)
133
+ ```
134
+
135
+ ### Which Prime Should I Use?
136
+
137
+ The choice of the prime you should use for encoding rationals into integers depends on two factors:
138
+
139
+ 1. What rationals you want to encode,
140
+ 2. What computations you want to perform over the encoded rationals.
141
+
142
+ To help you in this decision, given any Hensel code object `h`, you can check `h.n`:
143
+
144
+ ```ruby
145
+ h = HenselCode::TruncatedFinitePadicExpansion.new p, r, Rational(2,3)
146
+ # => <HenselCode: 11316396>
147
+ h.n
148
+ # => 2913
149
+ ```
150
+
151
+ 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`.
152
+
153
+ ### Correctness Depends on the Choices for `p` and `r`
154
+
155
+ 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:
156
+
157
+ ```ruby
158
+ rat = Rational(11,23)
159
+ h = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat
160
+ # => <HenselCode: 9>
161
+ h.to_r
162
+ # => (-4/5)
163
+ ```
164
+
165
+ 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.
166
+
167
+ 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`:
168
+
169
+ ```ruby
170
+ h1 = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat1
171
+ # => <HenselCode: 17>
172
+ h2 = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat2
173
+ # => <HenselCode: 13>
174
+ h1_plus_h2 = h1 + h2
175
+ # => <HenselCode: 30>
176
+ h1_plus_h2.to_r
177
+ # => (3/5)
178
+ ```
179
+
180
+ We obtain the incorrect result because `2/3 + 2/4 = 17/12` and this result is not supported by `p = 7` and `r = 2`.
181
+
182
+ If instead we define `p = 56807` and `r = 3`, we have:
183
+
184
+ ```ruby
185
+ h1 = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat1
186
+ # => <HenselCode: 122212127593296>
187
+ h2 = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat2
188
+ # => <HenselCode: 137488643542458>
189
+ h1_plus_h2 = h1 + h2
190
+ # => <HenselCode: 76382579745811>
191
+ h1_plus_h2.to_r
192
+ # => (17/12)
193
+ ```
194
+
195
+ 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`.
196
+
197
+ 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.
198
+
199
+ ### It Could Be Any Integer, But...
200
+
201
+ 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.
202
+
203
+ 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:
204
+
205
+ ```ruby
206
+ p = 25
207
+ r = 3
208
+ rat1 = Rational(1,2)
209
+ h1 = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat1
210
+ # => <HenselCode: 7813>
211
+ h1.to_r
212
+ # => (1/2)
83
213
  ```
84
214
 
215
+ However, it will fail in the following case:
216
+
217
+ ```ruby
218
+ rat2 = Rational(2,5)
219
+ h2 = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat2
220
+ # => 5 has no inverse modulo 15625 (ZeroDivisionError)
221
+ ```
222
+
223
+ 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`.
224
+
225
+ 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
226
  ### Constraints
86
227
 
87
228
  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`:
88
229
 
89
230
  ```ruby
90
231
  h1 = HenselCode::TruncatedFinitePadicExpansion.new(p1, r1, rat1)
91
- # => [HenselCode: 5599009, prime: 241, exponent: 3, modulus: 13997521]
232
+ # => <HenselCode: 5599009>
92
233
  h2 = HenselCode::TruncatedFinitePadicExpansion.new(p1, r2, rat1)
93
- # => [HenselCode: 1349361025, prime: 241, exponent: 4, modulus: 3373402561]
234
+ # => <HenselCode: 1349361025>
94
235
  h3 = HenselCode::TruncatedFinitePadicExpansion.new(p2, r1, rat1)
95
- # => [HenselCode: 6325301, prime: 251, exponent: 3, modulus: 15813251]
236
+ # => <HenselCode: 6325301>
96
237
  h4 = HenselCode::TruncatedFinitePadicExpansion.new(p2, r2, rat1)
97
- => [HenselCode: 1587650401, prime: 251, exponent: 4, modulus: 3969126001]
238
+ # => <HenselCode: 1587650401>
98
239
  ```
99
240
 
100
241
  The following operations will raise exceptions:
@@ -117,7 +258,7 @@ Let `p = 541`, `r = 3`, `rat = Rational(11,5)`. We create a Hensel code as befor
117
258
 
118
259
  ```ruby
119
260
  h = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat
120
- # => [HenselCode: 126672339, prime: 541, exponent: 3, modulus: 158340421]
261
+ # => <HenselCode: 126672339>
121
262
  ```
122
263
 
123
264
  We can change the prime and the exponent:
@@ -126,9 +267,9 @@ We can change the prime and the exponent:
126
267
  p = 1223
127
268
  r = 4
128
269
  h.replace_prime(p)
129
- # => [HenselCode: 731710629, prime: 1223, exponent: 3, modulus: 1829276567]
270
+ # => <HenselCode: 731710629>
130
271
  h.replace_exponent(r)
131
- # => [HenselCode: 1789764193155, prime: 1223, exponent: 4, modulus: 2237205241441]
272
+ # => <HenselCode: 1789764193155>
132
273
  ```
133
274
 
134
275
  Any change in the prime and/or the exponent of a Hensel code object will change the Hensel code value and the modulus as well, however, the Hensel code object continues to refer to represent the same rational number:
@@ -143,7 +284,7 @@ We can also change the rational number:
143
284
  ```ruby
144
285
  rat = Rational(13,7)
145
286
  h.replace_rational(rat)
146
- # => [HenselCode: 1278402995111, prime: 1223, exponent: 4, modulus: 2237205241441]
287
+ # => <HenselCode: 1278402995111>
147
288
  h.to_r
148
289
  # => (13/7)
149
290
  ```
@@ -152,6 +293,7 @@ We can initiate a Hensel code object with its Hensel code value, instead of a ra
152
293
 
153
294
  ```ruby
154
295
  h = HenselCode::TruncatedFinitePadicExpansion.new p, r, 53673296543
296
+ # => <HenselCode: 53673296543>
155
297
  ```
156
298
 
157
299
  and then we can check what is the rational number represented by the resulting object:
@@ -165,12 +307,326 @@ We can update the Hensel code value of an existing Hensel code object:
165
307
 
166
308
  ```ruby
167
309
  h.replace_hensel_code(38769823656)
168
- # => (-685859/94809)
310
+ # => <HenselCode: 38769823656>
311
+ ```
312
+
313
+ ## Polynomials
314
+
315
+ In order to support finite-segment p-adic Hensel codes, the HenselCode gem offers an engine for computing fixed-length polynomials where all the computations ared reduced modulo `p`.
316
+
317
+ Let `p = 257`. We intantiate polynomials as follows:
318
+
319
+ ```ruby
320
+ f = HenselCode::Polynomial.new p, [29, 102, 232]
321
+ # => <Polynomial: 29 + 102p + 232p^2>
322
+ g = HenselCode::Polynomial.new p, [195, 83, 244]
323
+ # => <Polynomial: 195 + 83p + 244p^2>
324
+ f.prime
325
+ # => 257
326
+ f.coefficients
327
+ # => [29, 102, 232]
328
+ f.degree
329
+ # => 2
330
+ puts f
331
+ # => 29 + 102p + 232p^2
332
+ ```
333
+
334
+ ### Arithmetic
335
+
336
+ All the mathematical oeprations objects of the `Polynomial` are over fixed-length single-variable polynomails in non-standard form (the terms are in ascending order with respect to their degrees). All the computation on the polynomial coefficients are reduced modulo `p` in every single step of unitary calculations, that is, no indivudal step of computation exceeds an addition followed by a binary multiplication in which the operands are bounded by `p` (a carry + a product of two integers). Therefore, if `p` has bit length `b`, the maximum space required for expanding the result of each step of computation is `1 + 2b` bits.
337
+
338
+ ```ruby
339
+ f + g
340
+ # => <Polynomial: 224 + 185p + 219p^2>
341
+ f - g
342
+ # => <Polynomial: 91 + 18p + 245p^2>
343
+ f * g
344
+ # => <Polynomial: 1 + 217p + 216p^2>
345
+ f / g
346
+ # => <Polynomial: 70 + 238p + 233p^2>
347
+ g.inverse
348
+ # => <Polynomial: 29 + 234p + 219p^2
349
+ f * g.inverse
350
+ # => <Polynomial: 70 + 238p + 233p^2>
351
+ g * g.inverse
352
+ # => <Polynomial: 1 + 0p + 0p^2>
169
353
  ```
170
354
 
171
- ### Class Alias
355
+ ### Constraints
356
+
357
+ Operations with fixed-length polynomials require operands with the same degree:
358
+
359
+ ```ruby
360
+ f = HenselCode::Polynomial.new p, [29, 102, 232]
361
+ # => <Polynomial: 29 + 102p + 232p^2>
362
+ g = HenselCode::Polynomial.new p, [195, 83, 244, 99]
363
+ # => <Polynomial: 195 + 83p + 244p^2 + 99p^3>
364
+ f.degree
365
+ # => 2
366
+ g.degree
367
+ # => 3
368
+ f + g
369
+ # => polynomials must have same degree (HenselCode::WrongHenselCodeInputType)
370
+ ```
371
+
372
+ Operations with fixed-length polynomials also require operands with the same prime:
373
+
374
+ ```ruby
375
+ f = HenselCode::Polynomial.new 251, [133, 206, 58]
376
+ # => <Polynomial: 133 + 206p + 58p^2>
377
+ g = HenselCode::Polynomial.new 257, [105, 129, 238]
378
+ # => <Polynomial: 105 + 129p + 238p^2>
379
+ f + g
380
+ # => polynomials must have same prime (HenselCode::WrongHenselCodeInputType)
381
+ g.prime = 251
382
+ # => 251
383
+ f + g
384
+ # => <Polynomial: 238 + 84p + 46p^2>
385
+ ```
386
+
387
+ ## Finite-segment p-adic Hensel codes
388
+
389
+ ### Description
390
+ The finite-segment p-adic Hensel code is a p-adic integer that can be seen as a polynomial of degree `r - 1` in non-standard form (increasing degree order). Each coefficient of such polynomials are called *p-adic digits* ranging from `0` to `p - 1`. Computations on p-adic digits reduced modulo `p` must take the *carry* into consideration so we can guarantee that the results of addition, subtraction, multiplication, and division also range from `0` to `p - 1`.
391
+
392
+ ### Unique Benefits
393
+ The finite-segment p-adic Hensel code takes advantage of the finite-segmenet p-adic number system in which we can compute all four basic arithemtic operations (and consequently, any function) without requiring a substantial expansion in space for each individual computation. In fact, as mentioned in the section on Polynomials, given a prime `p` of bit length `b`, all unitary computations will take at most `1 + 2b` bits.
394
+
395
+ In Ruby, as is several other scripting languages, we can work with arbitrarily large integers and therefore the truncated p-adic Hensel code can be a good choice for representing large rational numbers. However, Ruby can run in a variety of systems, some of which will have limited resources, such as many instances in the IoT world. Additionally, a Ruby application can be one amongts many other components that together compose a larger application. Some of these other components might run in systems with integers limited to small bit lengths, say, 16. This is where finite-segment p-adic Hensel codes can be intrumental by allowing arbitrarily large p-adic expansions with coefficients bounded to a small prime.
396
+
397
+ ### Usage
398
+
399
+ Let `p = 359`, `r = 3`, and `rat = Rational(2,3)`:
400
+
401
+ ```ruby
402
+ h1 = HenselCode::FinitePadicExpansion.new p, r, rat
403
+ # => <HenselCode: 240 + 119p + 239p^2>
404
+ puts h1
405
+ # => 240 + 119p + 239p^2
406
+ ```
407
+
408
+ 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`:
409
+
410
+ ```ruby
411
+ h2 = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat
412
+ # => <HenselCode: 240>
413
+ ```
414
+
415
+ 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
416
+
417
+ ```ruby
418
+ HenselCode::FinitePadicExpansion.new p, 1, rat
419
+ # => <HenselCode: 240>
420
+ ```
421
+
422
+ The following expressions are equivalent (they represent the same quantity):
423
+
424
+ ```ruby
425
+ r = 3
426
+ h1 = HenselCode::FinitePadicExpansion.new p, r, rat
427
+ # => <HenselCode: 240 + 119p + 239p^2>
428
+ h1.to_r
429
+ # => (2/3)
430
+ h2 = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat
431
+ # => <HenselCode: 30845520>
432
+ h2.to_r
433
+ # => (2/3)
434
+ ```
435
+
436
+ that is, they not only represent the same rational but also `30845520` is just the evaluation of the polynomial `240 + 119p + 239p^2`.
437
+
438
+ We can obtain just the coefficients of `h1` as follows:
439
+
440
+ ```ruby
441
+ h1.to_a
442
+ # => => [240, 119, 239]
443
+ ```
444
+
445
+ We can obtain the truncated version of `h1` as follows:
446
+
447
+ ```ruby
448
+ h1.to_truncated
449
+ # => <HenselCode: 30845520>
450
+ h1.to_truncated.class
451
+ # => HenselCode::TruncatedFinitePadicExpansion
452
+ ```
453
+
454
+ #### Arithmetic
455
+
456
+ Let `p = 409`, `r = 5`, `rat1 = Rational(2,3)` and `rat2 = Rational(11,7)` such that
457
+
458
+ ```ruby
459
+ h1 = HenselCode::FinitePadicExpansion.new p, r, rat1
460
+ # => <HenselCode: 137 + 136p + 136p^2 + 136p^3 + 136p^4>
461
+ h2 = HenselCode::FinitePadicExpansion.new p, r, rat2
462
+ # => <HenselCode: 60 + 292p + 233p^2 + 350p^3 + 116p^4>
463
+ ```
464
+
465
+ We compute addition, subtraction, multiplication, and division as follows:
466
+
467
+ ```ruby
468
+ h1_plus_h2 = h1 + h2
469
+ # => <HenselCode: 197 + 19p + 370p^2 + 77p^3 + 253p^4>
470
+ h1_minus_h2 = h1 - h2
471
+ # => <HenselCode: 77 + 253p + 311p^2 + 194p^3 + 19p^4>
472
+ h1_times_h2 = h1 * h2
473
+ # => <HenselCode: 40 + 331p + 155p^2 + 97p^3 + 214p^4>
474
+ h1_div_h2 = h1 / h2
475
+ # => <HenselCode: 50 + 161p + 12p^2 + 347p^3 + 309p^4>
476
+ h2.inverse
477
+ # => <HenselCode: 75 + 37p + 223p^2 + 111p^3 + 260p^4>
478
+ h1 * h2.inverse
479
+ # => <HenselCode: 50 + 161p + 12p^2 + 347p^3 + 309p^4>
480
+ h2 * h2.inverse
481
+ # => <HenselCode: 1 + 0p + 0p^2 + 0p^3 + 0p^4>
482
+ ```
483
+
484
+ And we can verify that
485
+
486
+ ```ruby
487
+ h1_plus_h2.to_r
488
+ # => (47/21)
489
+ rat1 + rat2
490
+ # => (47/21)
491
+ rat1 - rat2
492
+ # => (-19/21)
493
+ h1_minus_h2.to_r
494
+ # => (-19/21)
495
+ h1_times_h2.to_r
496
+ # => (22/21)
497
+ rat1 * rat2
498
+ # => (22/21)
499
+ h1_div_h2.to_r
500
+ # => (14/33)
501
+ rat1 / rat2
502
+ # => (14/33)
503
+ ```
504
+
505
+ ## Truncated finite-segment g-adic Hensel codes
506
+ ### Description
507
+ Kurt Mahler refers to p-adic numbers based on multiple distinct primes as `g-adic numbers` (Lectures on Diophantine Approximations, 1961 and Introduction to p-adic Numbers and Their Functions, 1973). John F. Morrison, 1988, remarks that `g` is the product of `k` distinct primes, which are used to generate a g-adic number with a unique g-adic representation.
508
+
509
+ ### Unique Benefits
510
+
511
+ Since each digit of a truncated finite-segment g-adic Expansion (or simply truncated g-adic Hensel codes) is independently computed with respect to their corresponding prime, we can carry computations on each digit also independetly. This makes the truncated g-adic Hensel codes ideal for parallel/distributed processing, that is, given a rational number, several g-adic digits of that rational are independently computed and computations can be carried on those digits, also indpendently.
512
+
513
+ ### Usage
514
+
515
+ Let `primes = [241, 251, 257]`, `r = 3`, `rat1 = Rational(2,3)`, and `rat2 = Rational(5,4)`:
516
+
517
+ ```ruby
518
+ h1 = HenselCode::TruncatedFiniteGadicExpansion.new primes, r, rat1
519
+ # => <HenselCode: [4665841, 10542168, 11316396]>
520
+ h2 = HenselCode::TruncatedFiniteGadicExpansion.new primes, r, rat2
521
+ # => <HenselCode: [10498142, 3953314, 12730946]>
522
+ h1.primes
523
+ # => [241, 251, 257]
524
+ h1.exponent
525
+ # => 3
526
+ h1.g
527
+ # => 15546187
528
+ h1.n
529
+ # => 43343186168
530
+ h1.hensel_code
531
+ # => [<HenselCode: 4665841>, <HenselCode: 10542168>, <HenselCode: 11316396>]
532
+ h1.to_r
533
+ # => (2/3)
534
+ h2.to_r
535
+ # => (5/4)
536
+ ```
537
+
538
+ ### Arithmetic
539
+
540
+ We compute addition, subtraction, multiplication, and division as follows:
541
+
542
+ ```ruby
543
+ h1_plus_h2 = h1 + h2
544
+ # => <HenselCode: [1166462, 14495482, 7072749]>
545
+ h1_minus_h2 = h1 - h2
546
+ # => <HenselCode: [8165220, 6588854, 15560043]>
547
+ h1_times_h2 = h1 * h2
548
+ # => <HenselCode: [2332921, 13177710, 14145495]>
549
+ h1_div_h2 = h1 / h2
550
+ # => <HenselCode: [6532177, 2108434, 15842954]>
551
+ h2.inverse
552
+ # => <HenselCode: [2799505, 3162651, 6789838]>
553
+ h1 * h2.inverse
554
+ # => <HenselCode: [6532177, 2108434, 15842954]>
555
+ h2 * h2.inverse
556
+ # => <HenselCode: [1, 1, 1]>
557
+ ```
558
+
559
+ And we can verify that
560
+
561
+ ```ruby
562
+ h1_plus_h2.to_r
563
+ # => (23/12)
564
+ rat1 + rat2
565
+ # => (23/12)
566
+ rat1 - rat2
567
+ # => (-7/12)
568
+ h1_minus_h2.to_r
569
+ # => (-7/12)
570
+ h1_times_h2.to_r
571
+ # => (5/6)
572
+ rat1 * rat2
573
+ # => (5/6)
574
+ h1_div_h2.to_r
575
+ # => (8/15)
576
+ rat1 / rat2
577
+ # => (8/15)
578
+ ```
579
+
580
+ ### Relatable, and yet, Unique
581
+
582
+ When we execute the following:
583
+
584
+ ```ruby
585
+ h1.hensel_code
586
+ # => [<HenselCode: 4665841>, <HenselCode: 10542168>, <HenselCode: 11316396>]
587
+ ```
588
+
589
+ it is clear that the truncated g-adic Hensel code is a collection of individual Hensel codes, each one computed for the same rational but with distinct primes. One can be tempted to think that these multiple independent Hensel codes are "extra" material, not really needed for representing the given rational. This is far from the truth. Besides enabling parallel/distributed computaitons over Hensel codes (which is already a great benefit to have), to illustrate another aspect of working with truncated g-adic Hensel codes, consider the rational `rat3 = Rational(37897,52234)`:
590
+
591
+ ```ruby
592
+ h3 = HenselCode::TruncatedFiniteGadicExpansion.new primes, r, rat3
593
+ # => <HenselCode: [3698890, 5577355, 7406440]>
594
+ h3.hensel_code
595
+ # => [<HenselCode: 3698890>, <HenselCode: 5577355>, <HenselCode: 7406440>]
596
+ ```
597
+
598
+ We can clearly see that each Hensel code in the truncated g-adic Hensel code is only a partial representation of `rat3` when we decode each individual Hensel code to see which rational they are representing:
599
+
600
+ ```ruby
601
+ h3.hensel_code.map(&:to_r)
602
+ # => [(1471/4613), (409/981), (-207/3298)]
603
+ ```
604
+
605
+ None of the above rationals equals `rat3`. The reason is that each individual prime in `primes` are insufficient for representing `rat3` and therefore they can only, individually, partially represent `rat3`. However, when considered as part of the same g-adic number system, they can jointly represent much larger rationals, yet independently:
606
+
607
+ ```ruby
608
+ h3.to_r
609
+ => (37897/52234)
610
+ ```
611
+
612
+ Therefore, even without increasing the size of each individual prime, we can homomorphically represent very large rationals by considering more primes in the g-adic system:
613
+
614
+ ```ruby
615
+ rat4 = Rational(84245698732457344123,198437243845987593234524
616
+ primes = [349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409]
617
+ h4 = HenselCode::TruncatedFiniteGadicExpansion.new primes, r, rat4
618
+ # => HenselCode: [16442637, 10524943, 2432723, 10742241, 37389750, 10164016, 7690494, 32341841, 26459590, 50463786, 28362831]>
619
+ h4.to_r
620
+ # => (84245698732457344123/198437243845987593234524)
621
+ ```
622
+
623
+ ### Class Aliases
624
+
625
+ Since some classes can have long names, here are some aliases that can be used for keeping the lines of code shorter:
172
626
 
173
- Since `HenselCode::TruncatedFinitePadicExpansion` is a bit long, the alias `HenselCode::TFPE` can be used instead.
627
+ - `HenselCode::TruncatedFinitePadicExpansion` => `HenselCode::TFPE`
628
+ - `HenselCode::HenselCodesWithDifferentPrimesAndExponents` => `HenselCode::HCWDPAE`
629
+ - `HenselCode::WrongHenselCodeInputType` => `HenselCode::WHIT`
174
630
 
175
631
  ## Coming Soon
176
632
 
@@ -188,4 +644,4 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/davidw
188
644
 
189
645
  ## License
190
646
 
191
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
647
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).