numerals 0.1.0 → 0.2.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 +4 -2
- data/lib/numerals/format/format.rb +20 -1
- data/lib/numerals/format/notations/html.rb +14 -3
- data/lib/numerals/format/notations/latex.rb +14 -3
- data/lib/numerals/format/notations/text.rb +61 -29
- data/lib/numerals/format/output.rb +0 -2
- data/lib/numerals/format/symbols.rb +117 -182
- data/lib/numerals/format/symbols/digits.rb +173 -0
- data/lib/numerals/format/symbols/padding.rb +112 -0
- data/lib/numerals/format/text_parts.rb +4 -0
- data/lib/numerals/version.rb +1 -1
- data/test/test_format.rb +47 -0
- data/test/test_format_input.rb +25 -0
- data/test/test_format_output.rb +60 -1
- metadata +4 -3
- data/lib/numerals/formatting/options.rb +0 -84
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e87d9faabf0392f12d84d026d85d480aa67d32c
|
4
|
+
data.tar.gz: 67b89570a8c97ae4abe727c730bdc77099ca70d0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3c3b97ef87dcda6dec5dce9a8d8449da8b8da3c5faab0664ab4563dfb32f4770b9295271454c6e9c2efbec2be3b72843cf12c6f2d48395e36c7474b8d6be886c
|
7
|
+
data.tar.gz: 6ff01ddb162578b80b30596adfd38be3fb992509b8ff7aa32018b98815dae4e2449770313728647e9b8af0110e6a0cd4078edc1f4d01754a91a3205ceb7cd93a
|
data/README.md
CHANGED
@@ -161,8 +161,10 @@ Done:
|
|
161
161
|
* Handling of 'unsignificant' digits: show them either as special
|
162
162
|
symbol, as zeros or omit them (comfigured in Symbols)
|
163
163
|
|
164
|
-
Pending:
|
165
|
-
|
166
164
|
* Padding aspect of formatting on output
|
167
165
|
|
168
166
|
* Show base indicators on output
|
167
|
+
|
168
|
+
Pending:
|
169
|
+
|
170
|
+
* HTML & Latex Input/Output
|
@@ -60,6 +60,14 @@ module Numerals
|
|
60
60
|
@rounding.base
|
61
61
|
end
|
62
62
|
|
63
|
+
def padding
|
64
|
+
@symbols.padding
|
65
|
+
end
|
66
|
+
|
67
|
+
def padded?
|
68
|
+
padding.padded?
|
69
|
+
end
|
70
|
+
|
63
71
|
# Presentation base for the significand
|
64
72
|
def significand_base
|
65
73
|
base**@mode.base_scale
|
@@ -94,6 +102,7 @@ module Numerals
|
|
94
102
|
@rounding.set! places: options[:places] if options[:places]
|
95
103
|
@symbols.set! repeating: options[:repeating] if options.has_key?(:repeating)
|
96
104
|
@symbols.set! case_sensitive: options[:case_sensitive] if options.has_key?(:case_sensitive)
|
105
|
+
@symbols.set! padding: options[:padding] if options[:padding]
|
97
106
|
end
|
98
107
|
|
99
108
|
def parameters
|
@@ -130,6 +139,10 @@ module Numerals
|
|
130
139
|
set! rounding: args
|
131
140
|
end
|
132
141
|
|
142
|
+
aspect :padding do |*args|
|
143
|
+
set! padding: args
|
144
|
+
end
|
145
|
+
|
133
146
|
aspect :base do |base|
|
134
147
|
set! base: base
|
135
148
|
end
|
@@ -200,6 +213,10 @@ module Numerals
|
|
200
213
|
@symbols.set_minus!(minus)
|
201
214
|
end
|
202
215
|
|
216
|
+
aspect :leading_zeros do |width|
|
217
|
+
@symbols.set_leading_zeros! width
|
218
|
+
end
|
219
|
+
|
203
220
|
private
|
204
221
|
|
205
222
|
def extract_options(*args)
|
@@ -217,6 +234,8 @@ module Numerals
|
|
217
234
|
options[:symbols] = arg
|
218
235
|
when Symbols::Digits
|
219
236
|
options[:digits] = arg
|
237
|
+
when Symbols::Padding
|
238
|
+
options[:padding] = arg
|
220
239
|
when Format
|
221
240
|
options.merge! arg.parameters
|
222
241
|
when :exact_input
|
@@ -230,7 +249,7 @@ module Numerals
|
|
230
249
|
sci_int_digits: 1
|
231
250
|
},
|
232
251
|
symbols: {
|
233
|
-
exponent: 'p'
|
252
|
+
exponent: 'p', base_prefix: '0x'
|
234
253
|
}
|
235
254
|
)
|
236
255
|
when :gen, :general, :sci, :scientific, :fix; :fixed
|
@@ -12,20 +12,31 @@ module Numerals
|
|
12
12
|
# <span class=”numerals-num”>1.23<span class="numerals-rep">456</span> ×10<span class="numerals-sup">9</span></span>
|
13
13
|
# .numerals-rep { text-decoration: overline; }
|
14
14
|
# .numerals-sup { vertical-align: super; }
|
15
|
+
# TODO: padding
|
15
16
|
if text_parts.special?
|
16
17
|
output << escape(text_parts.special)
|
17
18
|
else
|
18
19
|
output << escape(text_parts.sign)
|
20
|
+
if format.symbols.base_prefix
|
21
|
+
output << format.symbols.base_prefix
|
22
|
+
end
|
19
23
|
output << escape(text_parts.integer) # or decide here if empty integer part is show as 0?
|
20
|
-
|
21
|
-
!text_parts.repeat? &&
|
22
|
-
!format.symbols.show_point
|
24
|
+
if text_parts.show_point?(format)
|
23
25
|
output << escape(format.symbols.point)
|
24
26
|
end
|
25
27
|
output << escape(text_parts.fractional)
|
26
28
|
if text_parts.repeat
|
27
29
|
output << %(<span style="text-decoration: overline">#{escape(text_parts.repeat)}</span>)
|
28
30
|
end
|
31
|
+
if format.symbols.base_suffix || format.base != 10
|
32
|
+
if format.symbols.base_prefix
|
33
|
+
output << format.symbols.base_suffix
|
34
|
+
else
|
35
|
+
# show base suffix as a subscript
|
36
|
+
subscript = format.symbols.base_suffix || base.to_s
|
37
|
+
º output << "<sub>#{subscript}</sub>"
|
38
|
+
end
|
39
|
+
end
|
29
40
|
if text_parts.exponent_value != 0 || format.mode.mode == :scientific
|
30
41
|
output << "×"
|
31
42
|
output << escape(text_parts.exponent_base)
|
@@ -6,20 +6,31 @@ module Numerals
|
|
6
6
|
|
7
7
|
def assemble(output, text_parts)
|
8
8
|
# 1.23\overline{456}\times10^{9}
|
9
|
+
# TODO: padding
|
9
10
|
if text_parts.special?
|
10
11
|
output << text_parts.special
|
11
12
|
else
|
12
13
|
output << text_parts.sign
|
14
|
+
if format.symbols.base_prefix
|
15
|
+
output << format.symbols.base_prefix
|
16
|
+
end
|
13
17
|
output << text_parts.integer # or decide here if empty integer part is shown as 0?
|
14
|
-
|
15
|
-
!text_parts.repeat? &&
|
16
|
-
!format.symbols.show_point
|
18
|
+
if text_parts.show_point?(format)
|
17
19
|
output << format.symbols.point
|
18
20
|
end
|
19
21
|
output << text_parts.fractional
|
20
22
|
if text_parts.repeat?
|
21
23
|
output << "\\overline{#{text_parts.repeat}}"
|
22
24
|
end
|
25
|
+
if format.symbols.base_suffix || format.base != 10
|
26
|
+
if format.symbols.base_prefix
|
27
|
+
output << format.symbols.base_suffix
|
28
|
+
else
|
29
|
+
# show base suffix as a subscript
|
30
|
+
subscript = format.symbols.base_suffix || base.to_s
|
31
|
+
output << "_{#{subscript}}"
|
32
|
+
end
|
33
|
+
end
|
23
34
|
if text_parts.exponent_value != 0 || format.mode.mode == :scientific
|
24
35
|
output << "\\times"
|
25
36
|
output << text_parts.exponent_base
|
@@ -8,35 +8,12 @@ module Numerals
|
|
8
8
|
if text_parts.special?
|
9
9
|
output << text_parts.special
|
10
10
|
else
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
!format.symbols.show_point
|
16
|
-
output << format.symbols.point
|
17
|
-
end
|
18
|
-
output << text_parts.fractional
|
19
|
-
if text_parts.repeat?
|
20
|
-
if format.symbols.repeat_delimited
|
21
|
-
output << format.symbols.repeat_begin
|
22
|
-
output << text_parts.repeat
|
23
|
-
output << format.symbols.repeat_end
|
24
|
-
else
|
25
|
-
n = RepeatDetector.min_repeat_count(
|
26
|
-
text_parts.numeral.digits.digits_array,
|
27
|
-
text_parts.numeral.repeat,
|
28
|
-
format.symbols.repeat_count - 1
|
29
|
-
)
|
30
|
-
n.times do
|
31
|
-
output << text_parts.repeat
|
32
|
-
end
|
33
|
-
output << format.symbols.repeat_suffix
|
34
|
-
end
|
35
|
-
end
|
36
|
-
if text_parts.exponent_value != 0 || format.mode.mode == :scientific
|
37
|
-
output << format.symbols.exponent
|
38
|
-
output << text_parts.exponent
|
11
|
+
if format.symbols.padding.padded?
|
12
|
+
output_size = OutputSize.new
|
13
|
+
assemble_parts(output_size, text_parts)
|
14
|
+
left_padding, internal_padding, right_padding = format.symbols.paddings(output_size.size)
|
39
15
|
end
|
16
|
+
assemble_parts(output, text_parts, left_padding, internal_padding, right_padding)
|
40
17
|
end
|
41
18
|
end
|
42
19
|
|
@@ -57,11 +34,13 @@ module Numerals
|
|
57
34
|
valid = true
|
58
35
|
base = format.significand_base
|
59
36
|
# TODO: replace numbered groups by named variables ?<var>
|
60
|
-
# TODO: ignore padding, admit base indicators
|
61
37
|
regular = /
|
62
38
|
\A
|
39
|
+
#{s.regexp(:fill, no_capture: true, optional: true, multiple: true)}
|
63
40
|
#{s.regexp(:plus, :minus)}?
|
64
41
|
\s*
|
42
|
+
#{s.regexp(:fill, no_capture: true, optional: true, multiple: true)}
|
43
|
+
#{s.regexp(:base_prefix, no_capture: true, optional: true)}
|
65
44
|
(?:
|
66
45
|
(?:(#{s.regexp(:grouped_digits, base: base, no_capture: true)}+)#{s.regexp(:point)}?)
|
67
46
|
|
|
@@ -70,7 +49,9 @@ module Numerals
|
|
70
49
|
(#{s.regexp(:digits, base: base, no_capture: true)}*)
|
71
50
|
(?:#{s.regexp(:repeat_begin)}(#{s.regexp(:digits, base: base, no_capture: true)}+)#{s.regexp(:repeat_end)})?
|
72
51
|
#{s.regexp(:repeat_suffix)}?
|
52
|
+
#{s.regexp(:base_suffix, no_capture: true, optional: true)}
|
73
53
|
(?:#{s.regexp(:exponent)}#{s.regexp(:plus, :minus)}?(\d+))?
|
54
|
+
#{s.regexp(:fill, no_capture: true, optional: true, multiple: true)}
|
74
55
|
\Z
|
75
56
|
/x
|
76
57
|
unless s.case_sensitive?
|
@@ -132,6 +113,57 @@ module Numerals
|
|
132
113
|
text_parts
|
133
114
|
end
|
134
115
|
|
116
|
+
private
|
117
|
+
|
118
|
+
class OutputSize
|
119
|
+
def initialize
|
120
|
+
@size = 0
|
121
|
+
end
|
122
|
+
def <<(text)
|
123
|
+
@size += text.size
|
124
|
+
end
|
125
|
+
attr_reader :size
|
126
|
+
end
|
127
|
+
|
128
|
+
def assemble_parts(output, text_parts, left_padding='', internal_padding='', right_padding='')
|
129
|
+
output << left_padding
|
130
|
+
output << text_parts.sign
|
131
|
+
if format.symbols.base_prefix
|
132
|
+
output << format.symbols.base_prefix
|
133
|
+
end
|
134
|
+
output << internal_padding
|
135
|
+
output << text_parts.integer # or decide here if empty integer part is show as 0?
|
136
|
+
if text_parts.show_point?(format)
|
137
|
+
output << format.symbols.point
|
138
|
+
end
|
139
|
+
output << text_parts.fractional
|
140
|
+
if text_parts.repeat?
|
141
|
+
if format.symbols.repeat_delimited
|
142
|
+
output << format.symbols.repeat_begin
|
143
|
+
output << text_parts.repeat
|
144
|
+
output << format.symbols.repeat_end
|
145
|
+
else
|
146
|
+
n = RepeatDetector.min_repeat_count(
|
147
|
+
text_parts.numeral.digits.digits_array,
|
148
|
+
text_parts.numeral.repeat,
|
149
|
+
format.symbols.repeat_count - 1
|
150
|
+
)
|
151
|
+
n.times do
|
152
|
+
output << text_parts.repeat
|
153
|
+
end
|
154
|
+
output << format.symbols.repeat_suffix
|
155
|
+
end
|
156
|
+
end
|
157
|
+
if format.symbols.base_suffix
|
158
|
+
output << format.symbols.base_suffix
|
159
|
+
end
|
160
|
+
if text_parts.exponent_value != 0 || format.mode.mode == :scientific
|
161
|
+
output << format.symbols.exponent
|
162
|
+
output << text_parts.exponent
|
163
|
+
end
|
164
|
+
output << right_padding
|
165
|
+
end
|
166
|
+
|
135
167
|
end
|
136
168
|
|
137
169
|
define_notation :text, TextNotation
|
@@ -153,8 +153,6 @@ module Numerals
|
|
153
153
|
text_parts.exponent_base = num_parts.exponent_base.to_s(10) # use digits_definition ?
|
154
154
|
text_parts.exponent_base_value = num_parts.exponent_base
|
155
155
|
end
|
156
|
-
# TODO: justification
|
157
|
-
# TODO: base indicator for significand? significand_bas?
|
158
156
|
text_parts
|
159
157
|
end
|
160
158
|
|
@@ -11,158 +11,6 @@ module Numerals
|
|
11
11
|
#
|
12
12
|
class Format::Symbols < FormattingAspect
|
13
13
|
|
14
|
-
class Digits < FormattingAspect
|
15
|
-
|
16
|
-
DEFAULT_DIGITS = %w(0 1 2 3 4 5 6 7 8 9 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)
|
17
|
-
|
18
|
-
def initialize(*args)
|
19
|
-
@digits = DEFAULT_DIGITS
|
20
|
-
@downcase_digits = @digits.map(&:downcase)
|
21
|
-
@max_base = @digits.size
|
22
|
-
@case_sensitive = false
|
23
|
-
@uppercase = false
|
24
|
-
@lowercase = false
|
25
|
-
set! *args
|
26
|
-
end
|
27
|
-
|
28
|
-
include ModalSupport::StateEquivalent
|
29
|
-
|
30
|
-
set do |*args|
|
31
|
-
options = extract_options(*args)
|
32
|
-
options.each do |option, value|
|
33
|
-
send :"#{option}=", value
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
attr_reader :digits_string, :max_base, :case_sensitive, :uppercase, :lowercase
|
38
|
-
attr_writer :case_sensitive
|
39
|
-
|
40
|
-
def digits(options = {})
|
41
|
-
base = options[:base] || @max_base
|
42
|
-
if base >= @max_base
|
43
|
-
@digits
|
44
|
-
else
|
45
|
-
@digits[0, base]
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def digits=(digits)
|
50
|
-
if digits.is_a?(String)
|
51
|
-
@digits = digits.each_char.to_a
|
52
|
-
else
|
53
|
-
@digits = digits
|
54
|
-
end
|
55
|
-
@max_base = @digits.size
|
56
|
-
@lowercase = @digits.all? { |d| d.downcase == d }
|
57
|
-
@uppercase = @digits.all? { |d| d.upcase == d }
|
58
|
-
@downcase_digits = @digits.map(&:downcase)
|
59
|
-
if @digits.uniq.size != @max_base
|
60
|
-
raise "Inconsistent digits"
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def uppercase=(v)
|
65
|
-
@uppercase = v
|
66
|
-
self.digits = @digits.map(&:upcase) if v
|
67
|
-
end
|
68
|
-
|
69
|
-
def lowercase=(v)
|
70
|
-
@lowercase = v
|
71
|
-
self.digits = @digits.map(&:downcase) if v
|
72
|
-
end
|
73
|
-
|
74
|
-
def case_sensitive?
|
75
|
-
case_sensitive
|
76
|
-
end
|
77
|
-
|
78
|
-
def is_digit?(digit_symbol, options={})
|
79
|
-
base = options[:base] || @max_base
|
80
|
-
raise "Invalid base" if base > @max_base
|
81
|
-
v = digit_value(digit_symbol)
|
82
|
-
v && v < base
|
83
|
-
end
|
84
|
-
|
85
|
-
def digit_value(digit)
|
86
|
-
if @case_sensitive
|
87
|
-
@digits.index(digit)
|
88
|
-
else
|
89
|
-
@downcase_digits.index(digit.downcase)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
def digit_symbol(v, options={})
|
94
|
-
base = options[:base] || @max_base
|
95
|
-
raise "Invalid base" if base > @max_base
|
96
|
-
v >= 0 && v < base ? @digits[v] : nil
|
97
|
-
end
|
98
|
-
|
99
|
-
# Convert sequence of digits to its text representation.
|
100
|
-
# The nil value can be used in the digits sequence to
|
101
|
-
# represent the group separator.
|
102
|
-
def digits_text(digit_values, options={})
|
103
|
-
insignificant_digits = options[:insignificant_digits] || 0
|
104
|
-
num_digits = digit_values.reduce(0) { |num, digit|
|
105
|
-
digit.nil? ? num : num + 1
|
106
|
-
}
|
107
|
-
num_digits -= insignificant_digits
|
108
|
-
digit_values.map { |d|
|
109
|
-
if d.nil?
|
110
|
-
options[:separator]
|
111
|
-
else
|
112
|
-
num_digits -= 1
|
113
|
-
if num_digits >= 0
|
114
|
-
digit_symbol(d, options)
|
115
|
-
else
|
116
|
-
options[:insignificant_symbol]
|
117
|
-
end
|
118
|
-
end
|
119
|
-
}.join
|
120
|
-
end
|
121
|
-
|
122
|
-
def parameters
|
123
|
-
params = {}
|
124
|
-
params[:digits] = @digits
|
125
|
-
params[:case_sensitive] = @case_sensitive
|
126
|
-
params[:uppercase] = @uppercase
|
127
|
-
params[:lowercase] = @lowercase
|
128
|
-
params
|
129
|
-
end
|
130
|
-
|
131
|
-
def to_s
|
132
|
-
# TODO: show only non-defaults
|
133
|
-
"Digits[#{parameters.inspect.unwrap('{}')}]"
|
134
|
-
end
|
135
|
-
|
136
|
-
def inspect
|
137
|
-
"Format::Symbols::#{self}"
|
138
|
-
end
|
139
|
-
|
140
|
-
def dup
|
141
|
-
Digits[parameters]
|
142
|
-
end
|
143
|
-
|
144
|
-
private
|
145
|
-
|
146
|
-
def extract_options(*args)
|
147
|
-
options = {}
|
148
|
-
args = args.first if args.size == 1 && args.first.kind_of?(Array)
|
149
|
-
args.each do |arg|
|
150
|
-
case arg
|
151
|
-
when Hash
|
152
|
-
options.merge! arg
|
153
|
-
when String, Array
|
154
|
-
options[:digits] = arg
|
155
|
-
when Format::Symbols::Digits
|
156
|
-
options.merge! arg.parameters
|
157
|
-
else
|
158
|
-
raise "Invalid Symbols::Digits definition"
|
159
|
-
end
|
160
|
-
end
|
161
|
-
options
|
162
|
-
end
|
163
|
-
|
164
|
-
end
|
165
|
-
|
166
14
|
DEFAULTS = {
|
167
15
|
nan: 'NaN',
|
168
16
|
infinity: 'Infinity',
|
@@ -175,7 +23,6 @@ module Numerals
|
|
175
23
|
repeat_begin: '<',
|
176
24
|
repeat_end: '>',
|
177
25
|
repeat_suffix: '...',
|
178
|
-
#repeat_detect: false,
|
179
26
|
show_plus: false,
|
180
27
|
show_exponent_plus: false,
|
181
28
|
uppercase: false,
|
@@ -186,7 +33,9 @@ module Numerals
|
|
186
33
|
repeat_count: 3,
|
187
34
|
grouping: [],
|
188
35
|
insignificant_digit: nil,
|
189
|
-
repeating: true
|
36
|
+
repeating: true,
|
37
|
+
base_prefix: nil,
|
38
|
+
base_suffix: nil
|
190
39
|
}
|
191
40
|
|
192
41
|
def initialize(*args)
|
@@ -199,29 +48,27 @@ module Numerals
|
|
199
48
|
# default Digits among all Symbols)
|
200
49
|
@digits = Format::Symbols::Digits[]
|
201
50
|
|
202
|
-
#
|
203
|
-
|
204
|
-
|
205
|
-
# TODO: base_suffixes, base_preffixes, show_base
|
51
|
+
# same with @padding
|
52
|
+
@padding = Format::Symbols::Padding[]
|
206
53
|
|
207
54
|
set! *args
|
208
55
|
end
|
209
56
|
|
210
|
-
# TODO: transmit uppercase/lowercase to digits
|
211
|
-
|
212
57
|
attr_reader :digits, :nan, :infinity, :plus, :minus, :exponent, :point,
|
213
|
-
:group_separator, :zero, :insignificant_digit
|
214
|
-
|
215
|
-
|
216
|
-
:show_zero, :show_point
|
217
|
-
|
218
|
-
|
219
|
-
|
58
|
+
:group_separator, :zero, :insignificant_digit, :padding,
|
59
|
+
:repeat_begin, :repeat_end, :repeat_suffix, :repeat_delimited,
|
60
|
+
:show_plus, :show_exponent_plus, :uppercase, :lowercase,
|
61
|
+
:show_zero, :show_point,
|
62
|
+
:grouping, :repeat_count, :repeating,
|
63
|
+
:base_prefix, :base_suffix
|
64
|
+
|
65
|
+
attr_writer :digits, :uppercase, :lowercase, :nan, :infinity, :plus,
|
220
66
|
:minus, :exponent, :point, :group_separator, :zero,
|
221
67
|
:repeat_begin, :repeat_end, :repeat_suffix,
|
222
68
|
:show_plus, :show_exponent_plus, :show_zero, :show_point,
|
223
69
|
:repeat_delimited, :repeat_count, :grouping,
|
224
|
-
:insignificant_digit, :repeating
|
70
|
+
:insignificant_digit, :repeating,
|
71
|
+
:base_prefix, :base_suffix
|
225
72
|
|
226
73
|
include ModalSupport::StateEquivalent
|
227
74
|
|
@@ -251,11 +98,26 @@ module Numerals
|
|
251
98
|
!@grouping.empty? && @group_separator && !@group_separator.empty?
|
252
99
|
end
|
253
100
|
|
101
|
+
def padded?
|
102
|
+
@padding.padded?
|
103
|
+
end
|
104
|
+
|
105
|
+
def fill
|
106
|
+
fill = @padding.fill
|
107
|
+
if fill.is_a?(Integer)
|
108
|
+
@digits.digit_symbol(fill)
|
109
|
+
else
|
110
|
+
fill
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
254
114
|
set do |*args|
|
255
115
|
options = extract_options(*args)
|
256
116
|
options.each do |option, value|
|
257
117
|
if option == :digits
|
258
118
|
@digits.set! value
|
119
|
+
elsif option == :padding
|
120
|
+
@padding.set! value
|
259
121
|
else
|
260
122
|
send :"#{option}=", value
|
261
123
|
end
|
@@ -263,14 +125,38 @@ module Numerals
|
|
263
125
|
apply_case!
|
264
126
|
end
|
265
127
|
|
266
|
-
attr_writer :digits, :nan, :infinity,
|
267
|
-
:plus, :minus, :exponent, :point, :group_separator, :zero,
|
268
|
-
:repeat_begin, :repeat_end, :repeat_suffix, :show_plus,
|
269
|
-
:show_exponent_plus, :uppercase, :show_zero, :show_point,
|
270
|
-
:grouping, :repeat_count
|
271
|
-
|
272
128
|
aspect :repeat do |*args|
|
273
|
-
|
129
|
+
args.each do |arg|
|
130
|
+
case arg
|
131
|
+
when true, false
|
132
|
+
@repeating = arg
|
133
|
+
when Integer
|
134
|
+
@repeat_count = arg
|
135
|
+
when :delimited
|
136
|
+
@repeat_delimited = true
|
137
|
+
when :suffixed
|
138
|
+
@repeat_delimited = false
|
139
|
+
when Hash
|
140
|
+
arg.each do |key, value|
|
141
|
+
case key
|
142
|
+
when :delimiters
|
143
|
+
@repeat_begin, @repeat_end = Array(value)
|
144
|
+
when :begin
|
145
|
+
@repeat_begin = value
|
146
|
+
when :end
|
147
|
+
@repeat_end = value
|
148
|
+
when :suffix
|
149
|
+
@repeat_suffix = value
|
150
|
+
when :delimited
|
151
|
+
@repeat_delimited = value
|
152
|
+
when :count
|
153
|
+
@repeat_count = value
|
154
|
+
else
|
155
|
+
send "#{key}=", value
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
274
160
|
end
|
275
161
|
|
276
162
|
aspect :grouping do |*args|
|
@@ -278,12 +164,14 @@ module Numerals
|
|
278
164
|
case arg
|
279
165
|
when Symbol
|
280
166
|
if arg == :thousands
|
281
|
-
@
|
167
|
+
@grouping = [3]
|
282
168
|
end
|
283
169
|
when String
|
284
170
|
@group_separator = arg
|
285
171
|
when Array
|
286
|
-
@
|
172
|
+
@grouping = arg
|
173
|
+
when false
|
174
|
+
@grouping = []
|
287
175
|
end
|
288
176
|
end
|
289
177
|
end
|
@@ -351,6 +239,14 @@ module Numerals
|
|
351
239
|
@minus = minus
|
352
240
|
end
|
353
241
|
|
242
|
+
aspect :padding do |*args|
|
243
|
+
@padding.set! *args
|
244
|
+
end
|
245
|
+
|
246
|
+
aspect :leading_zeros do |width|
|
247
|
+
@padding.leading_zeros = width
|
248
|
+
end
|
249
|
+
|
354
250
|
def parameters(abbreviated=false)
|
355
251
|
params = {}
|
356
252
|
DEFAULTS.each do |param, default|
|
@@ -362,6 +258,9 @@ module Numerals
|
|
362
258
|
if !abbreviated || @digits != Format::Symbols::Digits[]
|
363
259
|
params[:digits] = @digits
|
364
260
|
end
|
261
|
+
if !abbreviated || @padding != Format::Symbols::Padding[]
|
262
|
+
params[:padding] = @padding
|
263
|
+
end
|
365
264
|
params
|
366
265
|
end
|
367
266
|
|
@@ -447,7 +346,9 @@ module Numerals
|
|
447
346
|
symbols = args
|
448
347
|
digits = symbols.delete(:digits)
|
449
348
|
grouped_digits = symbols.delete(:grouped_digits)
|
450
|
-
symbols = symbols.map { |s|
|
349
|
+
symbols = symbols.map { |s|
|
350
|
+
s.is_a?(Symbol) ? send(s) : s
|
351
|
+
}
|
451
352
|
if grouped_digits
|
452
353
|
symbols += [group_separator, insignificant_digit]
|
453
354
|
elsif digits
|
@@ -479,6 +380,23 @@ module Numerals
|
|
479
380
|
}.compact
|
480
381
|
end
|
481
382
|
|
383
|
+
# Returns left, internal and right padding for a number
|
384
|
+
# of given size (number of characters)
|
385
|
+
def paddings(number_size)
|
386
|
+
left_padding = internal_padding = right_padding = ''
|
387
|
+
if padded?
|
388
|
+
left_padding_size, internal_padding_size, right_padding_size = padding.padding_sizes(number_size)
|
389
|
+
right_padding_size = right_padding_size/fill.size
|
390
|
+
right_padding = fill*right_padding_size
|
391
|
+
d = right_padding_size - right_padding.size
|
392
|
+
left_padding_size = (left_padding_size + d)/fill.size
|
393
|
+
left_padding = fill*left_padding_size
|
394
|
+
internal_padding_size = internal_padding_size/fill.size
|
395
|
+
internal_padding = fill*internal_padding_size
|
396
|
+
end
|
397
|
+
[left_padding, internal_padding, right_padding ]
|
398
|
+
end
|
399
|
+
|
482
400
|
private
|
483
401
|
|
484
402
|
def regexp_char(c, options = {})
|
@@ -500,10 +418,22 @@ module Numerals
|
|
500
418
|
symbols = Array(symbols).compact.select { |s| !s.empty? }
|
501
419
|
.map{ |d| regexp_symbol(d, options) }.join('|')
|
502
420
|
if capture
|
503
|
-
"(#{symbols})"
|
421
|
+
symbols = "(#{symbols})"
|
504
422
|
else
|
505
|
-
|
423
|
+
if symbols != ''
|
424
|
+
symbols = "(?:#{symbols})"
|
425
|
+
if options[:optional]
|
426
|
+
if options[:multiple]
|
427
|
+
symbols = "#{symbols}*"
|
428
|
+
else
|
429
|
+
symbols = "#{symbols}?"
|
430
|
+
end
|
431
|
+
elsif options[:multiple]
|
432
|
+
symbols = "#{symbols}+"
|
433
|
+
end
|
434
|
+
end
|
506
435
|
end
|
436
|
+
symbols
|
507
437
|
end
|
508
438
|
|
509
439
|
def extract_options(*args)
|
@@ -540,7 +470,8 @@ module Numerals
|
|
540
470
|
@repeat_begin = @repeat_begin.upcase
|
541
471
|
@repeat_end = @repeat_end.upcase
|
542
472
|
@repeat_suffix = @repeat_suffix.upcase
|
543
|
-
@digits
|
473
|
+
@digits.set! uppercase: true
|
474
|
+
@padding.fill = @padding.fill.upcase if @padding.fill.is_a?(String)
|
544
475
|
elsif @lowercase
|
545
476
|
@nan = @nan.downcase
|
546
477
|
@infinity = @infinity.downcase
|
@@ -552,7 +483,8 @@ module Numerals
|
|
552
483
|
@repeat_begin = @repeat_begin.downcase
|
553
484
|
@repeat_end = @repeat_end.downcase
|
554
485
|
@repeat_suffix = @repeat_suffix.downcase
|
555
|
-
@digits
|
486
|
+
@digits.set! lowercase: true
|
487
|
+
@padding.fill = @padding.fill.downcase if @padding.filll.is_a?(String)
|
556
488
|
end
|
557
489
|
end
|
558
490
|
|
@@ -563,3 +495,6 @@ module Numerals
|
|
563
495
|
end
|
564
496
|
|
565
497
|
end
|
498
|
+
|
499
|
+
require 'numerals/format/symbols/digits'
|
500
|
+
require 'numerals/format/symbols/padding'
|
@@ -0,0 +1,173 @@
|
|
1
|
+
module Numerals
|
2
|
+
|
3
|
+
class Format::Symbols::Digits < FormattingAspect
|
4
|
+
|
5
|
+
DEFAULT_DIGITS = %w(0 1 2 3 4 5 6 7 8 9 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)
|
6
|
+
|
7
|
+
def initialize(*args)
|
8
|
+
@digits = DEFAULT_DIGITS
|
9
|
+
@downcase_digits = @digits.map(&:downcase)
|
10
|
+
@max_base = @digits.size
|
11
|
+
@case_sensitive = false
|
12
|
+
@uppercase = false
|
13
|
+
@lowercase = false
|
14
|
+
set! *args
|
15
|
+
end
|
16
|
+
|
17
|
+
include ModalSupport::StateEquivalent
|
18
|
+
|
19
|
+
set do |*args|
|
20
|
+
options = extract_options(*args)
|
21
|
+
options.each do |option, value|
|
22
|
+
send :"#{option}=", value
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_reader :digits_string, :max_base, :case_sensitive, :uppercase, :lowercase
|
27
|
+
attr_writer :case_sensitive
|
28
|
+
|
29
|
+
def digits(options = {})
|
30
|
+
base = options[:base] || @max_base
|
31
|
+
if base >= @max_base
|
32
|
+
@digits
|
33
|
+
else
|
34
|
+
@digits[0, base]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def digits=(digits)
|
39
|
+
if digits.is_a?(String)
|
40
|
+
@digits = digits.each_char.to_a
|
41
|
+
else
|
42
|
+
@digits = digits
|
43
|
+
end
|
44
|
+
@max_base = @digits.size
|
45
|
+
@lowercase = @digits.all? { |d| d.downcase == d }
|
46
|
+
@uppercase = @digits.all? { |d| d.upcase == d }
|
47
|
+
@downcase_digits = @digits.map(&:downcase)
|
48
|
+
if @digits.uniq.size != @max_base
|
49
|
+
raise "Inconsistent digits"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def uppercase=(v)
|
54
|
+
@uppercase = v
|
55
|
+
self.digits = @digits.map(&:upcase) if v
|
56
|
+
end
|
57
|
+
|
58
|
+
def lowercase=(v)
|
59
|
+
@lowercase = v
|
60
|
+
self.digits = @digits.map(&:downcase) if v
|
61
|
+
end
|
62
|
+
|
63
|
+
def case_sensitive?
|
64
|
+
case_sensitive
|
65
|
+
end
|
66
|
+
|
67
|
+
def is_digit?(digit_symbol, options={})
|
68
|
+
base = options[:base] || @max_base
|
69
|
+
raise "Invalid base" if base > @max_base
|
70
|
+
v = digit_value(digit_symbol)
|
71
|
+
v && v < base
|
72
|
+
end
|
73
|
+
|
74
|
+
def digit_value(digit)
|
75
|
+
if @case_sensitive
|
76
|
+
@digits.index(digit)
|
77
|
+
else
|
78
|
+
@downcase_digits.index(digit.downcase)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def digit_symbol(v, options={})
|
83
|
+
base = options[:base] || @max_base
|
84
|
+
raise "Invalid base" if base > @max_base
|
85
|
+
v >= 0 && v < base ? @digits[v] : nil
|
86
|
+
end
|
87
|
+
|
88
|
+
# Convert sequence of digits to its text representation.
|
89
|
+
# The nil value can be used in the digits sequence to
|
90
|
+
# represent the group separator.
|
91
|
+
def digits_text(digit_values, options={})
|
92
|
+
insignificant_digits = options[:insignificant_digits] || 0
|
93
|
+
num_digits = digit_values.reduce(0) { |num, digit|
|
94
|
+
digit.nil? ? num : num + 1
|
95
|
+
}
|
96
|
+
num_digits -= insignificant_digits
|
97
|
+
digit_values.map { |d|
|
98
|
+
if d.nil?
|
99
|
+
options[:separator]
|
100
|
+
else
|
101
|
+
num_digits -= 1
|
102
|
+
if num_digits >= 0
|
103
|
+
digit_symbol(d, options)
|
104
|
+
else
|
105
|
+
options[:insignificant_symbol]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
}.join
|
109
|
+
end
|
110
|
+
|
111
|
+
def parameters
|
112
|
+
params = {}
|
113
|
+
params[:digits] = @digits
|
114
|
+
params[:case_sensitive] = @case_sensitive
|
115
|
+
params[:uppercase] = @uppercase
|
116
|
+
params[:lowercase] = @lowercase
|
117
|
+
params
|
118
|
+
end
|
119
|
+
|
120
|
+
def to_s
|
121
|
+
# TODO: show only non-defaults
|
122
|
+
args = []
|
123
|
+
if @digits != DEFAULT_DIGITS
|
124
|
+
args << @digits.to_s
|
125
|
+
end
|
126
|
+
if @max_base != @digits.size
|
127
|
+
args << "max_base: #{@max_base}"
|
128
|
+
end
|
129
|
+
if @case_sensitive
|
130
|
+
args << "case_sensitive: #{case_sensitive.inspect}"
|
131
|
+
end
|
132
|
+
if @uppercase
|
133
|
+
args << "uppercase: #{uppercase.inspect}"
|
134
|
+
end
|
135
|
+
if @lowercase
|
136
|
+
args << "lowercase: #{lowercase.inspect}"
|
137
|
+
end
|
138
|
+
"Digits[#{args.join(', ')}]"
|
139
|
+
end
|
140
|
+
|
141
|
+
def inspect
|
142
|
+
"Format::Symbols::#{self}"
|
143
|
+
end
|
144
|
+
|
145
|
+
def dup
|
146
|
+
Format::Symbols::Digits[parameters]
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
|
151
|
+
def extract_options(*args)
|
152
|
+
options = {}
|
153
|
+
args = args.first if args.size == 1 && args.first.kind_of?(Array)
|
154
|
+
args.each do |arg|
|
155
|
+
case arg
|
156
|
+
when Hash
|
157
|
+
options.merge! arg
|
158
|
+
when String, Array
|
159
|
+
options[:digits] = arg
|
160
|
+
when Format::Symbols::Digits
|
161
|
+
options.merge! arg.parameters
|
162
|
+
when :uppercase, :downcase
|
163
|
+
send :"#{arg}=", true
|
164
|
+
else
|
165
|
+
raise "Invalid Symbols::Digits definition"
|
166
|
+
end
|
167
|
+
end
|
168
|
+
options
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module Numerals
|
2
|
+
|
3
|
+
# Padding a number to a given width
|
4
|
+
class Format::Symbols::Padding < FormattingAspect
|
5
|
+
|
6
|
+
# Parameters:
|
7
|
+
#
|
8
|
+
# * :width field width (0 for no padding)
|
9
|
+
# * :fill filling symbol; nil for no padding;
|
10
|
+
# 0 to use the digit zero; otherwise should be a String
|
11
|
+
# * :adjust adjust mode: :left, :right, :integer, :center
|
12
|
+
#
|
13
|
+
def initialize(*args)
|
14
|
+
@width = 0
|
15
|
+
@fill = nil
|
16
|
+
@adjust = :right
|
17
|
+
set! *args
|
18
|
+
end
|
19
|
+
|
20
|
+
include ModalSupport::StateEquivalent
|
21
|
+
|
22
|
+
set do |*args|
|
23
|
+
options = extract_options(*args)
|
24
|
+
options.each do |option, value|
|
25
|
+
send :"#{option}=", value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_accessor :width, :fill, :adjust
|
30
|
+
|
31
|
+
def leading_zeros=(width)
|
32
|
+
@width = width
|
33
|
+
@fill = 0
|
34
|
+
@adjust = :internal
|
35
|
+
end
|
36
|
+
|
37
|
+
def padded?
|
38
|
+
@width > 0 && @fill && @fill != ''
|
39
|
+
end
|
40
|
+
|
41
|
+
def parameters
|
42
|
+
{ width: width, fill: fill, adjust: adjust }
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
params = []
|
47
|
+
if fill == 0 && adjust == :internal
|
48
|
+
params << "leading_zeros: #{width}"
|
49
|
+
else
|
50
|
+
if width != 0
|
51
|
+
params << "width: #{width}"
|
52
|
+
end
|
53
|
+
if fill
|
54
|
+
params << "fill: #{fill.inspect}"
|
55
|
+
end
|
56
|
+
if adjust != :right || !params.empty?
|
57
|
+
params << "adjust: #{adjust.inspect}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
"Padding[#{params.join(', ')}]"
|
61
|
+
end
|
62
|
+
|
63
|
+
def inspect
|
64
|
+
"Format::Symbols::#{to_s}"
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns size (characters of left, internal and right padding)
|
68
|
+
# for a number of given width (without padding)
|
69
|
+
def padding_sizes(number_size)
|
70
|
+
left_padding_size = internal_padding_size = right_padding_size = 0
|
71
|
+
padding_size = width - number_size
|
72
|
+
if padding_size > 0 && padded?
|
73
|
+
case adjust
|
74
|
+
when :left
|
75
|
+
left_padding_size = padding_size
|
76
|
+
when :right
|
77
|
+
right_padding_size = padding_size
|
78
|
+
when :internal
|
79
|
+
internal_padding_size = padding_size
|
80
|
+
when :center
|
81
|
+
left_padding_size = (padding_size + 1) / 2
|
82
|
+
right_padding_size = padding_size - left_padding_size
|
83
|
+
end
|
84
|
+
end
|
85
|
+
[left_padding_size, internal_padding_size, right_padding_size]
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def extract_options(*args)
|
91
|
+
options = {}
|
92
|
+
args = args.first if args.size == 1 && args.first.kind_of?(Array)
|
93
|
+
args.each do |arg|
|
94
|
+
case arg
|
95
|
+
when Integer
|
96
|
+
options.merge! width: arg
|
97
|
+
when String
|
98
|
+
options.merge! fill: arg
|
99
|
+
when :left, :right, :internal, :center
|
100
|
+
options.merge! adjust: arg
|
101
|
+
when Hash
|
102
|
+
options.merge! arg
|
103
|
+
when Format::Symbols::Padding
|
104
|
+
options.merge! arg.parameters
|
105
|
+
end
|
106
|
+
end
|
107
|
+
options
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
data/lib/numerals/version.rb
CHANGED
data/test/test_format.rb
CHANGED
@@ -121,4 +121,51 @@ class TestFormat < Test::Unit::TestCase # < Minitest::Test
|
|
121
121
|
assert_equal 'PLUS', f2.symbols.plus
|
122
122
|
end
|
123
123
|
|
124
|
+
def test_repeat_aspect
|
125
|
+
s = Format::Symbols[]
|
126
|
+
assert_equal '<', s.repeat_begin
|
127
|
+
assert_equal '>', s.repeat_end
|
128
|
+
assert_equal '...', s.repeat_suffix
|
129
|
+
refute s.repeat_delimited
|
130
|
+
assert_equal 3, s.repeat_count
|
131
|
+
assert s.repeating
|
132
|
+
|
133
|
+
s.set_repeat! false
|
134
|
+
refute s.repeating
|
135
|
+
|
136
|
+
s.set_repeat! true, delimiters: '[', count: 2, suffix: '****'
|
137
|
+
assert_equal '[', s.repeat_begin
|
138
|
+
assert_nil s.repeat_end
|
139
|
+
assert_equal '****', s.repeat_suffix
|
140
|
+
refute s.repeat_delimited
|
141
|
+
assert_equal 2, s.repeat_count
|
142
|
+
assert s.repeating
|
143
|
+
|
144
|
+
s.set_repeat! true, delimiters: ['>', '<'], delimited: true
|
145
|
+
assert_equal '>', s.repeat_begin
|
146
|
+
assert_equal '<', s.repeat_end
|
147
|
+
assert s.repeat_delimited
|
148
|
+
end
|
149
|
+
|
150
|
+
def test_padding_aspect
|
151
|
+
f = Format[]
|
152
|
+
refute f.padded?
|
153
|
+
assert_equal :right, f.padding.adjust
|
154
|
+
f = f[padding:[10, ' ', :left]]
|
155
|
+
assert f.padded?
|
156
|
+
assert_equal :left, f.padding.adjust
|
157
|
+
assert_equal ' ', f.padding.fill
|
158
|
+
end
|
159
|
+
|
160
|
+
def tst_grouping
|
161
|
+
f = Format[]
|
162
|
+
refute f.grouping?
|
163
|
+
f.set_grouping! :thousands
|
164
|
+
assert f.grouping?
|
165
|
+
assert_equal [3], f.grouping
|
166
|
+
f.set_grouping! false
|
167
|
+
refute f.grouping?
|
168
|
+
assert_equal [], f.grouping
|
169
|
+
end
|
170
|
+
|
124
171
|
end
|
data/test/test_format_input.rb
CHANGED
@@ -223,4 +223,29 @@ class TestFormatInput < Test::Unit::TestCase # < Minitest::Test
|
|
223
223
|
|
224
224
|
end
|
225
225
|
|
226
|
+
def test_padding
|
227
|
+
f = Format[padding: '*']
|
228
|
+
assert_equal 643454333.32, f.read("******643,454,333.32", type: Float)
|
229
|
+
assert_equal -643454333.32, f.read("*****-643,454,333.32", type: Float)
|
230
|
+
assert_equal -643454333.32, f.read("-*****643,454,333.32", type: Float)
|
231
|
+
assert_equal 643454333.32, f.read("+*****643,454,333.32", type: Float)
|
232
|
+
assert_equal 643454333.32, f.read("643,454,333.32******", type: Float)
|
233
|
+
assert_equal -643454333.32, f.read("-643,454,333.32*****", type: Float)
|
234
|
+
assert_equal 643454333.32, f.read("***643,454,333.32***", type: Float)
|
235
|
+
assert_equal 643454333.32, f.read("***643,454,333.32***", type: Float)
|
236
|
+
f.set_leading_zeros! 10
|
237
|
+
assert_equal 123, f.read("0000000123", type: Integer)
|
238
|
+
assert_equal -123, f.read("-000000123", type: Integer)
|
239
|
+
assert_equal 123.5, f.read("00000123.5", type: Float)
|
240
|
+
assert_equal -123.5, f.read("-00000123.5000", type: Float)
|
241
|
+
assert_equal 123.5, f.read("00000123.5000", type: Float)
|
242
|
+
assert_equal 100.5, f.read("00000100.5", type: Float)
|
243
|
+
assert_equal -100.5, f.read("-0000100.5", type: Float)
|
244
|
+
assert_equal Rational(1,3), f.read("000000.<3>", type: Rational)
|
245
|
+
assert_equal Rational(-1,3), f.read("-00000.<3>", type: Rational)
|
246
|
+
f.set_padding! '*'
|
247
|
+
assert_equal Flt::DecNum('0.667'), f.read("********0.667*******", type: Flt::DecNum)
|
248
|
+
assert_equal Flt::DecNum('-0.667'), f.read("*******-0.667*******", type: Flt::DecNum)
|
249
|
+
end
|
250
|
+
|
226
251
|
end
|
data/test/test_format_output.rb
CHANGED
@@ -229,7 +229,7 @@ class TestFormatOutput < Test::Unit::TestCase # < Minitest::Test
|
|
229
229
|
def test_write_binnum_hex
|
230
230
|
context = Flt::BinNum::IEEEDoubleContext
|
231
231
|
x = Flt::BinNum('0.1', :fixed, context: context)
|
232
|
-
assert_equal "
|
232
|
+
assert_equal "0x1.999999999999Ap-4", Format[:hexbin].write(x)
|
233
233
|
end
|
234
234
|
|
235
235
|
def test_write_to_file
|
@@ -786,4 +786,63 @@ class TestFormatOutput < Test::Unit::TestCase # < Minitest::Test
|
|
786
786
|
Format[:short, repeating: false].write(Rational(2469,200))
|
787
787
|
end
|
788
788
|
end
|
789
|
+
|
790
|
+
def test_padding
|
791
|
+
f = Format[padding: [:right, 20, fill: 0]]
|
792
|
+
|
793
|
+
f = Format[padding: [:left, 20, fill: '*']]
|
794
|
+
f = f[symbols: [group_separator: ',', grouping: [3]]]
|
795
|
+
assert_equal "******643,454,333.32", f.write(643454333.32)
|
796
|
+
assert_equal "*****-643,454,333.32", f.write(-643454333.32)
|
797
|
+
assert_equal "-*****643,454,333.32", f[padding: :internal].write(-643454333.32)
|
798
|
+
f = f[padding: :right]
|
799
|
+
assert_equal "643,454,333.32******", f.write(643454333.32)
|
800
|
+
assert_equal "-643,454,333.32*****", f.write(-643454333.32)
|
801
|
+
f = f[padding: :center]
|
802
|
+
assert_equal "***643,454,333.32***", f.write(643454333.32)
|
803
|
+
assert_equal "***-643,454,333.32**", f.write(-643454333.32)
|
804
|
+
f.set_leading_zeros! 10
|
805
|
+
assert_equal "0000000123", f.write(123)
|
806
|
+
assert_equal "-000000123", f.write(-123)
|
807
|
+
assert_equal "00000123.5", f.write(123.5)
|
808
|
+
assert_equal "-0000123.5", f.write(-123.5)
|
809
|
+
f.set_grouping! false
|
810
|
+
assert_equal "0123456789", f.write(123456789)
|
811
|
+
assert_equal "1234567891", f.write(1234567891)
|
812
|
+
assert_equal "12345678912", f.write(12345678912)
|
813
|
+
assert_equal "-123456789", f.write(-123456789)
|
814
|
+
assert_equal "-1234567891", f.write(-1234567891)
|
815
|
+
assert_equal "-12345678912", f.write(-12345678912)
|
816
|
+
f.set! symbols: [repeat_delimited: true]
|
817
|
+
assert_equal "000000.<3>", f.write(Rational(1,3))
|
818
|
+
assert_equal "-00000.<3>", f.write(Rational(-1,3))
|
819
|
+
assert_equal "000000.<6>", f.write(Rational(2,3))
|
820
|
+
assert_equal "-00000.<6>", f.write(Rational(-2,3))
|
821
|
+
f.set! rounding: [places: 3]
|
822
|
+
assert_equal "000000.333", f.write(Rational(1,3))
|
823
|
+
assert_equal "-00000.333", f.write(Rational(-1,3))
|
824
|
+
assert_equal "000000.667", f.write(Rational(2,3))
|
825
|
+
assert_equal "-00000.667", f.write(Rational(-2,3))
|
826
|
+
f.set! rounding: [places: 4]
|
827
|
+
assert_equal "00000.3333", f.write(Rational(1,3))
|
828
|
+
assert_equal "-0000.3333", f.write(Rational(-1,3))
|
829
|
+
assert_equal "00000.6667", f.write(Rational(2,3))
|
830
|
+
assert_equal "-0000.6667", f.write(Rational(-2,3))
|
831
|
+
f.set! padding: [:center, width: 20, fill: '*']
|
832
|
+
f.set! rounding: [places: 3]
|
833
|
+
assert_equal "********0.667*******", f.write(Rational(2,3))
|
834
|
+
assert_equal "*******-0.667*******", f.write(Rational(-2,3))
|
835
|
+
f.set! rounding: [places: 4]
|
836
|
+
assert_equal "*******0.5555*******", f.write(Flt::DecNum('0.5555'))
|
837
|
+
f.set! padding: 8
|
838
|
+
assert_equal "*0.5555*", f.write(Flt::DecNum('0.5555'))
|
839
|
+
f.set! padding: 7
|
840
|
+
assert_equal "*0.5555", f.write(Flt::DecNum('0.5555'))
|
841
|
+
f.set! padding: 6
|
842
|
+
assert_equal "0.5555", f.write(Flt::DecNum('0.5555'))
|
843
|
+
f.set! padding: 5
|
844
|
+
assert_equal "0.5555", f.write(Flt::DecNum('0.5555'))
|
845
|
+
f.set! padding: 4
|
846
|
+
assert_equal "0.5555", f.write(Flt::DecNum('0.5555'))
|
847
|
+
end
|
789
848
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: numerals
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Javier Goizueta
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-04-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: flt
|
@@ -99,8 +99,9 @@ files:
|
|
99
99
|
- lib/numerals/format/notations/text.rb
|
100
100
|
- lib/numerals/format/output.rb
|
101
101
|
- lib/numerals/format/symbols.rb
|
102
|
+
- lib/numerals/format/symbols/digits.rb
|
103
|
+
- lib/numerals/format/symbols/padding.rb
|
102
104
|
- lib/numerals/format/text_parts.rb
|
103
|
-
- lib/numerals/formatting/options.rb
|
104
105
|
- lib/numerals/formatting_aspect.rb
|
105
106
|
- lib/numerals/numeral.rb
|
106
107
|
- lib/numerals/repeat_detector.rb
|
@@ -1,84 +0,0 @@
|
|
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
|