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 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: []