hensel_code 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|