numerals 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Gemfile +3 -0
- data/LICENSE +22 -0
- data/README.md +24 -0
- data/Rakefile +19 -0
- data/lib/numerals/conversions/bigdecimal.rb +30 -0
- data/lib/numerals/conversions/float.rb +226 -0
- data/lib/numerals/conversions/flt.rb +162 -0
- data/lib/numerals/conversions/integer.rb +39 -0
- data/lib/numerals/conversions/rational.rb +32 -0
- data/lib/numerals/conversions.rb +57 -0
- data/lib/numerals/digits.rb +99 -0
- data/lib/numerals/formatting/digits_definition.rb +75 -0
- data/lib/numerals/formatting/options.rb +84 -0
- data/lib/numerals/numeral.rb +650 -0
- data/lib/numerals/rounding.rb +229 -0
- data/lib/numerals/support.rb +10 -0
- data/lib/numerals/version.rb +3 -0
- data/lib/numerals.rb +12 -0
- data/numerals.gemspec +26 -0
- data/test/data.yaml +101 -0
- data/test/helper.rb +40 -0
- data/test/test_digits_definition.rb +110 -0
- data/test/test_float_conversions.rb +58 -0
- data/test/test_flt_conversions.rb +277 -0
- data/test/test_integer_conversions.rb +50 -0
- data/test/test_numeral.rb +366 -0
- data/test/test_rational_conversions.rb +75 -0
- data/test/test_rounding.rb +77 -0
- metadata +138 -0
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Numerals
|
4
|
+
|
5
|
+
# Sequence of digit values type, with an Array-compatible interface
|
6
|
+
# Having this encapsulated here allow to change the implementation
|
7
|
+
# e.g. to an Integer or packed in a String, ...
|
8
|
+
class Digits
|
9
|
+
def initialize(*args)
|
10
|
+
if Hash === args.last
|
11
|
+
options = args.pop
|
12
|
+
else
|
13
|
+
options = {}
|
14
|
+
end
|
15
|
+
@radix = options[:base] || options[:radix] || 10
|
16
|
+
if args.size == 1 && Array === args.first
|
17
|
+
@digits_array = args.first
|
18
|
+
else
|
19
|
+
@digits_array = args
|
20
|
+
end
|
21
|
+
if options[:value]
|
22
|
+
self.value = options[:value]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
include ModalSupport::BracketConstructor
|
27
|
+
|
28
|
+
attr_reader :digits_array, :radix
|
29
|
+
|
30
|
+
extend Forwardable
|
31
|
+
def_delegators :@digits_array,
|
32
|
+
:size, :map, :pop, :push, :shift, :unshift,
|
33
|
+
:empty?, :first, :last, :any?, :all?, :[]=
|
34
|
+
|
35
|
+
# The [] with a Range argument or two arguments (index, length)
|
36
|
+
# returns a Regular Array.
|
37
|
+
def_delegators :@digits_array, :[], :replace
|
38
|
+
include ModalSupport::StateEquivalent # maybe == with Arrays too?
|
39
|
+
|
40
|
+
# This could be changed to have [] return a Digits object (except when a single index is passed).
|
41
|
+
# In that case we would have to define replace, ==, != to accept either Array or Digits arguments
|
42
|
+
# (and also possibly the constructor)
|
43
|
+
|
44
|
+
# Integral coefficient
|
45
|
+
def value
|
46
|
+
if @radix == 10
|
47
|
+
@digits_array.join.to_i
|
48
|
+
else
|
49
|
+
@digits_array.inject(0){|x,y| x*@radix + y}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def value=(v)
|
54
|
+
raise "Invalid digits value" if v < 0
|
55
|
+
if @radix < 37
|
56
|
+
replace v.to_s(@radix).each_char.map{|c| c.to_i(@radix)}
|
57
|
+
else
|
58
|
+
if v == 0
|
59
|
+
replace [0]
|
60
|
+
else
|
61
|
+
while v > 0
|
62
|
+
v, r = v.divmod(@radix)
|
63
|
+
unshift r
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def zero?
|
70
|
+
# value == 0
|
71
|
+
@digits && @digits.all?{|d| d==0 }
|
72
|
+
end
|
73
|
+
|
74
|
+
# Deep copy
|
75
|
+
def dup
|
76
|
+
Digits[@digits_array.dup, base: @radix]
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_s
|
80
|
+
args = ""
|
81
|
+
if @digits_array.size > 0
|
82
|
+
args << @digits_array.to_s.unwrap('[]')
|
83
|
+
args << ', '
|
84
|
+
end
|
85
|
+
args << "base: #{radix}"
|
86
|
+
"Digits[#{args}]"
|
87
|
+
end
|
88
|
+
|
89
|
+
def inspect
|
90
|
+
to_s
|
91
|
+
end
|
92
|
+
|
93
|
+
def truncate!(n)
|
94
|
+
@digits_array.slice! n..-1
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Numerals
|
2
|
+
|
3
|
+
# Digits definition (symbols used as digits)
|
4
|
+
class DigitsDefinition
|
5
|
+
include ModalSupport::StateEquivalent
|
6
|
+
include ModalSupport::BracketConstructor
|
7
|
+
|
8
|
+
DEFAULT_DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
9
|
+
DEFAULT_BASE = 10
|
10
|
+
|
11
|
+
def initialize(*args)
|
12
|
+
if String === args.first
|
13
|
+
digits = args.shift
|
14
|
+
end
|
15
|
+
options = args.shift || {}
|
16
|
+
raise NumeralError, "Invalid DigitsDefinition" unless args.empty? && Hash === options
|
17
|
+
digits ||= options[:digits]
|
18
|
+
base = options[:base]
|
19
|
+
if base
|
20
|
+
if digits
|
21
|
+
raise NumeralError, "Inconsistent DigitsDefinition" unless digits.size == base
|
22
|
+
end
|
23
|
+
elsif digits
|
24
|
+
base = digits.size
|
25
|
+
else
|
26
|
+
base = DEFAULT_BASE
|
27
|
+
end
|
28
|
+
digits ||= DEFAULT_DIGITS[0, base]
|
29
|
+
|
30
|
+
@radix = base
|
31
|
+
@digits = digits
|
32
|
+
@case_sensitive = options[:case_sensitive]
|
33
|
+
@downcase = options[:downcase] || (@digits.downcase == @digits)
|
34
|
+
@digits = @digits.downcase if @downcase
|
35
|
+
end
|
36
|
+
|
37
|
+
def is_digit?(digit)
|
38
|
+
digit = set_case(digit)
|
39
|
+
@digits.include?(digit)
|
40
|
+
end
|
41
|
+
|
42
|
+
def digit_value(digit)
|
43
|
+
digit = set_case(digit)
|
44
|
+
@digits.index(digit)
|
45
|
+
end
|
46
|
+
|
47
|
+
def digit_char(v)
|
48
|
+
v >= 0 && v < @radix ? @digits[v] : nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def radix
|
52
|
+
@radix
|
53
|
+
end
|
54
|
+
|
55
|
+
def valid?
|
56
|
+
@digits.none?{|x| x.nil? || x<0 || x>=@radix}
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def set_case(digit_char)
|
62
|
+
if digit_char
|
63
|
+
unless @case_sensitive
|
64
|
+
if @downcase
|
65
|
+
digit_char = digit_char.downcase
|
66
|
+
else
|
67
|
+
digit_char = digit_char.upcase
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
digit_char
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
|
2
|
+
module Numerals
|
3
|
+
|
4
|
+
# Repeating decimal configuration options
|
5
|
+
class Options
|
6
|
+
include ModalSupport::StateEquivalent
|
7
|
+
include ModalSupport::BracketConstructor
|
8
|
+
|
9
|
+
def initialize(options={})
|
10
|
+
options = {
|
11
|
+
# default options
|
12
|
+
delim: ['<', '>'],
|
13
|
+
suffix: '...',
|
14
|
+
sep: '.',
|
15
|
+
grouping: [',', []], # [...,[3]] for thousands separators
|
16
|
+
special: ['NaN', 'Infinity'],
|
17
|
+
digits: nil,
|
18
|
+
signs: ['+', '-'],
|
19
|
+
maximum_number_of_digits: Numeral.maximum_number_of_digits
|
20
|
+
}.merge(options)
|
21
|
+
|
22
|
+
set_delim *Array(options[:delim])
|
23
|
+
set_suffix options[:suffix]
|
24
|
+
set_sep options[:sep]
|
25
|
+
set_grouping *Array(options[:grouping])
|
26
|
+
set_special *Array(options[:special])
|
27
|
+
set_digits *Array(options[:digits])
|
28
|
+
set_signs *Array(options[:signs])
|
29
|
+
@maximum_number_of_digits = options[:maximum_number_of_digits]
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_accessor :begin_rep, :end_rep, :auto_rep, :dec_sep, :grp_sep, :grp, :maximum_number_of_digits
|
33
|
+
attr_accessor :nan_txt, :inf_txt, :plus_sign, :minus_sign
|
34
|
+
|
35
|
+
def set_delim(begin_d, end_d='')
|
36
|
+
@begin_rep = begin_d
|
37
|
+
@end_rep = end_d
|
38
|
+
return self
|
39
|
+
end
|
40
|
+
|
41
|
+
def set_suffix(a)
|
42
|
+
@auto_rep = a
|
43
|
+
return self
|
44
|
+
end
|
45
|
+
|
46
|
+
def set_sep(d)
|
47
|
+
@dec_sep = d
|
48
|
+
return self
|
49
|
+
end
|
50
|
+
|
51
|
+
def set_grouping(sep, g=[])
|
52
|
+
@grp_sep = sep
|
53
|
+
@grp = g
|
54
|
+
return self
|
55
|
+
end
|
56
|
+
|
57
|
+
def set_special(nan_txt, inf_txt)
|
58
|
+
@nan_txt = nan_txt
|
59
|
+
@inf_txt = inf_txt
|
60
|
+
return self
|
61
|
+
end
|
62
|
+
|
63
|
+
def set_digits(*args)
|
64
|
+
@digits_defined = !args.empty?
|
65
|
+
@digits = DigitsDefinition[*args]
|
66
|
+
self
|
67
|
+
end
|
68
|
+
|
69
|
+
def set_signs(plus, minus)
|
70
|
+
@plus_sign = plus
|
71
|
+
@minus_sign = minus
|
72
|
+
end
|
73
|
+
|
74
|
+
attr_accessor :digits
|
75
|
+
|
76
|
+
def digits_defined?
|
77
|
+
@digits_defined
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
DEFAULT_OPTIONS = Options[]
|
83
|
+
|
84
|
+
end
|