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.
- checksums.yaml +4 -4
- data/.codecov.yml +6 -0
- data/CHANGELOG.md +32 -2
- data/Gemfile +2 -2
- data/Gemfile.lock +11 -2
- data/README.md +365 -42
- data/hensel_code.gemspec +39 -0
- data/lib/hensel_code/finite_gadic_expansion.rb +66 -0
- data/lib/hensel_code/finite_padic_expansion.rb +39 -12
- 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 +7 -0
- 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 +65 -0
- data/lib/hensel_code/truncated_finite_padic_expansion.rb +6 -1
- data/lib/hensel_code/version.rb +1 -1
- data/lib/hensel_code.rb +8 -0
- metadata +42 -5
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HenselCode
|
4
|
+
# truncated finite g-adic expansion hensel code class
|
5
|
+
class FiniteGadicExpansion < GAdicBase
|
6
|
+
def modululi
|
7
|
+
primes.map { |prime| prime**exponent }
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_a
|
11
|
+
hensel_code.map(&:to_a)
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
hensel_code.map(&:to_s).to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
def inspect
|
19
|
+
"<HenselCode: #{self}>"
|
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::FinitePadicExpansion]
|
38
|
+
@hensel_code = number
|
39
|
+
decode
|
40
|
+
else
|
41
|
+
message = "number must be a Rational or an\
|
42
|
+
Array of finite p-adic Hensel codes and it was a #{number.class}"
|
43
|
+
raise WrongHenselCodeInputType, message
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def valid_hensel_code?(new_hensel_code)
|
48
|
+
condition = new_hensel_code.is_a?(Array) && new_hensel_code.map(&:class).uniq == [HenselCode::FPE]
|
49
|
+
message = "must be an array of finite p-adic Hensel codes"
|
50
|
+
raise WrongHenselCodeInputType, message unless condition
|
51
|
+
end
|
52
|
+
|
53
|
+
def encode
|
54
|
+
@g = primes.inject(:*)
|
55
|
+
@hensel_code = primes.map do |prime|
|
56
|
+
FinitePadicExpansion.new prime, exponent, rational
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def decode
|
61
|
+
hs = hensel_code.map { |h| h.to_truncated.hensel_code }
|
62
|
+
h = TruncatedFinitePadicExpansion.new g, exponent, crt(modululi, hs)
|
63
|
+
@rational = h.to_r
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -3,6 +3,8 @@
|
|
3
3
|
module HenselCode
|
4
4
|
# finite p-adic expansion hensel code class
|
5
5
|
class FinitePadicExpansion < PAdicBase
|
6
|
+
attr_accessor :polynomial
|
7
|
+
|
6
8
|
def modulus
|
7
9
|
prime
|
8
10
|
end
|
@@ -22,26 +24,25 @@ module HenselCode
|
|
22
24
|
end
|
23
25
|
|
24
26
|
def inspect
|
25
|
-
"
|
27
|
+
"<HenselCode: #{polynomial_form}>"
|
26
28
|
end
|
27
29
|
|
28
|
-
|
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 }
|
30
|
+
def inverse
|
31
|
+
new_hensel_code = polynomial.inverse.coefficients
|
33
32
|
self.class.new prime, exponent, new_hensel_code
|
34
33
|
end
|
35
34
|
|
36
|
-
|
37
|
-
i = index > 1 ? 2 : index
|
38
|
-
["", "p", "p^#{index}"][i]
|
39
|
-
end
|
35
|
+
private
|
40
36
|
|
41
37
|
def polynomial_form
|
42
38
|
to_s
|
43
39
|
end
|
44
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
|
+
|
45
46
|
def valid_number?(number)
|
46
47
|
if number.is_a?(Rational)
|
47
48
|
@rational = number
|
@@ -66,8 +67,8 @@ module HenselCode
|
|
66
67
|
end
|
67
68
|
|
68
69
|
def encode
|
69
|
-
|
70
|
-
@
|
70
|
+
@hensel_code = rational_to_padic_digits
|
71
|
+
@polynomial = Polynomial.new prime, hensel_code
|
71
72
|
end
|
72
73
|
|
73
74
|
def decode
|
@@ -75,5 +76,31 @@ module HenselCode
|
|
75
76
|
hensel_code.each_with_index { |d, i| number += d * (prime**i) }
|
76
77
|
@rational = TruncatedFinitePadicExpansion.new(prime, exponent, number).to_r
|
77
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
|
78
105
|
end
|
79
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
|
@@ -10,11 +10,13 @@ module HenselCode
|
|
10
10
|
private :prime=, :exponent=, :rational=, :hensel_code=
|
11
11
|
|
12
12
|
def initialize(prime, exponent, number)
|
13
|
+
can_initilize?
|
13
14
|
@prime = prime
|
14
15
|
@exponent = exponent
|
15
16
|
@n = Integer.sqrt(((prime**exponent) - 1) / 2)
|
16
17
|
valid_number?(number)
|
17
18
|
encode
|
19
|
+
decode
|
18
20
|
end
|
19
21
|
|
20
22
|
def numerator
|
@@ -69,6 +71,11 @@ module HenselCode
|
|
69
71
|
|
70
72
|
private
|
71
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
|
+
|
72
79
|
def replace_attribute(attribute, new_value, order)
|
73
80
|
send(attribute, new_value)
|
74
81
|
order.zero? ? [encode, decode] : [decode, encode]
|
@@ -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 unless numbers.include?(number)
|
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,65 @@
|
|
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
|
+
message = "number must be a Rational or an\
|
42
|
+
Array of truncated p-adic Hensel codes and it was a #{number.class}"
|
43
|
+
raise WrongHenselCodeInputType, message
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def valid_hensel_code?(new_hensel_code)
|
48
|
+
condition = new_hensel_code.is_a?(Array) && new_hensel_code.map(&:class).uniq == [HenselCode::TFPE]
|
49
|
+
message = "must be an array of truncated p-adic Hensel codes"
|
50
|
+
raise WrongHenselCodeInputType, message unless condition
|
51
|
+
end
|
52
|
+
|
53
|
+
def encode
|
54
|
+
@g = primes.inject(:*)
|
55
|
+
@hensel_code = primes.map do |prime|
|
56
|
+
TruncatedFinitePadicExpansion.new prime, exponent, rational
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def decode
|
61
|
+
h = TruncatedFinitePadicExpansion.new g, exponent, crt(modululi, hensel_code.map(&:to_i))
|
62
|
+
@rational = h.to_r
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -16,7 +16,12 @@ module HenselCode
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def inspect
|
19
|
-
"
|
19
|
+
"<HenselCode: #{hensel_code}>"
|
20
|
+
end
|
21
|
+
|
22
|
+
def inverse
|
23
|
+
new_hensel_code = mod_inverse(hensel_code, modulus)
|
24
|
+
self.class.new prime, exponent, new_hensel_code
|
20
25
|
end
|
21
26
|
|
22
27
|
private
|
data/lib/hensel_code/version.rb
CHANGED
data/lib/hensel_code.rb
CHANGED
@@ -13,15 +13,23 @@ module HenselCode
|
|
13
13
|
class HenselCodesWithDifferentPrimesAndExponents < StandardError; end
|
14
14
|
class HenselCodesWithDifferentExponents < StandardError; end
|
15
15
|
class IncompatibleOperandTypes < StandardError; end
|
16
|
+
class NonInitializableClass < StandardError; end
|
16
17
|
|
17
18
|
autoload :Tools, "hensel_code/tools"
|
18
19
|
autoload :PAdicBase, "hensel_code/padic_base"
|
20
|
+
autoload :GAdicBase, "hensel_code/gadic_base"
|
21
|
+
autoload :Polynomial, "hensel_code/polynomial"
|
19
22
|
autoload :PAdicVerifier, "hensel_code/padic_verifier"
|
23
|
+
autoload :GAdicVerifier, "hensel_code/gadic_verifier"
|
24
|
+
autoload :ModularArithmetic, "hensel_code/modular_arithmetic"
|
20
25
|
autoload :FinitePadicExpansion, "hensel_code/finite_padic_expansion"
|
26
|
+
autoload :FiniteGadicExpansion, "hensel_code/finite_gadic_expansion"
|
21
27
|
autoload :TruncatedFinitePadicExpansion, "hensel_code/truncated_finite_padic_expansion"
|
28
|
+
autoload :TruncatedFiniteGadicExpansion, "hensel_code/truncated_finite_gadic_expansion"
|
22
29
|
|
23
30
|
# aliases for classes with long names
|
24
31
|
TFPE = TruncatedFinitePadicExpansion
|
32
|
+
FPE = TruncatedFinitePadicExpansion
|
25
33
|
HCWDPAE = HenselCodesWithDifferentPrimesAndExponents
|
26
34
|
WHIT = WrongHenselCodeInputType
|
27
35
|
end
|