fractify 1.0.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/.gitignore +9 -0
- data/.travis.yml +7 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +84 -0
- data/LICENSE.txt +21 -0
- data/README.md +39 -0
- data/Rakefile +12 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/fractify.gemspec +36 -0
- data/lib/fractify/calculator.rb +250 -0
- data/lib/fractify/fraction.rb +683 -0
- data/lib/fractify/operator.rb +52 -0
- data/lib/fractify/operator_array.rb +31 -0
- data/lib/fractify/version.rb +5 -0
- data/lib/fractify.rb +7 -0
- metadata +144 -0
@@ -0,0 +1,683 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fractify
|
4
|
+
class DividingByZeroError < StandardError; end
|
5
|
+
class IncorrectArgumentsError < StandardError; end
|
6
|
+
class IncorrectStringSyntax < StandardError; end
|
7
|
+
class Fraction
|
8
|
+
attr_accessor :whole_part, :numerator, :denominator, :negative
|
9
|
+
|
10
|
+
def initialize(whole_part: 0, numerator: 0, denominator: 1, string: '', number: false)
|
11
|
+
unless string.strip.empty?
|
12
|
+
to_zero!
|
13
|
+
parse_fraction_string!(string)
|
14
|
+
return
|
15
|
+
end
|
16
|
+
|
17
|
+
if number.is_a? Numeric
|
18
|
+
convert_number!(number)
|
19
|
+
return
|
20
|
+
end
|
21
|
+
raise DividingByZeroError if denominator.zero? && numerator != 0
|
22
|
+
|
23
|
+
@whole_part = whole_part
|
24
|
+
self.numerator = numerator
|
25
|
+
self.denominator = denominator
|
26
|
+
self.negative = false
|
27
|
+
|
28
|
+
simplify!
|
29
|
+
end
|
30
|
+
|
31
|
+
def negative?
|
32
|
+
negative
|
33
|
+
end
|
34
|
+
|
35
|
+
def zero?
|
36
|
+
return true if numerator.zero?
|
37
|
+
|
38
|
+
false
|
39
|
+
end
|
40
|
+
|
41
|
+
def one?
|
42
|
+
return true if numerator == 1 && denominator == 1 && whole_part.zero?
|
43
|
+
|
44
|
+
false
|
45
|
+
end
|
46
|
+
|
47
|
+
def simplify!
|
48
|
+
fix_negativity!
|
49
|
+
if !zero?
|
50
|
+
greatest_common_divisor = self.class.greatest_common_divisor(numerator, denominator)
|
51
|
+
|
52
|
+
@denominator /= greatest_common_divisor
|
53
|
+
@numerator /= greatest_common_divisor
|
54
|
+
|
55
|
+
if denominator == 1 && numerator != 1
|
56
|
+
@whole_part += numerator
|
57
|
+
@numerator = 1
|
58
|
+
elsif numerator > denominator
|
59
|
+
new_whole_part = numerator / denominator
|
60
|
+
@whole_part += new_whole_part
|
61
|
+
@numerator %= denominator
|
62
|
+
end
|
63
|
+
else
|
64
|
+
@negative = false
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def puts
|
69
|
+
puts to_s
|
70
|
+
end
|
71
|
+
|
72
|
+
def to_zero!
|
73
|
+
@whole_part = 0
|
74
|
+
@numerator = 0
|
75
|
+
@denominator = 1
|
76
|
+
@negative = false
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_improper!
|
80
|
+
return if whole_part.zero?
|
81
|
+
|
82
|
+
simplify!
|
83
|
+
@numerator -= 1 if numerator == 1 && denominator == 1
|
84
|
+
@numerator += whole_part * denominator
|
85
|
+
@whole_part = 0
|
86
|
+
end
|
87
|
+
|
88
|
+
def to_f
|
89
|
+
return 0.0 if zero?
|
90
|
+
|
91
|
+
fraction = dup
|
92
|
+
fraction.to_improper!
|
93
|
+
fraction.minus_to_numerator!
|
94
|
+
|
95
|
+
fraction.numerator.to_f / fraction.denominator
|
96
|
+
end
|
97
|
+
|
98
|
+
def to_i
|
99
|
+
return 0 if zero?
|
100
|
+
|
101
|
+
fraction = dup
|
102
|
+
fraction.simplify!
|
103
|
+
|
104
|
+
result = whole_part
|
105
|
+
result = -result if fraction.negative?
|
106
|
+
|
107
|
+
result
|
108
|
+
end
|
109
|
+
|
110
|
+
def to_s
|
111
|
+
fraction_string = '('
|
112
|
+
fraction_string += '-' if negative?
|
113
|
+
|
114
|
+
if zero?
|
115
|
+
fraction_string += '0'
|
116
|
+
elsif one?
|
117
|
+
fraction_string += '1'
|
118
|
+
else
|
119
|
+
fraction_string += whole_part.to_s if whole_part != 0
|
120
|
+
if numerator != 1 || denominator != 1
|
121
|
+
fraction_string += ' ' if whole_part != 0
|
122
|
+
fraction_string += numerator.to_s + '/' + denominator.to_s
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
fraction_string + ')'
|
127
|
+
end
|
128
|
+
|
129
|
+
def -(other)
|
130
|
+
fraction = dup
|
131
|
+
object = other.dup
|
132
|
+
if zero?
|
133
|
+
fraction.whole_part = object.whole_part
|
134
|
+
fraction.numerator = object.numerator
|
135
|
+
fraction.denominator = object.denominator
|
136
|
+
fraction.negative = !object.negative
|
137
|
+
else
|
138
|
+
fraction.to_common_denominator!(object) unless object.zero?
|
139
|
+
|
140
|
+
fraction.minus_to_numerator!
|
141
|
+
object.minus_to_numerator!
|
142
|
+
|
143
|
+
fraction.numerator -= object.numerator
|
144
|
+
fraction.minus_to_negative_field!
|
145
|
+
fraction.simplify!
|
146
|
+
end
|
147
|
+
|
148
|
+
fraction
|
149
|
+
end
|
150
|
+
|
151
|
+
def +(other)
|
152
|
+
fraction = dup
|
153
|
+
object = other.dup
|
154
|
+
if zero?
|
155
|
+
fraction.whole_part = object.whole_part
|
156
|
+
fraction.numerator = object.numerator
|
157
|
+
fraction.denominator = object.denominator
|
158
|
+
fraction.negative = object.negative
|
159
|
+
else
|
160
|
+
fraction.to_common_denominator!(object) unless object.zero?
|
161
|
+
|
162
|
+
fraction.minus_to_numerator!
|
163
|
+
object.minus_to_numerator!
|
164
|
+
|
165
|
+
fraction.numerator += object.numerator
|
166
|
+
fraction.minus_to_negative_field!
|
167
|
+
fraction.simplify!
|
168
|
+
end
|
169
|
+
|
170
|
+
fraction
|
171
|
+
end
|
172
|
+
|
173
|
+
def *(other)
|
174
|
+
fraction = dup
|
175
|
+
object = other.dup
|
176
|
+
|
177
|
+
fraction.to_improper!
|
178
|
+
object.to_improper!
|
179
|
+
|
180
|
+
negative_counter = 0
|
181
|
+
negative_counter += 1 if fraction.negative?
|
182
|
+
negative_counter += 1 if object.negative?
|
183
|
+
fraction.negative = negative_counter.even? ? false : true
|
184
|
+
|
185
|
+
fraction.numerator *= object.numerator
|
186
|
+
fraction.denominator *= object.denominator
|
187
|
+
|
188
|
+
fraction.simplify!
|
189
|
+
|
190
|
+
fraction.whole_part = 1 if fraction.zero?
|
191
|
+
|
192
|
+
fraction
|
193
|
+
end
|
194
|
+
|
195
|
+
def /(other)
|
196
|
+
fraction = dup
|
197
|
+
object = other.dup
|
198
|
+
|
199
|
+
object.to_reciprocal!
|
200
|
+
fraction *= object
|
201
|
+
|
202
|
+
fraction
|
203
|
+
end
|
204
|
+
|
205
|
+
def **(other)
|
206
|
+
if other.is_a? Numeric
|
207
|
+
power(other)
|
208
|
+
elsif other.is_a? Fractify::Fraction
|
209
|
+
x = other.to_f
|
210
|
+
power(x)
|
211
|
+
else
|
212
|
+
raise IncorrectArgumentsError
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def self.valid?(string)
|
217
|
+
return false unless string.is_a? String
|
218
|
+
|
219
|
+
validate_fraction_string(string)
|
220
|
+
end
|
221
|
+
|
222
|
+
def self.floating_point_part(number)
|
223
|
+
raise IncorrectArgumentsError unless number.is_a? Numeric
|
224
|
+
|
225
|
+
result = '0.'
|
226
|
+
number_string = number.to_s
|
227
|
+
past_decimal_separator = false
|
228
|
+
|
229
|
+
number_string.each_char do |c|
|
230
|
+
if past_decimal_separator
|
231
|
+
result += c
|
232
|
+
elsif c == '.'
|
233
|
+
past_decimal_separator = true
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
result.to_f
|
238
|
+
end
|
239
|
+
|
240
|
+
def self.least_common_multiple(first, second)
|
241
|
+
raise IncorrectArgumentsError unless first.is_a?(Numeric) && second.is_a?(Numeric)
|
242
|
+
|
243
|
+
(first * second) / greatest_common_divisor(first, second)
|
244
|
+
end
|
245
|
+
|
246
|
+
def self.greatest_common_divisor(first, second)
|
247
|
+
first = first.abs
|
248
|
+
second = second.abs
|
249
|
+
|
250
|
+
while first != second
|
251
|
+
if first > second
|
252
|
+
first -= second
|
253
|
+
else
|
254
|
+
second -= first
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
first
|
259
|
+
end
|
260
|
+
|
261
|
+
def self.numeric?(char)
|
262
|
+
char =~ /[[:digit:]]/
|
263
|
+
end
|
264
|
+
|
265
|
+
def self.letter?(char)
|
266
|
+
char =~ /[[:alpha:]]/
|
267
|
+
end
|
268
|
+
|
269
|
+
def self.validate_fraction_string(string)
|
270
|
+
is_float = false
|
271
|
+
string.each_char do |c|
|
272
|
+
if c == '.'
|
273
|
+
is_float = true
|
274
|
+
break
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
if is_float
|
279
|
+
validate_float_string(string)
|
280
|
+
else
|
281
|
+
validate_string(string)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
def self.validate_string(string)
|
286
|
+
stage = 0
|
287
|
+
open_bracket, incorrect_syntax, at_end, digit_present = false
|
288
|
+
minus_is_free = true
|
289
|
+
|
290
|
+
string.each_char do |c|
|
291
|
+
if at_end || letter?(c)
|
292
|
+
incorrect_syntax = true
|
293
|
+
break
|
294
|
+
end
|
295
|
+
|
296
|
+
if open_bracket
|
297
|
+
if stage.zero?
|
298
|
+
if c == '-' && minus_is_free
|
299
|
+
minus_is_free = false
|
300
|
+
elsif numeric?(c)
|
301
|
+
digit_present = true
|
302
|
+
elsif c == ' ' && digit_present
|
303
|
+
stage = 1
|
304
|
+
minus_is_free = true
|
305
|
+
digit_present = false
|
306
|
+
elsif c == '/' && digit_present
|
307
|
+
stage = 2
|
308
|
+
minus_is_free = true
|
309
|
+
digit_present = false
|
310
|
+
elsif c == ')' && digit_present
|
311
|
+
else
|
312
|
+
incorrect_syntax = true
|
313
|
+
break
|
314
|
+
end
|
315
|
+
elsif stage == 1
|
316
|
+
if c == '-' && minus_is_free
|
317
|
+
minus_is_free = false
|
318
|
+
elsif numeric?(c)
|
319
|
+
digit_present = true
|
320
|
+
elsif c == '/' && digit_present
|
321
|
+
stage = 2
|
322
|
+
minus_is_free = true
|
323
|
+
digit_present
|
324
|
+
else
|
325
|
+
incorrect_syntax = true
|
326
|
+
break
|
327
|
+
end
|
328
|
+
elsif stage == 2
|
329
|
+
if c == '-' && minus_is_free
|
330
|
+
minus_is_free = false
|
331
|
+
elsif numeric?(c)
|
332
|
+
digit_present = true
|
333
|
+
elsif c == ')' && digit_present
|
334
|
+
else
|
335
|
+
incorrect_syntax = true
|
336
|
+
break
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
if c == '('
|
342
|
+
if open_bracket
|
343
|
+
incorrect_syntax = true
|
344
|
+
break
|
345
|
+
end
|
346
|
+
open_bracket = true
|
347
|
+
elsif c == ')'
|
348
|
+
unless open_bracket
|
349
|
+
incorrect_syntax = true
|
350
|
+
break
|
351
|
+
end
|
352
|
+
open_bracket = false
|
353
|
+
at_end = true
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
incorrect_syntax = true if open_bracket
|
358
|
+
|
359
|
+
!incorrect_syntax
|
360
|
+
end
|
361
|
+
|
362
|
+
def self.validate_float_string(string)
|
363
|
+
open_bracket, incorrect_syntax, at_end, digit_present,
|
364
|
+
floating_point_present = false
|
365
|
+
minus_is_free = true
|
366
|
+
|
367
|
+
string.each_char do |c|
|
368
|
+
if at_end || letter?(c)
|
369
|
+
incorrect_syntax = true
|
370
|
+
break
|
371
|
+
end
|
372
|
+
|
373
|
+
if open_bracket
|
374
|
+
if c == '-'
|
375
|
+
if !minus_is_free || digit_present
|
376
|
+
incorrect_syntax = true
|
377
|
+
break
|
378
|
+
end
|
379
|
+
minus_is_free = false
|
380
|
+
elsif numeric?(c)
|
381
|
+
digit_present = true
|
382
|
+
elsif c == '.'
|
383
|
+
if floating_point_present || !digit_present
|
384
|
+
incorrect_syntax = true
|
385
|
+
break
|
386
|
+
end
|
387
|
+
floating_point_present = true
|
388
|
+
elsif c == ')'
|
389
|
+
open_bracket = false
|
390
|
+
at_end = true
|
391
|
+
else
|
392
|
+
incorrect_syntax = true
|
393
|
+
break
|
394
|
+
end
|
395
|
+
elsif c == '('
|
396
|
+
open_bracket = true
|
397
|
+
else
|
398
|
+
incorrect_syntax = true
|
399
|
+
break
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
incorrect_syntax = true if open_bracket
|
404
|
+
|
405
|
+
!incorrect_syntax
|
406
|
+
end
|
407
|
+
|
408
|
+
def to_common_denominator!(other)
|
409
|
+
to_improper!
|
410
|
+
other.to_improper!
|
411
|
+
|
412
|
+
least_common_multiple = self.class.least_common_multiple(denominator, other.denominator)
|
413
|
+
|
414
|
+
numerator_multiplication = least_common_multiple / denominator
|
415
|
+
@numerator *= numerator_multiplication
|
416
|
+
@denominator = least_common_multiple
|
417
|
+
|
418
|
+
numerator_multiplication = least_common_multiple / other.denominator
|
419
|
+
other.numerator *= numerator_multiplication
|
420
|
+
other.denominator = least_common_multiple
|
421
|
+
end
|
422
|
+
|
423
|
+
def to_reciprocal!
|
424
|
+
to_improper!
|
425
|
+
aux = denominator
|
426
|
+
@denominator = numerator
|
427
|
+
@numerator = aux
|
428
|
+
end
|
429
|
+
|
430
|
+
protected
|
431
|
+
|
432
|
+
def minus_to_numerator!
|
433
|
+
return unless negative
|
434
|
+
|
435
|
+
@negative = false
|
436
|
+
@numerator = -numerator
|
437
|
+
end
|
438
|
+
|
439
|
+
def minus_to_negative_field!
|
440
|
+
return unless numerator.negative?
|
441
|
+
|
442
|
+
@negative = true
|
443
|
+
@numerator = numerator.abs
|
444
|
+
end
|
445
|
+
|
446
|
+
private
|
447
|
+
|
448
|
+
def power(x)
|
449
|
+
fraction = dup
|
450
|
+
fraction.negative = false if x % 2 == 0
|
451
|
+
|
452
|
+
if x.negative?
|
453
|
+
fraction.to_reciprocal!
|
454
|
+
x = x.abs
|
455
|
+
else
|
456
|
+
fraction.to_improper!
|
457
|
+
end
|
458
|
+
|
459
|
+
if x == x.to_i
|
460
|
+
fraction.numerator **= x.to_i
|
461
|
+
fraction.denominator **= x.to_i
|
462
|
+
fraction.simplify!
|
463
|
+
else
|
464
|
+
float = fraction.to_f.abs
|
465
|
+
float **= x
|
466
|
+
float = -float if fraction.negative?
|
467
|
+
fraction = Fractify::Fraction.new(number: float)
|
468
|
+
end
|
469
|
+
|
470
|
+
fraction
|
471
|
+
end
|
472
|
+
|
473
|
+
def fix_negativity!
|
474
|
+
@negative = !negative if arguments_make_negative?
|
475
|
+
|
476
|
+
@whole_part = whole_part.abs
|
477
|
+
@numerator = numerator.abs
|
478
|
+
@denominator = denominator.abs
|
479
|
+
end
|
480
|
+
|
481
|
+
def arguments_make_negative?
|
482
|
+
negative_attributes = 0
|
483
|
+
%w[whole_part numerator denominator].each do |a|
|
484
|
+
negative_attributes += 1 if send(a).negative?
|
485
|
+
end
|
486
|
+
return true if negative_attributes.odd?
|
487
|
+
|
488
|
+
false
|
489
|
+
end
|
490
|
+
|
491
|
+
def parse_fraction_string!(string)
|
492
|
+
is_float = false
|
493
|
+
string.each_char do |c|
|
494
|
+
if c == '.'
|
495
|
+
is_float = true
|
496
|
+
break
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
if is_float
|
501
|
+
parse_float_string!(string)
|
502
|
+
else
|
503
|
+
parse_string!(string)
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
def parse_string!(string)
|
508
|
+
stage = 0
|
509
|
+
number_string = ''
|
510
|
+
|
511
|
+
open_bracket, incorrect_syntax, at_end, digit_present = false
|
512
|
+
|
513
|
+
minus_is_free = true
|
514
|
+
|
515
|
+
string.each_char do |c|
|
516
|
+
if at_end || self.class.letter?(c)
|
517
|
+
incorrect_syntax = true
|
518
|
+
break
|
519
|
+
end
|
520
|
+
|
521
|
+
if open_bracket
|
522
|
+
if stage.zero?
|
523
|
+
if c == '-' && minus_is_free
|
524
|
+
@negative = true
|
525
|
+
minus_is_free = false
|
526
|
+
elsif self.class.numeric?(c)
|
527
|
+
number_string += c
|
528
|
+
digit_present = true
|
529
|
+
elsif c == ' ' && digit_present
|
530
|
+
stage = 1
|
531
|
+
@whole_part = number_string.to_i
|
532
|
+
number_string = ''
|
533
|
+
minus_is_free = true
|
534
|
+
digit_present = false
|
535
|
+
elsif c == '/' && digit_present
|
536
|
+
stage = 2
|
537
|
+
@numerator = number_string.to_i
|
538
|
+
number_string = ''
|
539
|
+
minus_is_free = true
|
540
|
+
digit_present = false
|
541
|
+
elsif c == ')' && digit_present
|
542
|
+
@numerator = number_string.to_i
|
543
|
+
@denominator = 1
|
544
|
+
else
|
545
|
+
incorrect_syntax = true
|
546
|
+
break
|
547
|
+
end
|
548
|
+
elsif stage == 1
|
549
|
+
if c == '-' && minus_is_free
|
550
|
+
@negative = !negative
|
551
|
+
minus_is_free = false
|
552
|
+
elsif self.class.numeric?(c)
|
553
|
+
number_string += c
|
554
|
+
digit_present = true
|
555
|
+
elsif c == '/' && digit_present
|
556
|
+
stage = 2
|
557
|
+
@numerator = number_string.to_i
|
558
|
+
number_string = ''
|
559
|
+
minus_is_free = true
|
560
|
+
digit_present
|
561
|
+
else
|
562
|
+
incorrect_syntax = true
|
563
|
+
break
|
564
|
+
end
|
565
|
+
elsif stage == 2
|
566
|
+
if c == '-' && minus_is_free
|
567
|
+
@negative = !negative
|
568
|
+
minus_is_free = false
|
569
|
+
elsif self.class.numeric?(c)
|
570
|
+
number_string += c
|
571
|
+
digit_present = true
|
572
|
+
elsif c == ')' && digit_present
|
573
|
+
@denominator = number_string.to_i
|
574
|
+
else
|
575
|
+
incorrect_syntax = true
|
576
|
+
break
|
577
|
+
end
|
578
|
+
end
|
579
|
+
end
|
580
|
+
|
581
|
+
if c == '('
|
582
|
+
if open_bracket
|
583
|
+
incorrect_syntax = true
|
584
|
+
break
|
585
|
+
end
|
586
|
+
open_bracket = true
|
587
|
+
elsif c == ')'
|
588
|
+
unless open_bracket
|
589
|
+
incorrect_syntax = true
|
590
|
+
break
|
591
|
+
end
|
592
|
+
open_bracket = false
|
593
|
+
at_end = true
|
594
|
+
end
|
595
|
+
end
|
596
|
+
|
597
|
+
raise IncorrectStringSyntax if incorrect_syntax || open_bracket
|
598
|
+
|
599
|
+
simplify!
|
600
|
+
end
|
601
|
+
|
602
|
+
def parse_float_string!(string)
|
603
|
+
number_string = ''
|
604
|
+
open_bracket, incorrect_syntax, at_end, digit_present,
|
605
|
+
floating_point_present = false
|
606
|
+
|
607
|
+
minus_is_free = true
|
608
|
+
|
609
|
+
string.each_char do |c|
|
610
|
+
if at_end || self.class.letter?(c)
|
611
|
+
incorrect_syntax = true
|
612
|
+
break
|
613
|
+
end
|
614
|
+
|
615
|
+
if open_bracket
|
616
|
+
if c == '-'
|
617
|
+
if !minus_is_free || digit_present
|
618
|
+
incorrect_syntax = true
|
619
|
+
break
|
620
|
+
end
|
621
|
+
minus_is_free = false
|
622
|
+
number_string += c
|
623
|
+
elsif self.class.numeric?(c)
|
624
|
+
digit_present = true
|
625
|
+
number_string += c
|
626
|
+
elsif c == '.'
|
627
|
+
if floating_point_present || !digit_present
|
628
|
+
incorrect_syntax = true
|
629
|
+
break
|
630
|
+
end
|
631
|
+
floating_point_present = true
|
632
|
+
number_string += c
|
633
|
+
elsif c == ')'
|
634
|
+
open_bracket = false
|
635
|
+
at_end = true
|
636
|
+
else
|
637
|
+
incorrect_syntax = true
|
638
|
+
break
|
639
|
+
end
|
640
|
+
elsif c == '('
|
641
|
+
open_bracket = true
|
642
|
+
else
|
643
|
+
incorrect_syntax = true
|
644
|
+
break
|
645
|
+
end
|
646
|
+
end
|
647
|
+
|
648
|
+
raise IncorrectStringSyntax if open_bracket || incorrect_syntax
|
649
|
+
|
650
|
+
convert_number!(number_string.to_f)
|
651
|
+
end
|
652
|
+
|
653
|
+
def convert_number!(number)
|
654
|
+
if number.zero?
|
655
|
+
to_zero!
|
656
|
+
return
|
657
|
+
end
|
658
|
+
|
659
|
+
if number.negative?
|
660
|
+
number = number.abs
|
661
|
+
@negative = true
|
662
|
+
end
|
663
|
+
|
664
|
+
@whole_part = number.to_i
|
665
|
+
new_denominator = 1
|
666
|
+
floating_point_part = self.class.floating_point_part(number)
|
667
|
+
|
668
|
+
while floating_point_part.positive?
|
669
|
+
floating_point_part *= new_denominator
|
670
|
+
floating_point_part = self.class.floating_point_part(floating_point_part)
|
671
|
+
new_denominator *= 10
|
672
|
+
end
|
673
|
+
|
674
|
+
@numerator = self.class.floating_point_part(number) * new_denominator
|
675
|
+
@numerator = numerator.to_i
|
676
|
+
@denominator = new_denominator
|
677
|
+
|
678
|
+
@numerator = 1 if numerator.zero?
|
679
|
+
|
680
|
+
simplify!
|
681
|
+
end
|
682
|
+
end
|
683
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fractify
|
4
|
+
class IncorrectArgumentsError < StandardError; end
|
5
|
+
class Operator
|
6
|
+
attr_accessor :operator_character, :rank, :executed, :to_left, :to_right
|
7
|
+
|
8
|
+
def initialize(operator_character, rank, to_left = nil, to_right = nil)
|
9
|
+
if to_left && !to_left.is_a?(Fractify::Fraction) || to_right && !to_right.is_a?(Fractify::Fraction)
|
10
|
+
raise IncorrectArgumentsError
|
11
|
+
end
|
12
|
+
|
13
|
+
self.operator_character = operator_character
|
14
|
+
self.rank = rank
|
15
|
+
self.to_left = to_left
|
16
|
+
self.to_right = to_right
|
17
|
+
self.executed = false
|
18
|
+
end
|
19
|
+
|
20
|
+
def <=>(other)
|
21
|
+
raise IncorrectArgumentsError unless other.is_a? Fractify::Operator
|
22
|
+
return 0 if rank == other.rank
|
23
|
+
return 1 if rank > other.rank
|
24
|
+
|
25
|
+
-1
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
"(#{operator_character}, #{rank})"
|
30
|
+
end
|
31
|
+
|
32
|
+
def decrease_rank!(number = 1)
|
33
|
+
@rank -= number
|
34
|
+
end
|
35
|
+
|
36
|
+
def increase_rank!(number = 1)
|
37
|
+
@rank += number
|
38
|
+
end
|
39
|
+
|
40
|
+
def executed!
|
41
|
+
self.executed = true
|
42
|
+
end
|
43
|
+
|
44
|
+
def executed?
|
45
|
+
executed
|
46
|
+
end
|
47
|
+
|
48
|
+
def not_executed?
|
49
|
+
!executed
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|