hensel_code 0.2.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -2,12 +2,24 @@
2
2
 
3
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
+ ***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 and as many interesting features as time allows. 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.
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
 
7
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.
8
10
 
9
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.
10
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
+
17
+ ## History
18
+
19
+ The theory of p-adic numbers was introduced by Kurt Hensel in the early 1900's ([Theorie der algebraischen Zahlen](https://books.google.com/books?hl=en&lr=&id=0w3vAAAAMAAJ&oi=fnd&pg=PR3&dq=Theorie+der+algebraischen+Zahlen&ots=kQfsAd0GYZ&sig=dGsTggln9njYkc2zNYT5hkk2lXU#v=onepage&q=Theorie%20der%20algebraischen%20Zahlen&f=false)). Introductions to p-adic numbers are provided by George Bachman (Introduction to p-Adic Numbers and Valuation Theory), Neal Koblitz ([P-Adic Numbers, P-Adic Analysis, and Zeta Functions](https://books.google.com/books?hl=en&lr=&id=8sTgBwAAQBAJ&oi=fnd&pg=PA1&dq=P-Adic+Numbers,+P-Adic+Analysis,+and+Zeta+Functions&ots=fWIImSqW7-&sig=29LdWtpjSkmQ2kWvVFDmmG5SsOo#v=onepage&q=P-Adic%20Numbers%2C%20P-Adic%20Analysis%2C%20and%20Zeta%20Functions&f=false)), Kurt Mahler ([Introduction to p-adic numbers and their functions](https://books.google.com/books?hl=en&lr=&id=kbc8AAAAIAAJ&oi=fnd&pg=PA1&dq=Introduction+to+P-Adic+Numbers+and+Their+Functions&ots=GFpDeD8vMG&sig=TNSPVG0YA676rlflM9CfogQP7t8)), and Fernando Gouveia ([p-adic Number](https://books.google.com/books?hl=en&lr=&id=VWjsDwAAQBAJ&oi=fnd&pg=PR5&ots=MdgpeNTWLX&sig=9LzgTUkSzN76E1EOD7wna0c8S0I#v=onepage&q&f=false)).
20
+
21
+ The foundation of the particular application of finite-segement p-adic arithemetic (also known as Hensel codes) for error-free computation can be found in the works of Alparslan, Krishnamurthy, Rao, and Subramanian (Finite p-adic number systems with possible applications, [Finite segmentp-adic number systems with applications to exact computation](https://link.springer.com/article/10.1007/BF03051174), [p-Adic arithmetic procedures for exact matrix computations](https://link.springer.com/article/10.1007/BF03046725), [Error-Free Polynomial Matrix Computations](https://link.springer.com/book/10.1007/978-1-4612-5118-7)), Gregory ([Methods and Applications of Error-Free Computation](https://link.springer.com/book/10.1007/978-1-4612-5242-9), [Error-free computation with rational numbers](https://link.springer.com/article/10.1007/BF01933164), [Error-free computation with finite number systems](https://dl.acm.org/doi/abs/10.1145/1057502.1057503?casa_token=LTLotJJYEPAAAAAA:PQwNY8-RcpuSQyfCkEMv1xIpd10RlR-y7JeTWkCkYNQ3c1IroGEGzk4TVH_5JJ954sJsvcHRlTldtQ), [The use of finite-segmentp-adic arithmetic for exact computation](https://link.springer.com/article/10.1007/BF01930898)), Miola ([Algebraic approach to p-adic conversion of rational numbers](https://www.sciencedirect.com/science/article/abs/pii/002001908490022X)), Morrison ([Parallel p-adic computation](https://www.sciencedirect.com/science/article/abs/pii/0020019088901597)). Many algorithms, ideas, and concepts in Hensel codes are greatly benefitted by the remarkable series The Art of Computer Programming by Donald Knuth.
22
+
11
23
  ## Mathematical Background
12
24
 
13
25
  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.
@@ -32,22 +44,44 @@ Or install it yourself as:
32
44
 
33
45
  $ gem install hensel_code
34
46
 
35
- # Usage
47
+ # HenselCode
48
+
49
+ 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:
36
50
 
37
- There are several types of Hensel codes. There are currently two of the available in the gem HenselCode:
51
+ 1. Truncated finite-segment p-adic Hensel codes
52
+ 2. Finite-segment p-adic Hensel codes
53
+ 3. Truncated finite-segment g-adic Hensel codes
54
+ 4. Finite-segmenet g-adic Hensel codes
38
55
 
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)
56
+ 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.
41
57
 
42
58
  ## Truncated finite-segment p-adic Hensel codes
43
59
 
60
+ ### Description
61
+
62
+ 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.
63
+
64
+ ### Unique Benefits
65
+
66
+ 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.
67
+
68
+ ### Usage
69
+
44
70
  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:
45
71
 
46
72
  ```ruby
47
73
  h1 = HenselCode::TruncatedFinitePadicExpansion.new(p, r, rat1)
48
- # => [HenselCode: 13579675, prime: 257, exponent: 3, modulus: 16974593]
74
+ # => <HenselCode: 13579675>
49
75
  h2 = HenselCode::TruncatedFinitePadicExpansion.new(p, r, rat1)
50
- # => [HenselCode: 5658199, prime: 257, exponent: 3, modulus: 16974593]
76
+ # => <HenselCode: 5658199>
77
+ h1.n
78
+ # => 2913
79
+ h1.prime
80
+ # => 257
81
+ h1.exponent
82
+ # => 3
83
+ h1.modulus
84
+ # => 16974593
51
85
  h1.class
52
86
  # => HenselCode::TruncatedFinitePadicExpansion
53
87
  h2.class
@@ -62,9 +96,19 @@ Now we can carry arithmetic computations on the `h1` and `h2` objects as if we w
62
96
 
63
97
  ```ruby
64
98
  h1_plus_h2 = h1 + h2
99
+ # => <HenselCode: 2263281>
65
100
  h1_minus_h2 = h1 - h2
101
+ # => <HenselCode: 7921476>
66
102
  h1_times_h2 = h1 * h2
103
+ # => <HenselCode: 6789838>
67
104
  h1_div_h2 = h1 / h2
105
+ # => <HenselCode: 5941108>
106
+ h2.inverse
107
+ # => <HenselCode: 4243649>
108
+ h1 * h2.inverse
109
+ # => <HenselCode: 5941108>
110
+ h2 * h2.inverse
111
+ # => <HenselCode: 1>
68
112
  ```
69
113
 
70
114
  All the computations are reduced modulo `p^r`.
@@ -106,7 +150,7 @@ To help you in this decision, given any Hensel code object `h`, you can check `h
106
150
 
107
151
  ```ruby
108
152
  h = HenselCode::TruncatedFinitePadicExpansion.new p, r, Rational(2,3)
109
- # => [HenselCode: 11316396, prime: 257, exponent: 3, modulus: 16974593]
153
+ # => <HenselCode: 11316396>
110
154
  h.n
111
155
  # => 2913
112
156
  ```
@@ -120,7 +164,7 @@ If `p = 7` and `r = 2`, then `n = 4`. If I try to encode a rational number `rat
120
164
  ```ruby
121
165
  rat = Rational(11,23)
122
166
  h = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat
123
- # => [HenselCode: 9, prime: 7, exponent: 2, modulus: 49]
167
+ # => <HenselCode: 9>
124
168
  h.to_r
125
169
  # => (-4/5)
126
170
  ```
@@ -131,11 +175,11 @@ The same occurs with computation on Hensel codes. If `rat1 = Rational(2,3)` and
131
175
 
132
176
  ```ruby
133
177
  h1 = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat1
134
- # => [HenselCode: 17, prime: 7, exponent: 2, modulus: 49]
178
+ # => <HenselCode: 17>
135
179
  h2 = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat2
136
- # => [HenselCode: 13, prime: 7, exponent: 2, modulus: 49]
180
+ # => <HenselCode: 13>
137
181
  h1_plus_h2 = h1 + h2
138
- # => [HenselCode: 30, prime: 7, exponent: 2, modulus: 49]
182
+ # => <HenselCode: 30>
139
183
  h1_plus_h2.to_r
140
184
  # => (3/5)
141
185
  ```
@@ -146,11 +190,11 @@ If instead we define `p = 56807` and `r = 3`, we have:
146
190
 
147
191
  ```ruby
148
192
  h1 = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat1
149
- => [HenselCode: 122212127593296, prime: 56807, exponent: 3, modulus: 183318191389943]
193
+ # => <HenselCode: 122212127593296>
150
194
  h2 = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat2
151
- => [HenselCode: 137488643542458, prime: 56807, exponent: 3, modulus: 183318191389943]
195
+ # => <HenselCode: 137488643542458>
152
196
  h1_plus_h2 = h1 + h2
153
- => [HenselCode: 76382579745811, prime: 56807, exponent: 3, modulus: 183318191389943]
197
+ # => <HenselCode: 76382579745811>
154
198
  h1_plus_h2.to_r
155
199
  # => (17/12)
156
200
  ```
@@ -170,7 +214,7 @@ p = 25
170
214
  r = 3
171
215
  rat1 = Rational(1,2)
172
216
  h1 = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat1
173
- # => [HenselCode: 7813, prime: 25, exponent: 3, modulus: 15625]
217
+ # => <HenselCode: 7813>
174
218
  h1.to_r
175
219
  # => (1/2)
176
220
  ```
@@ -192,13 +236,13 @@ In order to operate on two or more Hensel codes, they all must be of the same ob
192
236
 
193
237
  ```ruby
194
238
  h1 = HenselCode::TruncatedFinitePadicExpansion.new(p1, r1, rat1)
195
- # => [HenselCode: 5599009, prime: 241, exponent: 3, modulus: 13997521]
239
+ # => <HenselCode: 5599009>
196
240
  h2 = HenselCode::TruncatedFinitePadicExpansion.new(p1, r2, rat1)
197
- # => [HenselCode: 1349361025, prime: 241, exponent: 4, modulus: 3373402561]
241
+ # => <HenselCode: 1349361025>
198
242
  h3 = HenselCode::TruncatedFinitePadicExpansion.new(p2, r1, rat1)
199
- # => [HenselCode: 6325301, prime: 251, exponent: 3, modulus: 15813251]
243
+ # => <HenselCode: 6325301>
200
244
  h4 = HenselCode::TruncatedFinitePadicExpansion.new(p2, r2, rat1)
201
- # => [HenselCode: 1587650401, prime: 251, exponent: 4, modulus: 3969126001]
245
+ # => <HenselCode: 1587650401>
202
246
  ```
203
247
 
204
248
  The following operations will raise exceptions:
@@ -221,7 +265,7 @@ Let `p = 541`, `r = 3`, `rat = Rational(11,5)`. We create a Hensel code as befor
221
265
 
222
266
  ```ruby
223
267
  h = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat
224
- # => [HenselCode: 126672339, prime: 541, exponent: 3, modulus: 158340421]
268
+ # => <HenselCode: 126672339>
225
269
  ```
226
270
 
227
271
  We can change the prime and the exponent:
@@ -230,9 +274,9 @@ We can change the prime and the exponent:
230
274
  p = 1223
231
275
  r = 4
232
276
  h.replace_prime(p)
233
- # => [HenselCode: 731710629, prime: 1223, exponent: 3, modulus: 1829276567]
277
+ # => <HenselCode: 731710629>
234
278
  h.replace_exponent(r)
235
- # => [HenselCode: 1789764193155, prime: 1223, exponent: 4, modulus: 2237205241441]
279
+ # => <HenselCode: 1789764193155>
236
280
  ```
237
281
 
238
282
  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:
@@ -247,7 +291,7 @@ We can also change the rational number:
247
291
  ```ruby
248
292
  rat = Rational(13,7)
249
293
  h.replace_rational(rat)
250
- # => [HenselCode: 1278402995111, prime: 1223, exponent: 4, modulus: 2237205241441]
294
+ # => <HenselCode: 1278402995111>
251
295
  h.to_r
252
296
  # => (13/7)
253
297
  ```
@@ -256,6 +300,7 @@ We can initiate a Hensel code object with its Hensel code value, instead of a ra
256
300
 
257
301
  ```ruby
258
302
  h = HenselCode::TruncatedFinitePadicExpansion.new p, r, 53673296543
303
+ # => <HenselCode: 53673296543>
259
304
  ```
260
305
 
261
306
  and then we can check what is the rational number represented by the resulting object:
@@ -269,18 +314,100 @@ We can update the Hensel code value of an existing Hensel code object:
269
314
 
270
315
  ```ruby
271
316
  h.replace_hensel_code(38769823656)
272
- # => (-685859/94809)
317
+ # => <HenselCode: 38769823656>
318
+ ```
319
+
320
+ ## Polynomials
321
+
322
+ 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`.
323
+
324
+ Let `p = 257`. We intantiate polynomials as follows:
325
+
326
+ ```ruby
327
+ f = HenselCode::Polynomial.new p, [29, 102, 232]
328
+ # => <Polynomial: 29 + 102p + 232p^2>
329
+ g = HenselCode::Polynomial.new p, [195, 83, 244]
330
+ # => <Polynomial: 195 + 83p + 244p^2>
331
+ f.prime
332
+ # => 257
333
+ f.coefficients
334
+ # => [29, 102, 232]
335
+ f.degree
336
+ # => 2
337
+ puts f
338
+ # => 29 + 102p + 232p^2
339
+ ```
340
+
341
+ ### Arithmetic
342
+
343
+ 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.
344
+
345
+ ```ruby
346
+ f + g
347
+ # => <Polynomial: 224 + 185p + 219p^2>
348
+ f - g
349
+ # => <Polynomial: 91 + 18p + 245p^2>
350
+ f * g
351
+ # => <Polynomial: 1 + 217p + 216p^2>
352
+ f / g
353
+ # => <Polynomial: 70 + 238p + 233p^2>
354
+ g.inverse
355
+ # => <Polynomial: 29 + 234p + 219p^2
356
+ f * g.inverse
357
+ # => <Polynomial: 70 + 238p + 233p^2>
358
+ g * g.inverse
359
+ # => <Polynomial: 1 + 0p + 0p^2>
360
+ ```
361
+
362
+ ### Constraints
363
+
364
+ Operations with fixed-length polynomials require operands with the same degree:
365
+
366
+ ```ruby
367
+ f = HenselCode::Polynomial.new p, [29, 102, 232]
368
+ # => <Polynomial: 29 + 102p + 232p^2>
369
+ g = HenselCode::Polynomial.new p, [195, 83, 244, 99]
370
+ # => <Polynomial: 195 + 83p + 244p^2 + 99p^3>
371
+ f.degree
372
+ # => 2
373
+ g.degree
374
+ # => 3
375
+ f + g
376
+ # => polynomials must have same degree (HenselCode::WrongHenselCodeInputType)
377
+ ```
378
+
379
+ Operations with fixed-length polynomials also require operands with the same prime:
380
+
381
+ ```ruby
382
+ f = HenselCode::Polynomial.new 251, [133, 206, 58]
383
+ # => <Polynomial: 133 + 206p + 58p^2>
384
+ g = HenselCode::Polynomial.new 257, [105, 129, 238]
385
+ # => <Polynomial: 105 + 129p + 238p^2>
386
+ f + g
387
+ # => polynomials must have same prime (HenselCode::WrongHenselCodeInputType)
388
+ g.prime = 251
389
+ # => 251
390
+ f + g
391
+ # => <Polynomial: 238 + 84p + 46p^2>
273
392
  ```
274
393
 
275
394
  ## Finite-segment p-adic Hensel codes
276
395
 
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`.
396
+ ### Description
397
+ 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`.
398
+
399
+ ### Unique Benefits
400
+ 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.
401
+
402
+ 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.
403
+
404
+ ### Usage
278
405
 
279
406
  Let `p = 359`, `r = 3`, and `rat = Rational(2,3)`:
280
407
 
281
408
  ```ruby
282
409
  h1 = HenselCode::FinitePadicExpansion.new p, r, rat
283
- # => [HenselCode: 240 + 119p + 239p^2, prime: 359, exponent: 3, modulus: 359]
410
+ # => <HenselCode: 240 + 119p + 239p^2>
284
411
  puts h1
285
412
  # => 240 + 119p + 239p^2
286
413
  ```
@@ -289,26 +416,26 @@ We say that `h` is a p-adic number with `r` digits. We clearly see the correspon
289
416
 
290
417
  ```ruby
291
418
  h2 = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat
292
- # => [HenselCode: 240, prime: 359, exponent: 1, modulus: 359]
419
+ # => <HenselCode: 240>
293
420
  ```
294
421
 
295
422
  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
423
 
297
424
  ```ruby
298
425
  HenselCode::FinitePadicExpansion.new p, 1, rat
299
- # => [HenselCode: 240, prime: 359, exponent: 1, modulus: 359]
426
+ # => <HenselCode: 240>
300
427
  ```
301
428
 
302
- The following expressions are equivalent:
429
+ The following expressions are equivalent (they represent the same quantity):
303
430
 
304
431
  ```ruby
305
432
  r = 3
306
433
  h1 = HenselCode::FinitePadicExpansion.new p, r, rat
307
- # => [HenselCode: 240 + 119p + 239p^2, prime: 359, exponent: 3, modulus: 359]
434
+ # => <HenselCode: 240 + 119p + 239p^2>
308
435
  h1.to_r
309
436
  # => (2/3)
310
437
  h2 = HenselCode::TruncatedFinitePadicExpansion.new p, r, rat
311
- # => [HenselCode: 30845520, prime: 359, exponent: 3, modulus: 46268279]
438
+ # => <HenselCode: 30845520>
312
439
  h2.to_r
313
440
  # => (2/3)
314
441
  ```
@@ -326,7 +453,7 @@ We can obtain the truncated version of `h1` as follows:
326
453
 
327
454
  ```ruby
328
455
  h1.to_truncated
329
- # => [HenselCode: 30845520, prime: 359, exponent: 3, modulus: 46268279]
456
+ # => <HenselCode: 30845520>
330
457
  h1.to_truncated.class
331
458
  # => HenselCode::TruncatedFinitePadicExpansion
332
459
  ```
@@ -337,22 +464,28 @@ Let `p = 409`, `r = 5`, `rat1 = Rational(2,3)` and `rat2 = Rational(11,7)` such
337
464
 
338
465
  ```ruby
339
466
  h1 = HenselCode::FinitePadicExpansion.new p, r, rat1
340
- # => [HenselCode: 137 + 136p + 136p^2 + 136p^3 + 136p^4, prime: 409, exponent: 5, modulus: 409]
467
+ # => <HenselCode: 137 + 136p + 136p^2 + 136p^3 + 136p^4>
341
468
  h2 = HenselCode::FinitePadicExpansion.new p, r, rat2
342
- # => [HenselCode: 60 + 292p + 233p^2 + 350p^3 + 116p^4, prime: 409, exponent: 5, modulus: 409]
469
+ # => <HenselCode: 60 + 292p + 233p^2 + 350p^3 + 116p^4>
343
470
  ```
344
471
 
345
472
  We compute addition, subtraction, multiplication, and division as follows:
346
473
 
347
474
  ```ruby
348
- h1_plus_h2 =h1 + h2
349
- # => [HenselCode: 197 + 19p + 370p^2 + 77p^3 + 253p^4, prime: 409, exponent: 5, modulus: 409]
475
+ h1_plus_h2 = h1 + h2
476
+ # => <HenselCode: 197 + 19p + 370p^2 + 77p^3 + 253p^4>
350
477
  h1_minus_h2 = h1 - h2
351
- # => [HenselCode: 77 + 253p + 311p^2 + 194p^3 + 19p^4, prime: 409, exponent: 5, modulus: 409]
478
+ # => <HenselCode: 77 + 253p + 311p^2 + 194p^3 + 19p^4>
352
479
  h1_times_h2 = h1 * h2
353
- # => [HenselCode: 40 + 331p + 155p^2 + 97p^3 + 214p^4, prime: 409, exponent: 5, modulus: 409]
480
+ # => <HenselCode: 40 + 331p + 155p^2 + 97p^3 + 214p^4>
354
481
  h1_div_h2 = h1 / h2
355
- # => [HenselCode: 50 + 161p + 12p^2 + 347p^3 + 309p^4, prime: 409, exponent: 5, modulus: 409]
482
+ # => <HenselCode: 50 + 161p + 12p^2 + 347p^3 + 309p^4>
483
+ h2.inverse
484
+ # => <HenselCode: 75 + 37p + 223p^2 + 111p^3 + 260p^4>
485
+ h1 * h2.inverse
486
+ # => <HenselCode: 50 + 161p + 12p^2 + 347p^3 + 309p^4>
487
+ h2 * h2.inverse
488
+ # => <HenselCode: 1 + 0p + 0p^2 + 0p^3 + 0p^4>
356
489
  ```
357
490
 
358
491
  And we can verify that
@@ -376,9 +509,199 @@ rat1 / rat2
376
509
  # => (14/33)
377
510
  ```
378
511
 
379
- ### Class Alias
512
+ ## Truncated finite-segment g-adic Hensel codes
513
+ ### Description
514
+ 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.
515
+
516
+ ### Unique Benefits
517
+
518
+ 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.
519
+
520
+ ### Usage
521
+
522
+ Let `primes = [241, 251, 257]`, `r = 3`, `rat1 = Rational(2,3)`, and `rat2 = Rational(5,4)`:
523
+
524
+ ```ruby
525
+ h1 = HenselCode::TruncatedFiniteGadicExpansion.new primes, r, rat1
526
+ # => <HenselCode: [4665841, 10542168, 11316396]>
527
+ h2 = HenselCode::TruncatedFiniteGadicExpansion.new primes, r, rat2
528
+ # => <HenselCode: [10498142, 3953314, 12730946]>
529
+ h1.primes
530
+ # => [241, 251, 257]
531
+ h1.exponent
532
+ # => 3
533
+ h1.g
534
+ # => 15546187
535
+ h1.n
536
+ # => 43343186168
537
+ h1.hensel_code
538
+ # => [<HenselCode: 4665841>, <HenselCode: 10542168>, <HenselCode: 11316396>]
539
+ h1.to_r
540
+ # => (2/3)
541
+ h2.to_r
542
+ # => (5/4)
543
+ ```
544
+
545
+ ### Arithmetic
546
+
547
+ We compute addition, subtraction, multiplication, and division as follows:
548
+
549
+ ```ruby
550
+ h1_plus_h2 = h1 + h2
551
+ # => <HenselCode: [1166462, 14495482, 7072749]>
552
+ h1_minus_h2 = h1 - h2
553
+ # => <HenselCode: [8165220, 6588854, 15560043]>
554
+ h1_times_h2 = h1 * h2
555
+ # => <HenselCode: [2332921, 13177710, 14145495]>
556
+ h1_div_h2 = h1 / h2
557
+ # => <HenselCode: [6532177, 2108434, 15842954]>
558
+ h2.inverse
559
+ # => <HenselCode: [2799505, 3162651, 6789838]>
560
+ h1 * h2.inverse
561
+ # => <HenselCode: [6532177, 2108434, 15842954]>
562
+ h2 * h2.inverse
563
+ # => <HenselCode: [1, 1, 1]>
564
+ ```
565
+
566
+ And we can verify that
567
+
568
+ ```ruby
569
+ h1_plus_h2.to_r
570
+ # => (23/12)
571
+ rat1 + rat2
572
+ # => (23/12)
573
+ rat1 - rat2
574
+ # => (-7/12)
575
+ h1_minus_h2.to_r
576
+ # => (-7/12)
577
+ h1_times_h2.to_r
578
+ # => (5/6)
579
+ rat1 * rat2
580
+ # => (5/6)
581
+ h1_div_h2.to_r
582
+ # => (8/15)
583
+ rat1 / rat2
584
+ # => (8/15)
585
+ ```
586
+
587
+ ### Relatable, and yet, Unique
588
+
589
+ When we execute the following:
590
+
591
+ ```ruby
592
+ h1.hensel_code
593
+ # => [<HenselCode: 4665841>, <HenselCode: 10542168>, <HenselCode: 11316396>]
594
+ ```
595
+
596
+ 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)`:
597
+
598
+ ```ruby
599
+ h3 = HenselCode::TruncatedFiniteGadicExpansion.new primes, r, rat3
600
+ # => <HenselCode: [3698890, 5577355, 7406440]>
601
+ h3.hensel_code
602
+ # => [<HenselCode: 3698890>, <HenselCode: 5577355>, <HenselCode: 7406440>]
603
+ ```
604
+
605
+ 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:
606
+
607
+ ```ruby
608
+ h3.hensel_code.map(&:to_r)
609
+ # => [(1471/4613), (409/981), (-207/3298)]
610
+ ```
611
+
612
+ 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:
613
+
614
+ ```ruby
615
+ h3.to_r
616
+ => (37897/52234)
617
+ ```
618
+
619
+ 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:
620
+
621
+ ```ruby
622
+ rat4 = Rational(84245698732457344123,198437243845987593234524
623
+ primes = [349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409]
624
+ h4 = HenselCode::TruncatedFiniteGadicExpansion.new primes, r, rat4
625
+ # => HenselCode: [16442637, 10524943, 2432723, 10742241, 37389750, 10164016, 7690494, 32341841, 26459590, 50463786, 28362831]>
626
+ h4.to_r
627
+ # => (84245698732457344123/198437243845987593234524)
628
+ ```
629
+
630
+ ## Finite-segment g-adic Hensel codes
631
+
632
+ ### Description
633
+ The finite-segment g-adic Hensel codes are analogous to the relationship between truncated finite p-adic expansions and finite p-adic expansions. With the finite-segement g-adic Hensel codes, we have a collection of fixed-degree univariate polynomials for each one of `k` distinct primes used to compute a g-adic Hensel code.
634
+
635
+ ### Unique Benefits
636
+
637
+ Finite-segement g-adic Hensel codes combine the best of two worlds: multiple independent Hensel codes which can be computed in parallel / in a distributed manner and computations using fixed-degree polynomials where for each `b`-bit prime `p_i`, the maximum expansion of all computations will take at most `1 + 2b` bits. Combining parallel/distributed computation with minimal computation expansion can be beneficial for a number of applications including massive parallel computations and egde computing.
638
+
639
+ ### Usage
640
+ ```ruby
641
+ primes = [241, 251, 257]
642
+ r = 3
643
+ rat1 = Rational(2,3)
644
+ rat2 = Rational(5,9)
645
+ h1 = HenselCode::FiniteGadicExpansion.new primes, r, rat1
646
+ # => <HenselCode: ["81 + 80p + 80p^2", "168 + 83p + 167p^2", "172 + 85p + 171p^2"]>
647
+ h2 = HenselCode::FiniteGadicExpansion.new primes, r, rat2
648
+ # => <HenselCode: ["188 + 26p + 107p^2", "140 + 111p + 139p^2", "229 + 199p + 142p^2"]>
649
+ h1.to_r
650
+ # => (2/3)
651
+ h2.to_r
652
+ # => (5/9)
653
+ h1.to_a
654
+ # => [[81, 80, 80], [168, 83, 167], [172, 85, 171]]
655
+ h2.to_a
656
+ # => [[188, 26, 107], [140, 111, 139], [229, 199, 142]]
657
+ ```
658
+
659
+ ### Arithmetic
660
+ ```ruby
661
+ h1_plus_h2 = h1 + h2
662
+ # => <HenselCode: ["28 + 107p + 187p^2", "57 + 195p + 55p^2", "144 + 28p + 57p^2"]>
663
+ h1_minus_h2 = h1 - h2
664
+ # => <HenselCode: ["134 + 53p + 214p^2", "28 + 223p + 27p^2", "200 + 142p + 28p^2"]>
665
+ h1_times_h2 = h1 * h2
666
+ # => <HenselCode: ["45 + 98p + 71p^2", "177 + 241p + 92p^2", "67 + 133p + 9p^2"]>
667
+ h1_div_h2 = h1 / h2
668
+ # => <HenselCode: ["194 + 192p + 192p^2", "202 + 200p + 200p^2", "104 + 51p + 154p^2"]>
669
+ h2.inverse
670
+ # => <HenselCode: ["50 + 48p + 48p^2", "52 + 50p + 50p^2", "156 + 205p + 102p^2"]>
671
+ h1 * h2.inverse
672
+ # => <HenselCode: ["194 + 192p + 192p^2", "202 + 200p + 200p^2", "104 + 51p + 154p^2"]>
673
+ h2 * h2.inverse
674
+ # => <HenselCode: ["1 + 0p + 0p^2", "1 + 0p + 0p^2", "1 + 0p + 0p^2"]>
675
+ ```
676
+
677
+ and we can check that
678
+
679
+ ```ruby
680
+ h1_plus_h2.to_r
681
+ # => (11/9)
682
+ rat1 + rat2
683
+ # => (11/9)
684
+ h1_minus_h2.to_r
685
+ # => (1/9)
686
+ rat1 - rat2
687
+ # => (1/9)
688
+ h1_times_h2.to_r
689
+ # => (10/27)
690
+ rat1 * rat2
691
+ # => (10/27)
692
+ h1_div_h2.to_r
693
+ # => (6/5)
694
+ rat1 / rat2
695
+ # => (6/5)
696
+ ```
697
+
698
+ ## Class Aliases
699
+
700
+ Since some classes can have long names, here are some aliases that can be used for keeping the lines of code shorter:
380
701
 
381
- Since `HenselCode::TruncatedFinitePadicExpansion` is a bit long, the alias `HenselCode::TFPE` can be used instead.
702
+ - `HenselCode::TruncatedFinitePadicExpansion` => `HenselCode::TFPE`
703
+ - `HenselCode::HenselCodesWithDifferentPrimesAndExponents` => `HenselCode::HCWDPAE`
704
+ - `HenselCode::WrongHenselCodeInputType` => `HenselCode::WHIT`
382
705
 
383
706
  ## Coming Soon
384
707
 
@@ -396,4 +719,4 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/davidw
396
719
 
397
720
  ## License
398
721
 
399
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
722
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/hensel_code/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "hensel_code"
7
+ spec.version = HenselCode::VERSION
8
+ spec.authors = ["David William Silva"]
9
+ spec.email = ["contact@davidwsilva.com"]
10
+
11
+ spec.summary = "Error-free computation with homomorphic encoding of rational numbers."
12
+ spec.description = "A Ruby library for error-free computation via homomorphic encoding of rational numbers as integers."
13
+ spec.homepage = "https://github.com/davidwilliam/hensel_code"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 2.6.0"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = "https://github.com/davidwilliam/hensel_code"
19
+ spec.metadata["changelog_uri"] = "https://github.com/davidwilliam/hensel_code/blob/main/CHANGELOG.md"
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
+ `git ls-files -z`.split("\x0").reject do |f|
25
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
26
+ end
27
+ end
28
+ spec.bindir = "exe"
29
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ["lib"]
31
+
32
+ # Uncomment to register a new dependency of your gem
33
+ spec.add_dependency "openssl", "~> 3.0.0"
34
+ spec.add_dependency "prime", "~> 0.1.2"
35
+
36
+ # For more information and examples about making a new gem, check out our
37
+ # guide at: https://bundler.io/guides/creating_gem.html
38
+ spec.metadata["rubygems_mfa_required"] = "true"
39
+ end