numerals 0.0.0 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +149 -5
- data/lib/numerals/conversions/bigdecimal.rb +209 -9
- data/lib/numerals/conversions/context_conversion.rb +40 -0
- data/lib/numerals/conversions/float.rb +106 -71
- data/lib/numerals/conversions/flt.rb +115 -44
- data/lib/numerals/conversions/integer.rb +32 -3
- data/lib/numerals/conversions/rational.rb +27 -3
- data/lib/numerals/conversions.rb +74 -33
- data/lib/numerals/digits.rb +8 -5
- data/lib/numerals/format/base_scaler.rb +160 -0
- data/lib/numerals/format/exp_setter.rb +218 -0
- data/lib/numerals/format/format.rb +257 -0
- data/lib/numerals/format/input.rb +140 -0
- data/lib/numerals/format/mode.rb +157 -0
- data/lib/numerals/format/notation.rb +51 -0
- data/lib/numerals/format/notations/html.rb +53 -0
- data/lib/numerals/format/notations/latex.rb +48 -0
- data/lib/numerals/format/notations/text.rb +141 -0
- data/lib/numerals/format/output.rb +167 -0
- data/lib/numerals/format/symbols.rb +565 -0
- data/lib/numerals/format/text_parts.rb +35 -0
- data/lib/numerals/format.rb +25 -0
- data/lib/numerals/formatting_aspect.rb +36 -0
- data/lib/numerals/numeral.rb +34 -21
- data/lib/numerals/repeat_detector.rb +99 -0
- data/lib/numerals/rounding.rb +340 -181
- data/lib/numerals/version.rb +1 -1
- data/lib/numerals.rb +4 -2
- data/numerals.gemspec +1 -1
- data/test/test_base_scaler.rb +189 -0
- data/test/test_big_conversions.rb +105 -0
- data/test/test_digits_definition.rb +23 -28
- data/test/test_exp_setter.rb +732 -0
- data/test/test_float_conversions.rb +48 -30
- data/test/test_flt_conversions.rb +476 -80
- data/test/test_format.rb +124 -0
- data/test/test_format_input.rb +226 -0
- data/test/test_format_mode.rb +124 -0
- data/test/test_format_output.rb +789 -0
- data/test/test_integer_conversions.rb +22 -22
- data/test/test_numeral.rb +35 -0
- data/test/test_rational_conversions.rb +28 -28
- data/test/test_repeat_detector.rb +72 -0
- data/test/test_rounding.rb +158 -0
- data/test/test_symbols.rb +32 -0
- metadata +38 -5
- data/lib/numerals/formatting/digits_definition.rb +0 -75
data/lib/numerals/numeral.rb
CHANGED
@@ -22,15 +22,15 @@ module Numerals
|
|
22
22
|
#
|
23
23
|
# A Numeral is equivalent to a Rational number; a quotient of integers
|
24
24
|
# can be converted to a Numeral in any base and back to a quotient without
|
25
|
-
# altering its value (
|
25
|
+
# altering its value (although the fraction might be simplified).
|
26
26
|
#
|
27
27
|
# By default a Numeral represents an exact quantity (rational number).
|
28
|
-
# A numeral can
|
29
|
-
# precision
|
28
|
+
# A numeral can also represent an approximate value with a specific
|
29
|
+
# precision: the number of significant digits (numeral.digits.size),
|
30
30
|
# which can include significant trailing zeros. Approximate numerals
|
31
31
|
# are never repeating.
|
32
32
|
#
|
33
|
-
# Exact numerals are always repeating, but when the repeating digits are
|
33
|
+
# Exact numerals are always repeating, but, when the repeating digits are
|
34
34
|
# just zeros, the repeating? method returns false.
|
35
35
|
#
|
36
36
|
class Numeral
|
@@ -44,24 +44,25 @@ module Numerals
|
|
44
44
|
def self.maximum_number_of_digits=(n)
|
45
45
|
@maximum_number_of_digits = [n, 2048].max
|
46
46
|
end
|
47
|
+
|
47
48
|
# Return the maximum number of digits that Numeral objects
|
48
49
|
# can handle.
|
49
50
|
def self.maximum_number_of_digits
|
50
51
|
@maximum_number_of_digits
|
51
52
|
end
|
52
53
|
|
53
|
-
# Special
|
54
|
-
# :negative_infinity, :positive_infinity
|
55
|
-
# :sign option which should be either +1 or -1
|
54
|
+
# Special numerals may be constructed with the symbols :nan, :infinity,
|
55
|
+
# :negative_infinity, :positive_infinity (or with :infinity and the
|
56
|
+
# :sign option which should be either +1 or -1)
|
56
57
|
#
|
57
58
|
# Examples:
|
58
59
|
#
|
59
60
|
# Numeral[:nan]
|
60
61
|
# Numeral[:infinity, sign: -1]
|
61
62
|
#
|
62
|
-
# For
|
63
|
-
# an Array of digits, and the
|
64
|
-
# :repeat) are passed as options.
|
63
|
+
# For non-special numerals, the first argument may be a Digits object or
|
64
|
+
# an Array of digits, and the remaining parameters (:base, :sign, :point
|
65
|
+
# and :repeat) are passed as options.
|
65
66
|
#
|
66
67
|
# Examples:
|
67
68
|
#
|
@@ -69,10 +70,10 @@ module Numerals
|
|
69
70
|
# Numeral[1,2,3,4, point: 1, repeat: 2] # 1.234343434...
|
70
71
|
#
|
71
72
|
# The :normalize option can be used to specify the kind of normalization
|
72
|
-
# applied to the numeral:
|
73
|
+
# to be applied to the numeral:
|
73
74
|
#
|
74
|
-
# * :exact, the default produces a normalized :exact number
|
75
|
-
# where no trailing zeros are kept and there
|
75
|
+
# * :exact, the default, produces a normalized :exact number
|
76
|
+
# where no trailing zeros are kept and there is always a repeat point
|
76
77
|
# (which may just repeat trailing zeros)
|
77
78
|
# * :approximate produces a non-repeating numeral with a fixed number of
|
78
79
|
# digits (where trailing zeros are significant)
|
@@ -213,7 +214,8 @@ module Numerals
|
|
213
214
|
else
|
214
215
|
repeated_length = @digits.size - @repeat
|
215
216
|
i = (i - @repeat) % repeated_length
|
216
|
-
|
217
|
+
i += @repeat
|
218
|
+
i < 0 ? 0 : @digits[i]
|
217
219
|
end
|
218
220
|
end
|
219
221
|
|
@@ -249,6 +251,16 @@ module Numerals
|
|
249
251
|
@digits.replace @digits[0...-rep_length]
|
250
252
|
end
|
251
253
|
end
|
254
|
+
# remove unneeded partial repetitions
|
255
|
+
if rep_length > 0 && @digits.size > rep_length
|
256
|
+
removed = 0
|
257
|
+
while @repeat > 0 && @digits[@repeat-1] == @digits[@repeat-1+rep_length]
|
258
|
+
@repeat -= 1
|
259
|
+
removed += 1
|
260
|
+
end
|
261
|
+
@digits.replace @digits[0...-removed] if removed > 0
|
262
|
+
end
|
263
|
+
|
252
264
|
end
|
253
265
|
|
254
266
|
# Replace 'nines' repetition 0.999... -> 1
|
@@ -313,7 +325,6 @@ module Numerals
|
|
313
325
|
end
|
314
326
|
end
|
315
327
|
end
|
316
|
-
|
317
328
|
self
|
318
329
|
end
|
319
330
|
|
@@ -383,9 +394,10 @@ module Numerals
|
|
383
394
|
Numeral[digits, sign: sign]
|
384
395
|
end
|
385
396
|
|
386
|
-
# Create a Numeral from a quotient (Rational number)
|
387
|
-
# The quotient can be passed as an Array,
|
388
|
-
#
|
397
|
+
# Create a Numeral from a quotient (Rational number).
|
398
|
+
# The quotient can be passed as an Array [numerator, denomnator];
|
399
|
+
# to allow fractions with a zero denominator
|
400
|
+
# (representing indefinite or infinite numbers).
|
389
401
|
def self.from_quotient(*args)
|
390
402
|
r = args.shift
|
391
403
|
if Integer === args.first
|
@@ -451,7 +463,7 @@ module Numerals
|
|
451
463
|
|
452
464
|
# Return a quotient (Rational) that represents the exact value of the numeral.
|
453
465
|
# The quotient is returned as an Array, so that fractions with a zero denominator
|
454
|
-
# can be handled (
|
466
|
+
# can be handled (representing indefinite or infinite numbers).
|
455
467
|
def to_quotient
|
456
468
|
if @special
|
457
469
|
y = 0
|
@@ -503,7 +515,7 @@ module Numerals
|
|
503
515
|
else
|
504
516
|
sign = +1
|
505
517
|
end
|
506
|
-
digits = Digits[radix]
|
518
|
+
digits = Digits[base: radix]
|
507
519
|
digits.value = coefficient
|
508
520
|
point = scale + digits.size
|
509
521
|
normalization = options[:normalize] || :exact
|
@@ -564,6 +576,7 @@ module Numerals
|
|
564
576
|
'Numeral[:inf]'
|
565
577
|
end
|
566
578
|
else
|
579
|
+
args = ''
|
567
580
|
if @digits.size > 0
|
568
581
|
args = @digits.digits_array.to_s.unwrap('[]')
|
569
582
|
args << ', '
|
@@ -581,7 +594,7 @@ module Numerals
|
|
581
594
|
end
|
582
595
|
|
583
596
|
# An exact Numeral represents exactly a rational number.
|
584
|
-
# It always has a repeat position,
|
597
|
+
# It always has a repeat position, although the repeated digits
|
585
598
|
# may all be zero.
|
586
599
|
def exact?
|
587
600
|
!!@repeat
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module RepeatDetector
|
2
|
+
|
3
|
+
# detec digitss repetitions
|
4
|
+
def self.detect(digits, min_repetitions=1)
|
5
|
+
n = min_repetitions + 1
|
6
|
+
for l in 1..(digits.size/n)
|
7
|
+
l = digits.size/n + 1 - l
|
8
|
+
# l loops from digits.size/n to 1
|
9
|
+
# l is the length of the trailing repetition to be check
|
10
|
+
# the longest possible repetition to occur at least n times
|
11
|
+
# is 1/n of the digits,
|
12
|
+
# and the shortest is 1 digit long.
|
13
|
+
# Longest repetitions are found first.
|
14
|
+
|
15
|
+
# check the last l digits for n repetitions
|
16
|
+
repeating = true
|
17
|
+
(2..n).each do |i|
|
18
|
+
if digits[-l..-1] != digits[-i*l...-(i-1)*l]
|
19
|
+
repeating = false
|
20
|
+
break
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
if repeating
|
25
|
+
# found n-1 repetitions of length l;
|
26
|
+
# remove them:
|
27
|
+
digits = digits[0...-(n-1)*l]
|
28
|
+
# keap index of repetition beginning
|
29
|
+
repeat = digits.size - l
|
30
|
+
|
31
|
+
# now iterate over the divisors of l
|
32
|
+
# to find sub-repetitions
|
33
|
+
for m in 1..l
|
34
|
+
if l.modulo(m) == 0
|
35
|
+
# m is a divisor of l
|
36
|
+
# check if the l-digit repeating sequence
|
37
|
+
# is composed of m-digit sub-repetitions
|
38
|
+
reduce_l = true
|
39
|
+
for i in 2..l/m
|
40
|
+
if digits[-m..-1] != digits[-i*m...-i*m+m]
|
41
|
+
reduce_l = false
|
42
|
+
break
|
43
|
+
end
|
44
|
+
end
|
45
|
+
if reduce_l
|
46
|
+
# found an m-digit sub-repetition
|
47
|
+
l = m
|
48
|
+
repeat = digits.size - l
|
49
|
+
break
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# now remove innecesary repetitions
|
55
|
+
while digits.size >= 2*l && digits[-l..-1] == digits[-2*l...-l]
|
56
|
+
repeat -= l
|
57
|
+
digits = digits[0...-l]
|
58
|
+
end
|
59
|
+
|
60
|
+
break
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
if repeat
|
65
|
+
# remove zero repetition
|
66
|
+
if digits.size == repeat+1 && digits[repeat]==0
|
67
|
+
repeat = nil
|
68
|
+
digits.pop
|
69
|
+
end
|
70
|
+
end
|
71
|
+
# and remove trailing zeros that may be exposed
|
72
|
+
# TODO: review: this may not be needed
|
73
|
+
digits.pop while digits[-1]==0 && !digits.empty?
|
74
|
+
|
75
|
+
[digits, repeat]
|
76
|
+
end
|
77
|
+
|
78
|
+
# Compute the miniumum number of times the repeated part must
|
79
|
+
# apper in a digit sequence so that it is correctly detected
|
80
|
+
# with detect_min_rep repeatitions
|
81
|
+
def self.min_repeat_count(digits, repeat, detect_min_rep=1)
|
82
|
+
if repeat
|
83
|
+
n = detect_min_rep
|
84
|
+
loop do
|
85
|
+
n += 1
|
86
|
+
detected_digits, detected_repeat = detect(
|
87
|
+
digits + digits[repeat..-1]*(n-1), detect_min_rep
|
88
|
+
)
|
89
|
+
if repeat == detected_repeat # && detected_digits == digits
|
90
|
+
break
|
91
|
+
end
|
92
|
+
end
|
93
|
+
n
|
94
|
+
else
|
95
|
+
0
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|