hyperint 0.1.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 +7 -0
- data/lib/hyperint/arithmetic.rb +255 -0
- data/lib/hyperint/comparison.rb +36 -0
- data/lib/hyperint/conversion.rb +76 -0
- data/lib/hyperint.rb +102 -0
- metadata +44 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 16b917b6d6e3980ac82b82a2d1fd63d8a1e80357f8eae7cb91d165b0de2057ff
|
|
4
|
+
data.tar.gz: 590789dda4742539c3ac640f942b78d54efae3d5df65839a0e7bc151ad642ae3
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 15d10f1f67bb90dde3a75e92670133a8b860e0e5482b36d4596cf84098bd4a150a97d5f62d409a3dcfcb18b73443a054441d725d3b684b7bd828d26817887027
|
|
7
|
+
data.tar.gz: ee69e14032cc324c50feb77996a129f7d764f825c4c1115a3f8a024d4922369e0c25339fbaa82d06f185ca980d32c4cb90aca09280ac5daf95a8dacd3b00ddfe
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
class HyperInt
|
|
2
|
+
def add(other)
|
|
3
|
+
check_type(other)
|
|
4
|
+
|
|
5
|
+
if zero?
|
|
6
|
+
return other.abs
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
if other.zero?
|
|
10
|
+
return abs
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
if @sign == other.sign
|
|
14
|
+
result = add_magnitudes(@digits, other.digits)
|
|
15
|
+
return build_from_digits(@sign, result)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
comparison = compare_magnitudes(@digits, other.digits)
|
|
19
|
+
|
|
20
|
+
if comparison == 0
|
|
21
|
+
return HyperInt.zero
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
if comparison > 0
|
|
25
|
+
result = subtract_magnitudes(@digits, other.digits)
|
|
26
|
+
build_from_digits(@sign, result)
|
|
27
|
+
else
|
|
28
|
+
result = subtract_magnitudes(other.digits, @digits)
|
|
29
|
+
build_from_digits(other.sign, result)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def sub(other)
|
|
34
|
+
check_type(other)
|
|
35
|
+
add(other.negate)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def mul(other)
|
|
39
|
+
check_type(other)
|
|
40
|
+
|
|
41
|
+
return HyperInt.zero if zero? || other.zero?
|
|
42
|
+
|
|
43
|
+
result = Array.new(@digits.length + other.digits.length, 0)
|
|
44
|
+
|
|
45
|
+
@digits.each_with_index do |a, i|
|
|
46
|
+
carry = 0
|
|
47
|
+
|
|
48
|
+
other.digits.each_with_index do |b, j|
|
|
49
|
+
index = i + j
|
|
50
|
+
value = result[index] + a * b + carry
|
|
51
|
+
|
|
52
|
+
result[index] = value % BASE
|
|
53
|
+
carry = value / BASE
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
index = i + other.digits.length
|
|
57
|
+
|
|
58
|
+
while carry > 0
|
|
59
|
+
value = result[index] + carry
|
|
60
|
+
result[index] = value % BASE
|
|
61
|
+
carry = value / BASE
|
|
62
|
+
index += 1
|
|
63
|
+
result << 0 if index == result.length && carry > 0
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
sign = @sign == other.sign ? 1 : -1
|
|
68
|
+
build_from_digits(sign, result)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def div(other)
|
|
72
|
+
check_type(other)
|
|
73
|
+
raise ZeroDivisionError, "divided by 0" if other.zero?
|
|
74
|
+
|
|
75
|
+
quotient, = divide_magnitudes(@digits, other.digits)
|
|
76
|
+
|
|
77
|
+
if quotient == [0]
|
|
78
|
+
return HyperInt.zero
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
sign = @sign == other.sign ? 1 : -1
|
|
82
|
+
build_from_digits(sign, quotient)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def mod(other)
|
|
86
|
+
check_type(other)
|
|
87
|
+
raise ZeroDivisionError, "divided by 0" if other.zero?
|
|
88
|
+
|
|
89
|
+
_, remainder = divide_magnitudes(@digits, other.digits)
|
|
90
|
+
|
|
91
|
+
if remainder == [0]
|
|
92
|
+
return HyperInt.zero
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
build_from_digits(@sign, remainder)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def pow(other)
|
|
99
|
+
check_type(other)
|
|
100
|
+
raise ArgumentError, "negative exponent" if other.negative?
|
|
101
|
+
|
|
102
|
+
result = HyperInt.one
|
|
103
|
+
base = abs
|
|
104
|
+
exponent = other.abs
|
|
105
|
+
two = HyperInt.new(2)
|
|
106
|
+
|
|
107
|
+
while !exponent.zero?
|
|
108
|
+
if exponent.mod(two).equals?(HyperInt.one)
|
|
109
|
+
result = result.mul(base)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
exponent = exponent.div(two)
|
|
113
|
+
|
|
114
|
+
unless exponent.zero?
|
|
115
|
+
base = base.mul(base)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
if @sign < 0 && other.mod(two).equals?(HyperInt.one)
|
|
120
|
+
result.negate
|
|
121
|
+
else
|
|
122
|
+
result
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
private
|
|
127
|
+
|
|
128
|
+
def add_magnitudes(a, b)
|
|
129
|
+
result = []
|
|
130
|
+
carry = 0
|
|
131
|
+
length = [a.length, b.length].max
|
|
132
|
+
|
|
133
|
+
length.times do |i|
|
|
134
|
+
sum = (a[i] || 0) + (b[i] || 0) + carry
|
|
135
|
+
|
|
136
|
+
if sum >= BASE
|
|
137
|
+
sum -= BASE
|
|
138
|
+
carry = 1
|
|
139
|
+
else
|
|
140
|
+
carry = 0
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
result << sum
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
result << carry if carry > 0
|
|
147
|
+
result
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def subtract_magnitudes(a, b)
|
|
151
|
+
result = []
|
|
152
|
+
borrow = 0
|
|
153
|
+
|
|
154
|
+
a.length.times do |i|
|
|
155
|
+
difference = a[i] - (b[i] || 0) - borrow
|
|
156
|
+
|
|
157
|
+
if difference < 0
|
|
158
|
+
difference += BASE
|
|
159
|
+
borrow = 1
|
|
160
|
+
else
|
|
161
|
+
borrow = 0
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
result << difference
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
result.pop while result.length > 1 && result[-1] == 0
|
|
168
|
+
result
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def compare_magnitudes(a, b)
|
|
172
|
+
return 1 if a.length > b.length
|
|
173
|
+
return -1 if a.length < b.length
|
|
174
|
+
|
|
175
|
+
(a.length - 1).downto(0) do |i|
|
|
176
|
+
return 1 if a[i] > b[i]
|
|
177
|
+
return -1 if a[i] < b[i]
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
0
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def divide_magnitudes(dividend, divisor)
|
|
184
|
+
return [[0], dividend.dup] if compare_magnitudes(dividend, divisor) < 0
|
|
185
|
+
|
|
186
|
+
quotient = Array.new(dividend.length, 0)
|
|
187
|
+
remainder = [0]
|
|
188
|
+
|
|
189
|
+
(dividend.length - 1).downto(0) do |i|
|
|
190
|
+
remainder.unshift(dividend[i])
|
|
191
|
+
normalize_digits(remainder)
|
|
192
|
+
|
|
193
|
+
low = 0
|
|
194
|
+
high = BASE - 1
|
|
195
|
+
|
|
196
|
+
while low <= high
|
|
197
|
+
middle = (low + high) / 2
|
|
198
|
+
product = multiply_magnitude_by_small(divisor, middle)
|
|
199
|
+
comparison = compare_magnitudes(product, remainder)
|
|
200
|
+
|
|
201
|
+
if comparison <= 0
|
|
202
|
+
low = middle + 1
|
|
203
|
+
else
|
|
204
|
+
high = middle - 1
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
digit = high
|
|
209
|
+
|
|
210
|
+
if digit > 0
|
|
211
|
+
product = multiply_magnitude_by_small(divisor, digit)
|
|
212
|
+
remainder = subtract_magnitudes(remainder, product)
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
quotient[i] = digit
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
normalize_digits(quotient)
|
|
219
|
+
normalize_digits(remainder)
|
|
220
|
+
|
|
221
|
+
[quotient, remainder]
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def multiply_magnitude_by_small(digits, multiplier)
|
|
225
|
+
return [0] if multiplier == 0
|
|
226
|
+
|
|
227
|
+
result = []
|
|
228
|
+
carry = 0
|
|
229
|
+
|
|
230
|
+
digits.each do |digit|
|
|
231
|
+
value = digit * multiplier + carry
|
|
232
|
+
result << value % BASE
|
|
233
|
+
carry = value / BASE
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
result << carry if carry > 0
|
|
237
|
+
result
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def normalize_digits(digits)
|
|
241
|
+
digits.pop while digits.length > 1 && digits[-1] == 0
|
|
242
|
+
|
|
243
|
+
digits << 0 if digits.empty?
|
|
244
|
+
|
|
245
|
+
digits
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def build_from_digits(sign, digits)
|
|
249
|
+
result = HyperInt.new(0)
|
|
250
|
+
result.instance_variable_set(:@sign, sign)
|
|
251
|
+
result.instance_variable_set(:@digits, digits)
|
|
252
|
+
result.send(:normalize)
|
|
253
|
+
result
|
|
254
|
+
end
|
|
255
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
class HyperInt
|
|
2
|
+
def equals?(other)
|
|
3
|
+
check_type(other)
|
|
4
|
+
to_i == other.to_i
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def not_equals?(other)
|
|
8
|
+
!equals?(other)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def greater_than?(other)
|
|
12
|
+
check_type(other)
|
|
13
|
+
to_i > other.to_i
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def greater_than_or_equal?(other)
|
|
17
|
+
check_type(other)
|
|
18
|
+
to_i >= other.to_i
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def less_than?(other)
|
|
22
|
+
check_type(other)
|
|
23
|
+
to_i < other.to_i
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def less_than_or_equal?(other)
|
|
27
|
+
check_type(other)
|
|
28
|
+
to_i <= other.to_i
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def check_type(other)
|
|
34
|
+
raise TypeError, "argument must be HyperInt" unless other.is_a?(HyperInt)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
class HyperInt
|
|
2
|
+
def to_s(base = 10)
|
|
3
|
+
raise ArgumentError, "base must be between 2 and 36" unless (2..36).include?(base)
|
|
4
|
+
return "0" if zero?
|
|
5
|
+
|
|
6
|
+
value = self
|
|
7
|
+
result = +""
|
|
8
|
+
|
|
9
|
+
while !value.zero?
|
|
10
|
+
quotient, remainder = value.divide_small(base)
|
|
11
|
+
result << remainder.to_s(36)
|
|
12
|
+
value = quotient
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
result.reverse!
|
|
16
|
+
@sign < 0 ? "-#{result}" : result
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def abs
|
|
20
|
+
copy = HyperInt.new(0)
|
|
21
|
+
copy.instance_variable_set(:@sign, @sign.abs)
|
|
22
|
+
copy.instance_variable_set(:@digits, @digits.dup)
|
|
23
|
+
copy
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def negate
|
|
27
|
+
copy = HyperInt.new(0)
|
|
28
|
+
copy.instance_variable_set(:@sign, -@sign)
|
|
29
|
+
copy.instance_variable_set(:@digits, @digits.dup)
|
|
30
|
+
copy
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def zero?
|
|
34
|
+
@sign == 0
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def positive?
|
|
38
|
+
@sign > 0
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def negative?
|
|
42
|
+
@sign < 0
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.zero
|
|
46
|
+
new(0)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def self.one
|
|
50
|
+
new(1)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def divide_small(divisor)
|
|
54
|
+
result = []
|
|
55
|
+
remainder = 0
|
|
56
|
+
|
|
57
|
+
@digits.reverse_each do |digit|
|
|
58
|
+
current = remainder * BASE + digit
|
|
59
|
+
quotient = current / divisor
|
|
60
|
+
remainder = current % divisor
|
|
61
|
+
result << quotient
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
result.reverse!
|
|
65
|
+
|
|
66
|
+
while result.length > 1 && result[-1] == 0
|
|
67
|
+
result.pop
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
quotient = HyperInt.new(0)
|
|
71
|
+
quotient.instance_variable_set(:@sign, result == [0] ? 0 : @sign)
|
|
72
|
+
quotient.instance_variable_set(:@digits, result)
|
|
73
|
+
|
|
74
|
+
[quotient, remainder]
|
|
75
|
+
end
|
|
76
|
+
end
|
data/lib/hyperint.rb
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
require_relative "hyperint/arithmetic"
|
|
2
|
+
require_relative "hyperint/comparison"
|
|
3
|
+
require_relative "hyperint/conversion"
|
|
4
|
+
|
|
5
|
+
class HyperInt
|
|
6
|
+
BASE = 1_000_000_000
|
|
7
|
+
BASE_DIGITS = 9
|
|
8
|
+
|
|
9
|
+
attr_reader :sign, :digits
|
|
10
|
+
|
|
11
|
+
def initialize(value, base = 10)
|
|
12
|
+
raise TypeError, "value must be an Integer or String" unless value.is_a?(Integer) || value.is_a?(String)
|
|
13
|
+
raise ArgumentError, "base must be between 2 and 36" unless (2..36).include?(base)
|
|
14
|
+
|
|
15
|
+
if value.is_a?(Integer)
|
|
16
|
+
initialize_from_integer(value)
|
|
17
|
+
else
|
|
18
|
+
initialize_from_string(value, base)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.from_integer(value)
|
|
23
|
+
new(value)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def to_i
|
|
27
|
+
value = 0
|
|
28
|
+
|
|
29
|
+
@digits.reverse_each do |digit|
|
|
30
|
+
value = value * BASE + digit
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
@sign < 0 ? -value : value
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
def initialize_from_integer(value)
|
|
38
|
+
@sign = value <=> 0
|
|
39
|
+
|
|
40
|
+
if value == 0
|
|
41
|
+
@digits = [0]
|
|
42
|
+
return
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
value = value.abs
|
|
46
|
+
@digits = []
|
|
47
|
+
|
|
48
|
+
while value > 0
|
|
49
|
+
@digits << value % BASE
|
|
50
|
+
value /= BASE
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def initialize_from_string(value, base)
|
|
55
|
+
value = value.strip
|
|
56
|
+
|
|
57
|
+
negative = value.start_with?("-")
|
|
58
|
+
value = value.delete_prefix("-").delete_prefix("+")
|
|
59
|
+
|
|
60
|
+
raise ArgumentError, "invalid integer" unless value.match?(/\A[0-9a-zA-Z]+\z/)
|
|
61
|
+
|
|
62
|
+
@sign = negative ? -1 : 1
|
|
63
|
+
@digits = convert_string_to_digits(value, base)
|
|
64
|
+
normalize
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def convert_string_to_digits(value, base)
|
|
68
|
+
result = [0]
|
|
69
|
+
|
|
70
|
+
value.each_char do |char|
|
|
71
|
+
digit = char.to_i(base)
|
|
72
|
+
|
|
73
|
+
raise ArgumentError, "invalid digit" if digit >= base
|
|
74
|
+
|
|
75
|
+
carry = digit
|
|
76
|
+
|
|
77
|
+
result.each_index do |i|
|
|
78
|
+
current = result[i] * base + carry
|
|
79
|
+
result[i] = current % BASE
|
|
80
|
+
carry = current / BASE
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
result << carry if carry > 0
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
result
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def normalize
|
|
90
|
+
@digits.pop while @digits.length > 1 && @digits[-1] == 0
|
|
91
|
+
|
|
92
|
+
if @digits.length == 1 && @digits[0] == 0
|
|
93
|
+
@sign = 0
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def check_type(other)
|
|
98
|
+
raise TypeError, "argument must be HyperInt" unless other.is_a?(HyperInt)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
private :check_type
|
|
102
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: hyperint
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Araki-Tomoya
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: A large integer library implemented independently from Ruby Integer.
|
|
13
|
+
email:
|
|
14
|
+
- ''
|
|
15
|
+
executables: []
|
|
16
|
+
extensions: []
|
|
17
|
+
extra_rdoc_files: []
|
|
18
|
+
files:
|
|
19
|
+
- lib/hyperint.rb
|
|
20
|
+
- lib/hyperint/arithmetic.rb
|
|
21
|
+
- lib/hyperint/comparison.rb
|
|
22
|
+
- lib/hyperint/conversion.rb
|
|
23
|
+
homepage: https://github.com/rimsky-yamatov/hyperint
|
|
24
|
+
licenses:
|
|
25
|
+
- MIT
|
|
26
|
+
metadata: {}
|
|
27
|
+
rdoc_options: []
|
|
28
|
+
require_paths:
|
|
29
|
+
- lib
|
|
30
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
31
|
+
requirements:
|
|
32
|
+
- - ">="
|
|
33
|
+
- !ruby/object:Gem::Version
|
|
34
|
+
version: '3.1'
|
|
35
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
40
|
+
requirements: []
|
|
41
|
+
rubygems_version: 4.0.16
|
|
42
|
+
specification_version: 4
|
|
43
|
+
summary: Arbitrary-precision integer library for Ruby
|
|
44
|
+
test_files: []
|