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 +4 -4
- data/.codecov.yml +6 -0
- data/CHANGELOG.md +26 -2
- data/Gemfile.lock +1 -1
- data/README.md +472 -16
- data/lib/hensel_code/finite_padic_expansion.rb +106 -0
- data/lib/hensel_code/gadic_base.rb +86 -0
- data/lib/hensel_code/gadic_verifier.rb +36 -0
- data/lib/hensel_code/modular_arithmetic.rb +75 -0
- data/lib/hensel_code/padic_base.rb +85 -0
- data/lib/hensel_code/{tfpe_verifier.rb → padic_verifier.rb} +1 -1
- data/lib/hensel_code/polynomial.rb +103 -0
- data/lib/hensel_code/tools.rb +24 -6
- data/lib/hensel_code/truncated_finite_gadic_expansion.rb +64 -0
- data/lib/hensel_code/truncated_finite_padic_expansion.rb +21 -77
- data/lib/hensel_code/version.rb +1 -1
- data/lib/hensel_code.rb +12 -3
- metadata +11 -3
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HenselCode
|
4
|
+
# finite p-adic expansion hensel code class
|
5
|
+
class FinitePadicExpansion < PAdicBase
|
6
|
+
attr_accessor :polynomial
|
7
|
+
|
8
|
+
def modulus
|
9
|
+
prime
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_a
|
13
|
+
hensel_code
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_truncated
|
17
|
+
TruncatedFinitePadicExpansion.new(prime, exponent, rational)
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
hensel_code.map.with_index do |h, i|
|
22
|
+
"#{h}#{polynomial_variable(i)}"
|
23
|
+
end.join(" + ")
|
24
|
+
end
|
25
|
+
|
26
|
+
def inspect
|
27
|
+
"<HenselCode: #{polynomial_form}>"
|
28
|
+
end
|
29
|
+
|
30
|
+
def inverse
|
31
|
+
new_hensel_code = polynomial.inverse.coefficients
|
32
|
+
self.class.new prime, exponent, new_hensel_code
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def polynomial_form
|
38
|
+
to_s
|
39
|
+
end
|
40
|
+
|
41
|
+
def evaluate(operation, other)
|
42
|
+
new_hensel_code = polynomial.send(operation, other.polynomial).coefficients
|
43
|
+
self.class.new prime, exponent, new_hensel_code
|
44
|
+
end
|
45
|
+
|
46
|
+
def valid_number?(number)
|
47
|
+
if number.is_a?(Rational)
|
48
|
+
@rational = number
|
49
|
+
elsif number.is_a?(Array) && number.map(&:class).uniq == [Integer] && number.size == exponent
|
50
|
+
@hensel_code = number
|
51
|
+
decode
|
52
|
+
else
|
53
|
+
message = "number must be a Rational or an\
|
54
|
+
Array of integers of size #{exponent}"
|
55
|
+
raise WrongHenselCodeInputType, message
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def valid_hensel_code?(new_hensel_code)
|
60
|
+
conditions = [
|
61
|
+
new_hensel_code.is_a?(Array),
|
62
|
+
new_hensel_code.map(&:class).uniq == [Integer],
|
63
|
+
new_hensel_code.size == exponent
|
64
|
+
]
|
65
|
+
message = "must be an array of integers of size #{exponent}"
|
66
|
+
raise WrongHenselCodeInputType, message unless conditions.uniq == [true]
|
67
|
+
end
|
68
|
+
|
69
|
+
def encode
|
70
|
+
@hensel_code = rational_to_padic_digits
|
71
|
+
@polynomial = Polynomial.new prime, hensel_code
|
72
|
+
end
|
73
|
+
|
74
|
+
def decode
|
75
|
+
number = 0
|
76
|
+
hensel_code.each_with_index { |d, i| number += d * (prime**i) }
|
77
|
+
@rational = TruncatedFinitePadicExpansion.new(prime, exponent, number).to_r
|
78
|
+
end
|
79
|
+
|
80
|
+
def rational_to_padic_digits
|
81
|
+
digits = [rational_to_integer(rational)]
|
82
|
+
alpha = rational - digits.last
|
83
|
+
(exponent - 1).times do
|
84
|
+
alpha = reduce_rational_in_terms_of_prime(alpha)
|
85
|
+
digits << rational_to_integer(alpha)
|
86
|
+
alpha -= digits.last
|
87
|
+
end
|
88
|
+
digits
|
89
|
+
end
|
90
|
+
|
91
|
+
def reduce_rational_in_terms_of_prime(alpha)
|
92
|
+
divisor_numerator = alpha.numerator.gcd(prime)
|
93
|
+
divisor_denominator = alpha.denominator.gcd(prime)
|
94
|
+
if divisor_numerator != 1
|
95
|
+
alpha /= divisor_numerator
|
96
|
+
elsif divisor_denominator != 1
|
97
|
+
alpha *= divisor_denominator
|
98
|
+
end
|
99
|
+
alpha
|
100
|
+
end
|
101
|
+
|
102
|
+
def rational_to_integer(rat)
|
103
|
+
(rat.numerator * mod_inverse(rat.denominator, prime)) % prime
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HenselCode
|
4
|
+
# base hensel code class
|
5
|
+
class GAdicBase
|
6
|
+
include Tools
|
7
|
+
include GAdicVerifier
|
8
|
+
|
9
|
+
attr_accessor :primes, :exponent, :rational, :hensel_code, :g, :n
|
10
|
+
private :primes=, :exponent=, :rational=, :hensel_code=
|
11
|
+
|
12
|
+
def initialize(primes, exponent, number)
|
13
|
+
can_initilize?
|
14
|
+
@primes = primes
|
15
|
+
@exponent = exponent
|
16
|
+
@g = primes.inject(:*)
|
17
|
+
@n = Integer.sqrt(((g**exponent) - 1) / 2)
|
18
|
+
valid_number?(number)
|
19
|
+
encode
|
20
|
+
decode
|
21
|
+
end
|
22
|
+
|
23
|
+
def numerator
|
24
|
+
rational.numerator
|
25
|
+
end
|
26
|
+
|
27
|
+
def denominator
|
28
|
+
rational.denominator
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_r
|
32
|
+
decode
|
33
|
+
rational
|
34
|
+
end
|
35
|
+
|
36
|
+
def +(other)
|
37
|
+
valid?(other)
|
38
|
+
evaluate("+", other)
|
39
|
+
end
|
40
|
+
|
41
|
+
def -(other)
|
42
|
+
valid?(other)
|
43
|
+
evaluate("-", other)
|
44
|
+
end
|
45
|
+
|
46
|
+
def *(other)
|
47
|
+
valid?(other)
|
48
|
+
evaluate("*", other)
|
49
|
+
end
|
50
|
+
|
51
|
+
def /(other)
|
52
|
+
valid?(other)
|
53
|
+
evaluate("/", other)
|
54
|
+
end
|
55
|
+
|
56
|
+
def replace_primes(new_primes)
|
57
|
+
replace_attribute("primes=", new_primes, 0)
|
58
|
+
end
|
59
|
+
|
60
|
+
def replace_exponent(new_exponent)
|
61
|
+
replace_attribute("exponent=", new_exponent, 0)
|
62
|
+
end
|
63
|
+
|
64
|
+
def replace_rational(new_rational)
|
65
|
+
replace_attribute("rational=", new_rational, 0)
|
66
|
+
end
|
67
|
+
|
68
|
+
def replace_hensel_code(new_hensel_code)
|
69
|
+
valid_hensel_code?(new_hensel_code)
|
70
|
+
replace_attribute("hensel_code=", new_hensel_code, 1)
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def can_initilize?
|
76
|
+
message = "#{self.class} can only be inherited."
|
77
|
+
raise NonInitializableClass, message if instance_of?(HenselCode::GAdicBase)
|
78
|
+
end
|
79
|
+
|
80
|
+
def replace_attribute(attribute, new_value, order)
|
81
|
+
send(attribute, new_value)
|
82
|
+
order.zero? ? [encode, decode] : [decode, encode]
|
83
|
+
self
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HenselCode
|
4
|
+
# verifications pre-evaluation of hensel codes
|
5
|
+
module GAdicVerifier
|
6
|
+
def valid?(other)
|
7
|
+
incompatible_operand_type?(other)
|
8
|
+
different_primes_and_same_exponent?(other)
|
9
|
+
different_primes_and_different_exponent?(other)
|
10
|
+
same_primes_and_different_exponent?(other)
|
11
|
+
end
|
12
|
+
|
13
|
+
def incompatible_operand_type?(other)
|
14
|
+
message = "#{self} is a #{self.class} while #{other} is a #{other.class}"
|
15
|
+
raise IncompatibleOperandTypes, message unless instance_of?(other.class)
|
16
|
+
end
|
17
|
+
|
18
|
+
def different_primes_and_same_exponent?(other)
|
19
|
+
message = "#{self} has primes #{primes} while #{other} has prime #{other.primes}"
|
20
|
+
raise HenselCodesWithDifferentPrimes, message if primes != other.primes && exponent == other.exponent
|
21
|
+
end
|
22
|
+
|
23
|
+
def different_primes_and_different_exponent?(other)
|
24
|
+
message = <<~MSG
|
25
|
+
"#{self} has prime #{primes} and exponent #{exponent}
|
26
|
+
while #{other} has prime #{other.primes} and exponent #{other.exponent}
|
27
|
+
MSG
|
28
|
+
raise HenselCodesWithDifferentPrimesAndExponents, message if primes != other.primes && exponent != other.exponent
|
29
|
+
end
|
30
|
+
|
31
|
+
def same_primes_and_different_exponent?(other)
|
32
|
+
message = "#{self} has exponent #{exponent} while #{other} has exponent #{other.exponent}"
|
33
|
+
raise HenselCodesWithDifferentExponents, message if primes == other.primes && exponent != other.exponent
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HenselCode
|
4
|
+
# modular arithmetic class
|
5
|
+
module ModularArithmetic
|
6
|
+
def cauchy_product(prime, coefficients1, coefficients2)
|
7
|
+
product = []
|
8
|
+
carry = 0
|
9
|
+
(0..coefficients1.size - 1).each do |i|
|
10
|
+
sum = 0
|
11
|
+
(0..i).each { |j| sum += (coefficients1[j] * coefficients2[i - j]) }
|
12
|
+
product << ((carry + sum) % prime)
|
13
|
+
carry = (carry + sum) / prime
|
14
|
+
end
|
15
|
+
product
|
16
|
+
end
|
17
|
+
|
18
|
+
def multiplication(prime, coefficients1, coefficients2)
|
19
|
+
partial_multiplications = []
|
20
|
+
coefficients2.each_with_index do |c1, i|
|
21
|
+
rows = multiplication_inner_loop(prime, coefficients1, coefficients2, c1, i)
|
22
|
+
partial_multiplications << rows
|
23
|
+
rows.append(*([0] * i))
|
24
|
+
end
|
25
|
+
sum_of_partial_multiplications(partial_multiplications)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def multiplication_inner_loop(prime, coefficients1, coefficients2, c1_, index)
|
31
|
+
rows = []
|
32
|
+
carry = 0
|
33
|
+
coefficients1.each_with_index do |c2, j|
|
34
|
+
rows << ((carry + (c1_ * c2)) % prime)
|
35
|
+
carry = (carry + (c1_ * c2)) / prime
|
36
|
+
(rows << carry).reverse!.insert(0, *([0] * (j - index))) if j == coefficients2.size - 1
|
37
|
+
end
|
38
|
+
rows
|
39
|
+
end
|
40
|
+
|
41
|
+
def sum_of_partial_multiplications(partial_multiplications)
|
42
|
+
carry = 0
|
43
|
+
sum = []
|
44
|
+
partial_multiplications.map(&:reverse).transpose.map do |x|
|
45
|
+
sum << ((carry + x.reduce(:+)) % prime)
|
46
|
+
carry = (carry + x.reduce(:+)) / prime
|
47
|
+
end
|
48
|
+
sum
|
49
|
+
end
|
50
|
+
|
51
|
+
def addition(prime, coefficients1, coefficients2)
|
52
|
+
carry = 0
|
53
|
+
result_coefficients = []
|
54
|
+
coefficients1.zip(coefficients2).each do |x|
|
55
|
+
result_coefficients << ((carry + x.reduce(:+)) % prime)
|
56
|
+
carry = (carry + x.reduce(:+)) / prime
|
57
|
+
end
|
58
|
+
result_coefficients
|
59
|
+
end
|
60
|
+
|
61
|
+
def subtraction(prime, coefficients1, coefficients2)
|
62
|
+
addition(prime, coefficients1, negation(prime, coefficients2))
|
63
|
+
end
|
64
|
+
|
65
|
+
def negation(prime, coefficients)
|
66
|
+
leading_zeros = coefficients.take_while(&:zero?)
|
67
|
+
coefficients_without_leading_zeros = coefficients.drop_while(&:zero?)
|
68
|
+
new_coefficients = [(prime - coefficients_without_leading_zeros[0]) % prime]
|
69
|
+
new_coefficients += coefficients[leading_zeros.size + 1..].map do |c|
|
70
|
+
((prime - 1) - c) % prime
|
71
|
+
end
|
72
|
+
leading_zeros + new_coefficients
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,85 @@
|
|
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
|
+
can_initilize?
|
14
|
+
@prime = prime
|
15
|
+
@exponent = exponent
|
16
|
+
@n = Integer.sqrt(((prime**exponent) - 1) / 2)
|
17
|
+
valid_number?(number)
|
18
|
+
encode
|
19
|
+
decode
|
20
|
+
end
|
21
|
+
|
22
|
+
def numerator
|
23
|
+
rational.numerator
|
24
|
+
end
|
25
|
+
|
26
|
+
def denominator
|
27
|
+
rational.denominator
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_r
|
31
|
+
decode
|
32
|
+
rational
|
33
|
+
end
|
34
|
+
|
35
|
+
def +(other)
|
36
|
+
valid?(other)
|
37
|
+
evaluate("+", other)
|
38
|
+
end
|
39
|
+
|
40
|
+
def -(other)
|
41
|
+
valid?(other)
|
42
|
+
evaluate("-", other)
|
43
|
+
end
|
44
|
+
|
45
|
+
def *(other)
|
46
|
+
valid?(other)
|
47
|
+
evaluate("*", other)
|
48
|
+
end
|
49
|
+
|
50
|
+
def /(other)
|
51
|
+
valid?(other)
|
52
|
+
evaluate("/", other)
|
53
|
+
end
|
54
|
+
|
55
|
+
def replace_prime(new_prime)
|
56
|
+
replace_attribute("prime=", new_prime, 0)
|
57
|
+
end
|
58
|
+
|
59
|
+
def replace_exponent(new_exponent)
|
60
|
+
replace_attribute("exponent=", new_exponent, 0)
|
61
|
+
end
|
62
|
+
|
63
|
+
def replace_rational(new_rational)
|
64
|
+
replace_attribute("rational=", new_rational, 0)
|
65
|
+
end
|
66
|
+
|
67
|
+
def replace_hensel_code(new_hensel_code)
|
68
|
+
valid_hensel_code?(new_hensel_code)
|
69
|
+
replace_attribute("hensel_code=", new_hensel_code, 1)
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def can_initilize?
|
75
|
+
message = "#{self.class} can only be inherited."
|
76
|
+
raise NonInitializableClass, message if instance_of?(HenselCode::PAdicBase)
|
77
|
+
end
|
78
|
+
|
79
|
+
def replace_attribute(attribute, new_value, order)
|
80
|
+
send(attribute, new_value)
|
81
|
+
order.zero? ? [encode, decode] : [decode, encode]
|
82
|
+
self
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HenselCode
|
4
|
+
# polynomial class
|
5
|
+
class Polynomial
|
6
|
+
include HenselCode::Tools
|
7
|
+
include HenselCode::ModularArithmetic
|
8
|
+
|
9
|
+
attr_accessor :prime, :coefficients, :fixed_length
|
10
|
+
|
11
|
+
def initialize(prime, coefficients, fixed_length: true)
|
12
|
+
@prime = prime
|
13
|
+
@coefficients = coefficients
|
14
|
+
@fixed_length = fixed_length
|
15
|
+
valid_prime?
|
16
|
+
valid_coefficients?
|
17
|
+
end
|
18
|
+
|
19
|
+
def +(other)
|
20
|
+
valid_operands?(other)
|
21
|
+
new_coefficients = addition(prime, coefficients, other.coefficients)
|
22
|
+
self.class.new prime, new_coefficients
|
23
|
+
end
|
24
|
+
|
25
|
+
def -(other)
|
26
|
+
valid_operands?(other)
|
27
|
+
new_coefficients = subtraction(prime, coefficients, other.coefficients)
|
28
|
+
self.class.new prime, new_coefficients
|
29
|
+
end
|
30
|
+
|
31
|
+
def *(other)
|
32
|
+
valid_operands?(other)
|
33
|
+
new_coefficients = multiplication(prime, coefficients, other.coefficients)
|
34
|
+
self.class.new prime, new_coefficients[0..coefficients.size - 1]
|
35
|
+
end
|
36
|
+
|
37
|
+
def /(other)
|
38
|
+
valid_operands?(other)
|
39
|
+
new_coefficients = (self * other.inverse).coefficients
|
40
|
+
self.class.new prime, new_coefficients[0..coefficients.size - 1]
|
41
|
+
end
|
42
|
+
|
43
|
+
def inverse
|
44
|
+
x = generate_padic_x
|
45
|
+
two = generate_padic_constant_integer(2)
|
46
|
+
x = (two * x) - (self * x * x) while (x * self).coefficients != [1] + Array.new(coefficients.size - 1, 0)
|
47
|
+
x
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_s
|
51
|
+
coefficients.map.with_index do |c, i|
|
52
|
+
"#{c}#{polynomial_variable(i)}"
|
53
|
+
end.join(" + ")
|
54
|
+
end
|
55
|
+
|
56
|
+
def inspect
|
57
|
+
"<Polynomial: #{polynomial_form}>"
|
58
|
+
end
|
59
|
+
|
60
|
+
def degree
|
61
|
+
coefficients.size - 1
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def valid_prime?
|
67
|
+
raise ArgumentError, "prime can't be nil" if @prime.nil?
|
68
|
+
raise ArgumentError, "prime must be an integer" unless @prime.is_a?(Integer)
|
69
|
+
end
|
70
|
+
|
71
|
+
def valid_coefficients?
|
72
|
+
coefficients_condition = @coefficients.is_a?(Array) && @coefficients.map(&:class).uniq == [Integer]
|
73
|
+
raise ArgumentError, "coefficients can't be nil" if @coefficients.nil?
|
74
|
+
raise ArgumentError, "coefficients must be an array" unless @coefficients.is_a?(Array)
|
75
|
+
raise ArgumentError, "coefficients must be an array" unless coefficients_condition
|
76
|
+
end
|
77
|
+
|
78
|
+
def valid_operands?(other)
|
79
|
+
s1 = coefficients.size
|
80
|
+
s2 = other.coefficients.size
|
81
|
+
raise WrongHenselCodeInputType, "polynomials must have same degree" if s1 != s2
|
82
|
+
raise WrongHenselCodeInputType, "polynomials must have same prime" if prime != other.prime
|
83
|
+
end
|
84
|
+
|
85
|
+
def generate_padic_x
|
86
|
+
x_coefficients = [mod_inverse(coefficients[0], prime)] + Array.new(coefficients.size - 1) { rand(0..prime - 1) }
|
87
|
+
self.class.new prime, x_coefficients
|
88
|
+
end
|
89
|
+
|
90
|
+
def generate_padic_constant_integer(number)
|
91
|
+
self.class.new prime, [number] + Array.new(coefficients.size - 1, 0)
|
92
|
+
end
|
93
|
+
|
94
|
+
def mul(other)
|
95
|
+
new_coefficients = multiplication(prime, coefficients, other.coefficients)
|
96
|
+
self.class.new prime, new_coefficients
|
97
|
+
end
|
98
|
+
|
99
|
+
def polynomial_form
|
100
|
+
to_s
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
data/lib/hensel_code/tools.rb
CHANGED
@@ -36,13 +36,13 @@ module HenselCode
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
def
|
40
|
-
|
41
|
-
while
|
42
|
-
|
43
|
-
|
39
|
+
def random_distinct_numbers(type, quantity, bits)
|
40
|
+
numbers = [send("random_#{type}", bits)]
|
41
|
+
while numbers.size < quantity
|
42
|
+
number = send("random_#{type}", bits)
|
43
|
+
numbers << number if number != numbers.last
|
44
44
|
end
|
45
|
-
|
45
|
+
numbers
|
46
46
|
end
|
47
47
|
|
48
48
|
def eea_core(num1, num2, bound = 0)
|
@@ -77,5 +77,23 @@ module HenselCode
|
|
77
77
|
|
78
78
|
y % mod
|
79
79
|
end
|
80
|
+
|
81
|
+
def crt(moduli, remainders)
|
82
|
+
g = moduli.inject(:*)
|
83
|
+
result = 0
|
84
|
+
moduli.zip(remainders) do |modulus, remainder|
|
85
|
+
g_prime = g / modulus
|
86
|
+
g_prime_inverse = mod_inverse(g_prime, modulus)
|
87
|
+
result += ((g_prime * g_prime_inverse * remainder))
|
88
|
+
end
|
89
|
+
result % g
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def polynomial_variable(index)
|
95
|
+
i = index > 1 ? 2 : index
|
96
|
+
["", "p", "p^#{index}"][i]
|
97
|
+
end
|
80
98
|
end
|
81
99
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HenselCode
|
4
|
+
# truncated finite g-adic expansion hensel code class
|
5
|
+
class TruncatedFiniteGadicExpansion < GAdicBase
|
6
|
+
def modululi
|
7
|
+
primes.map { |prime| prime**exponent }
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_a
|
11
|
+
hensel_code.map(&:to_i)
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
hensel_code.map(&:to_i).to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
def inspect
|
19
|
+
"<HenselCode: #{to_a}>"
|
20
|
+
end
|
21
|
+
|
22
|
+
def inverse
|
23
|
+
new_hensel_code = hensel_code.map(&:inverse)
|
24
|
+
self.class.new primes, exponent, new_hensel_code
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def evaluate(operation, other)
|
30
|
+
new_hensel_code = hensel_code.zip(other.hensel_code).map { |pair| pair[0].send(operation, pair[1]) }
|
31
|
+
self.class.new primes, exponent, new_hensel_code
|
32
|
+
end
|
33
|
+
|
34
|
+
def valid_number?(number)
|
35
|
+
if number.is_a?(Rational)
|
36
|
+
@rational = number
|
37
|
+
elsif number.is_a?(Array) && number.map(&:class).uniq == [HenselCode::TruncatedFinitePadicExpansion]
|
38
|
+
@hensel_code = number
|
39
|
+
decode
|
40
|
+
else
|
41
|
+
raise WrongHenselCodeInputType, "number must be a Rational or an\
|
42
|
+
Array of truncated p-adic Hensel codes and it was a #{number.class}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def valid_hensel_code?(new_hensel_code)
|
47
|
+
condition = new_hensel_code.is_a?(Array) && new_hensel_code.map(&:class).uniq == [HenselCode::TFPE]
|
48
|
+
message = "must be an array of truncated p-adic Hensel codes"
|
49
|
+
raise WrongHenselCodeInputType, message unless condition
|
50
|
+
end
|
51
|
+
|
52
|
+
def encode
|
53
|
+
@g = primes.inject(:*)
|
54
|
+
@hensel_code = primes.map do |prime|
|
55
|
+
TruncatedFinitePadicExpansion.new prime, exponent, rational
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def decode
|
60
|
+
h = TruncatedFinitePadicExpansion.new g, exponent, crt(modululi, hensel_code.map(&:to_i))
|
61
|
+
@rational = h.to_r
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|