numerals 0.0.0 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +149 -5
  3. data/lib/numerals/conversions/bigdecimal.rb +209 -9
  4. data/lib/numerals/conversions/context_conversion.rb +40 -0
  5. data/lib/numerals/conversions/float.rb +106 -71
  6. data/lib/numerals/conversions/flt.rb +115 -44
  7. data/lib/numerals/conversions/integer.rb +32 -3
  8. data/lib/numerals/conversions/rational.rb +27 -3
  9. data/lib/numerals/conversions.rb +74 -33
  10. data/lib/numerals/digits.rb +8 -5
  11. data/lib/numerals/format/base_scaler.rb +160 -0
  12. data/lib/numerals/format/exp_setter.rb +218 -0
  13. data/lib/numerals/format/format.rb +257 -0
  14. data/lib/numerals/format/input.rb +140 -0
  15. data/lib/numerals/format/mode.rb +157 -0
  16. data/lib/numerals/format/notation.rb +51 -0
  17. data/lib/numerals/format/notations/html.rb +53 -0
  18. data/lib/numerals/format/notations/latex.rb +48 -0
  19. data/lib/numerals/format/notations/text.rb +141 -0
  20. data/lib/numerals/format/output.rb +167 -0
  21. data/lib/numerals/format/symbols.rb +565 -0
  22. data/lib/numerals/format/text_parts.rb +35 -0
  23. data/lib/numerals/format.rb +25 -0
  24. data/lib/numerals/formatting_aspect.rb +36 -0
  25. data/lib/numerals/numeral.rb +34 -21
  26. data/lib/numerals/repeat_detector.rb +99 -0
  27. data/lib/numerals/rounding.rb +340 -181
  28. data/lib/numerals/version.rb +1 -1
  29. data/lib/numerals.rb +4 -2
  30. data/numerals.gemspec +1 -1
  31. data/test/test_base_scaler.rb +189 -0
  32. data/test/test_big_conversions.rb +105 -0
  33. data/test/test_digits_definition.rb +23 -28
  34. data/test/test_exp_setter.rb +732 -0
  35. data/test/test_float_conversions.rb +48 -30
  36. data/test/test_flt_conversions.rb +476 -80
  37. data/test/test_format.rb +124 -0
  38. data/test/test_format_input.rb +226 -0
  39. data/test/test_format_mode.rb +124 -0
  40. data/test/test_format_output.rb +789 -0
  41. data/test/test_integer_conversions.rb +22 -22
  42. data/test/test_numeral.rb +35 -0
  43. data/test/test_rational_conversions.rb +28 -28
  44. data/test/test_repeat_detector.rb +72 -0
  45. data/test/test_rounding.rb +158 -0
  46. data/test/test_symbols.rb +32 -0
  47. metadata +38 -5
  48. data/lib/numerals/formatting/digits_definition.rb +0 -75
@@ -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 (though the fraction might be simplified).
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 alo represent an approximate value given with a certain
29
- # precision, the number of significant digits (numeral.digits.size)
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 nomerals may be contructed with the symbols :nan, :infinity,
54
- # :negative_infinity, :positive_infinity. Or also with :infinity and the
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 nonspecial numerals, the first argument may be a Digits object or
63
- # an Array of digits, and the rest parameters (:base, :sign, :point and
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 are always a repeat point
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
- @digits[i + @repeat]
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, so that fractions with a zero denominator
388
- # can be handled (represented indefinite or infinite numbers).
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 (represented indefinite or infinite numbers).
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, althugh the repeated digits
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