numerals 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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