numerals 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
@@ -1,20 +1,16 @@
1
- require 'numerals/conversions'
2
- require 'flt/float'
1
+ require 'numerals/conversions/context_conversion'
3
2
 
4
- class Numerals::FloatConversion
3
+ class Numerals::FloatConversion < Numerals::ContextConversion
5
4
 
5
+ # Options:
6
+ #
7
+ # * :input_rounding (optional, a non-exact Rounding or rounding mode)
8
+ # which is used when input is approximate as the assumed rounding
9
+ # mode which would be used so that the result numeral rounds back
10
+ # to the input number
11
+ #
6
12
  def initialize(options={})
7
- @type = Float
8
- @context = @type.context
9
- options = { use_native_float: true }.merge(options)
10
- @use_native_float = options[:use_native_float]
11
- # @rounding_mode if used for :free numeral to number conversion
12
- # and should be the implied rounding mode of the invers conversion
13
- # (number to numeral);
14
- # TODO: it should be possible to assign it for higher level
15
- # formatting handling.
16
- @rounding_mode = @context.rounding
17
- @honor_rounding = true
13
+ super Float, options
18
14
  end
19
15
 
20
16
  def order_of_magnitude(value, options={})
@@ -26,6 +22,19 @@ class Numerals::FloatConversion
26
22
  end
27
23
  end
28
24
 
25
+ def number_of_digits(value, options={})
26
+ base = options[:base] || 10
27
+ if base == @context.radix
28
+ @context.precision
29
+ else
30
+ @context.necessary_digits(base)
31
+ end
32
+ end
33
+
34
+ def exact?(value, options={})
35
+ options[:exact]
36
+ end
37
+
29
38
  # mode is either :exact or :approximate
30
39
  def number_to_numeral(number, mode, rounding)
31
40
  if @context.special?(number)
@@ -49,70 +58,87 @@ class Numerals::FloatConversion
49
58
  end
50
59
  end
51
60
 
61
+ def write(number, exact_input, output_rounding)
62
+ output_base = output_rounding.base
63
+ input_base = @context.radix
64
+
65
+ if @context.special?(number)
66
+ special_float_to_numeral number
67
+ elsif exact_input
68
+ if output_base == input_base && output_rounding.free?
69
+ # akin to number.format(base: output_base, simplified: true)
70
+ general_float_to_numeral number, output_rounding, false
71
+ else
72
+ # akin to number.format(base: output_base, exact: true)
73
+ exact_float_to_numeral number, output_rounding
74
+ end
75
+ else
76
+ if output_base == input_base && output_rounding.preserving?
77
+ # akin to number.format(base: output_base)
78
+ sign, coefficient, exp = @context.split(number)
79
+ Numerals::Numeral.from_coefficient_scale(
80
+ sign*coefficient, exp,
81
+ approximate: true, base: output_base
82
+ )
83
+ elsif output_rounding.simplifying?
84
+ # akin to number.forma(base: output_base, simplify: true)
85
+ general_float_to_numeral number, output_rounding, false
86
+ else
87
+ # akin to number.forma(base: output_base, all_digits: true)
88
+ general_float_to_numeral number, output_rounding, true
89
+ end
90
+ end
91
+ end
92
+
93
+ def read(numeral, exact_input, approximate_simplified)
94
+ if numeral.special?
95
+ special_numeral_to_float numeral
96
+ # elsif numeral.approximate? && !exact_input
97
+ # if approximate_simplified
98
+ # # akin to @context.Num(numeral_text, :short)
99
+ # short_numeral_to_float numeral
100
+ # else
101
+ # # akin to @context.Num(numeral_text, :free)
102
+ # free_numeral_to_float numeral
103
+ # end
104
+ else
105
+ # akin to @context.Num(numeral_text, :fixed)
106
+ fixed_numeral_to_float numeral
107
+ end
108
+ end
109
+
52
110
  private
53
111
 
54
112
  def special_float_to_numeral(x)
55
113
  if x.nan?
56
- Numeral.nan
114
+ Numerals::Numeral.nan
57
115
  elsif x.infinite?
58
- Numeral.infinity @context.sign(x)
116
+ Numerals::Numeral.infinity @context.sign(x)
59
117
  end
60
118
  end
61
119
 
62
120
  def exact_float_to_numeral(number, rounding)
63
121
  quotient = number.to_r
64
- numeral = Numeral.from_quotient(quotient, base: rounding.base)
65
- unless rounding.exact?
122
+ numeral = Numerals::Numeral.from_quotient(quotient, base: rounding.base)
123
+ unless rounding.free?
66
124
  numeral = rounding.round(numeral)
67
125
  end
68
126
  numeral
69
127
  end
70
128
 
71
129
  def approximate_float_to_numeral(number, rounding)
72
- all_digits = !rounding.exact?
130
+ all_digits = !rounding.free?
73
131
  general_float_to_numeral(number, rounding, all_digits)
74
132
  end
75
133
 
76
- # def fixed_float_to_numeral(number, rounding)
77
- # # adjust to rounding.precision
78
- # if rounding.exact?
79
- # # if simplify
80
- # # number = @context.rationalize(simplify)
81
- # # end
82
- # exact_float_to_numeral number, rounding.base
83
- # else
84
- # if rounding.base == 10 && @use_native_float
85
- # native_float_to_numeral number, rounding
86
- # else
87
- # general_float_to_numeral number, rounding, true
88
- # end
89
- # end
90
- # end
91
-
92
- # def free_float_to_numeral(number, rounding)
93
- # # free mode ignores output precision (rounding) and
94
- # # produces the result based only on the number precision
95
- # rounding = Rounding[:exact, base: rounding.base]
96
- # general_float_to_numeral number, rounding, false
97
- # end
98
-
99
- def native_float_to_numeral(number, rounding)
100
- need_to_round = (rounding.mode != @context.rounding)
101
- n = need_to_round ? Float::DECIMAL_DIG : rounding.precision
102
- txt = format("%.*e", n - 1, number)
103
- numeral = text_to_numeral(txt, normalize: :approximate) # C-Locale text to numeral...
104
- numeral = rounding.round(numeral) if need_to_round
105
- numeral
106
- end
107
-
108
134
  def general_float_to_numeral(x, rounding, all_digits)
109
- sign, coefficient, exponent = x.split
110
- precision = x.number_of_digits
135
+ sign, coefficient, exponent = @context.split(x)
136
+ precision = @context.precision
111
137
  output_base = rounding.base
112
138
 
113
- # here rounding_mode should be not the output rounding mode, but the rounding mode used for input
114
- # we'll assume rounding.mode will be used for input unless it is exact
115
- rounding_mode = rounding.exact? ? @context.rounding : rounding.mode
139
+ # here rounding_mode is not the output rounding mode, but the rounding mode used for input
140
+ rounding_mode = (@input_rounding || rounding).mode
141
+
116
142
  formatter = Flt::Support::Formatter.new(
117
143
  @context.radix, @context.etiny, output_base, raise_on_repeat: false
118
144
  )
@@ -122,10 +148,14 @@ class Numerals::FloatConversion
122
148
 
123
149
  dec_pos, digits = formatter.digits
124
150
  rep_pos = formatter.repeat
125
- numeral = Numeral[digits, sign: sign, point: dec_pos, rep_pos: rep_pos, base: output_base]
126
- if all_digits
127
- numeral = rounding.round(numeral, formatter.round_up)
128
- end
151
+
152
+ normalization = :approximate
153
+
154
+ numeral = Numerals::Numeral[digits, sign: sign, point: dec_pos, rep_pos: rep_pos, base: output_base,
155
+ normalize: normalization]
156
+
157
+ numeral = rounding.round(numeral, round_up: formatter.round_up)
158
+
129
159
  numeral
130
160
  end
131
161
 
@@ -139,8 +169,7 @@ class Numerals::FloatConversion
139
169
  end
140
170
 
141
171
  def fixed_numeral_to_float(numeral)
142
- # consider:
143
- # return exact_numeral_to_float(numeral) if numeral.exact?
172
+ return exact_numeral_to_float(numeral) if numeral.exact?
144
173
  if numeral.base == @context.radix
145
174
  same_base_numeral_to_float numeral
146
175
  else
@@ -148,9 +177,9 @@ class Numerals::FloatConversion
148
177
  # to a numeral preserving its value.
149
178
  representable_digits = @context.representable_digits(numeral.base)
150
179
  k = numeral.scale
151
- if !@honor_rounding && numeral.digits.size <= representable_digits && k.abs <= representable_digits
180
+ if !@input_rounding && numeral.digits.size <= representable_digits && k.abs <= representable_digits
152
181
  representable_numeral_to_float numeral
153
- elsif !@honor_rounding && (k > 0 && numeral.point < 2*representable_digits)
182
+ elsif !@input_rounding && (k > 0 && numeral.point < 2*representable_digits)
154
183
  near_representable_numeral_to_float numeral
155
184
  elsif numeral.base.modulo(@context.radix) == 0
156
185
  conmensurable_base_numeral_to_float numeral
@@ -172,6 +201,12 @@ class Numerals::FloatConversion
172
201
  general_numeral_to_float numeral, :free
173
202
  end
174
203
 
204
+ def short_numeral_to_float(numeral)
205
+ # raise "Invalid Conversion" # Float does not support short (arbitrary precision)
206
+ # fixed_numeral_to_float numeral
207
+ general_numeral_to_float numeral, :short
208
+ end
209
+
175
210
  def same_base_numeral_to_float(numeral)
176
211
  sign, coefficient, scale = numeral.split
177
212
  @context.Num(sign, coefficient, scale)
@@ -203,10 +238,10 @@ class Numerals::FloatConversion
203
238
  def general_numeral_to_float(numeral, mode)
204
239
  sign, coefficient, scale = numeral.split
205
240
  reader = Flt::Support::Reader.new(mode: mode)
206
- if mode == :fixed
207
- rounding_mode = @context.rounding
241
+ if @input_rounding
242
+ rounding_mode = @input_rounding.mode
208
243
  else
209
- rounding_mode = @rounding_mode
244
+ rounding_Mode = @context.rounding
210
245
  end
211
246
  reader.read(@context, rounding_mode, sign, coefficient, scale, numeral.base).tap do
212
247
  # @exact = reader.exact?
@@ -215,12 +250,12 @@ class Numerals::FloatConversion
215
250
 
216
251
  end
217
252
 
218
- def Float.numerals_conversion
219
- Numerals::FloatConversion.new
253
+ def Float.numerals_conversion(options = {})
254
+ Numerals::FloatConversion.new(options)
220
255
  end
221
256
 
222
257
  class <<Float.context
223
- def numerals_conversion
224
- Numerals::FloatConversion.new
258
+ def numerals_conversion(options = {})
259
+ Numerals::FloatConversion.new(options)
225
260
  end
226
261
  end
@@ -1,28 +1,20 @@
1
- require 'numerals/conversions'
2
- require 'flt'
3
-
4
- class Numerals::FltConversion
5
-
6
- def initialize(context_or_type)
7
- if Class === context_or_type && context_or_type < Flt::Num
8
- @type = context_or_type
9
- @context = @type.context
10
- elsif Flt::Num::ContextBase === context_or_type
11
- @context = context_or_type
12
- @type = @context.num_class
13
- else
14
- raise "Invalid FltConversion definition"
15
- end
16
- # @rounding_mode if used for :free numeral to number conversion
17
- # and should be the implied rounding mode of the invers conversion
18
- # (number to numeral);
19
- # TODO: it should be possible to assign it for higher level
20
- # formatting handling.
21
- @rounding_mode = @context.rounding
1
+ require 'numerals/conversions/context_conversion'
2
+
3
+ class Numerals::FltConversion < Numerals::ContextConversion
4
+
5
+ # Options:
6
+ #
7
+ # * :input_rounding (optional, a non-exact Rounding or rounding mode)
8
+ # which is used when input is approximate as the assumed rounding
9
+ # mode which would be used so that the result numeral rounds back
10
+ # to the input number. :context can be used to use the
11
+ # numeric context as input rounding.
12
+ # input_rounding is also used to round input ...
13
+ #
14
+ def initialize(context_or_type, options={})
15
+ super
22
16
  end
23
17
 
24
- attr_reader :context, :type
25
-
26
18
  def order_of_magnitude(value, options={})
27
19
  base = options[:base] || 10 # value.num_class.radix
28
20
  if value.class.radix == base
@@ -32,6 +24,19 @@ class Numerals::FltConversion
32
24
  end
33
25
  end
34
26
 
27
+ def number_of_digits(value, options={})
28
+ base = options[:base] || 10
29
+ if base == @context.radix
30
+ value.number_of_digits
31
+ else
32
+ x.class.context[precision: value.number_of_digits].necessary_digits(base)
33
+ end
34
+ end
35
+
36
+ def exact?(value, options={})
37
+ options[:exact]
38
+ end
39
+
35
40
  # mode is either :exact or :approximate
36
41
  def number_to_numeral(number, mode, rounding)
37
42
  if number.special? # @context.special?(number)
@@ -55,6 +60,54 @@ class Numerals::FltConversion
55
60
  end
56
61
  end
57
62
 
63
+ def write(number, exact_input, output_rounding)
64
+ output_base = output_rounding.base
65
+ input_base = @context.radix
66
+
67
+ if number.special? # @context.special?(number)
68
+ special_num_to_numeral number
69
+ elsif exact_input
70
+ if output_base == input_base && output_rounding.free?
71
+ # akin to number.format(base: output_base, simplified: true)
72
+ general_num_to_numeral number, output_rounding, false
73
+ else
74
+ # akin to number.format(base: output_base, exact: true)
75
+ exact_num_to_numeral number, output_rounding
76
+ end
77
+ else
78
+ if output_base == input_base && output_rounding.preserving?
79
+ # akin to number.format(base: output_base)
80
+ Numeral.from_coefficient_scale(
81
+ number.sign*number.coefficient, number.integral_exponent,
82
+ approximate: true, base: output_base
83
+ )
84
+ elsif output_rounding.simplifying?
85
+ # akin to number.forma(base: output_base, simplify: true)
86
+ general_num_to_numeral number, output_rounding, false
87
+ else
88
+ # akin to number.forma(base: output_base, all_digits: true)
89
+ general_num_to_numeral number, output_rounding, true
90
+ end
91
+ end
92
+ end
93
+
94
+ def read(numeral, exact_input, approximate_simplified)
95
+ if numeral.special?
96
+ special_numeral_to_num numeral
97
+ elsif numeral.approximate? && !exact_input
98
+ if approximate_simplified
99
+ # akin to @context.Num(numeral_text, :short)
100
+ short_numeral_to_num numeral
101
+ else
102
+ # akin to @context.Num(numeral_text, :free)
103
+ free_numeral_to_num numeral
104
+ end
105
+ else
106
+ # akin to @context.Num(numeral_text, :fixed)
107
+ fixed_numeral_to_num numeral
108
+ end
109
+ end
110
+
58
111
  private
59
112
 
60
113
  def special_num_to_numeral(x)
@@ -67,26 +120,26 @@ class Numerals::FltConversion
67
120
 
68
121
  def exact_num_to_numeral(number, rounding)
69
122
  quotient = number.to_r
70
- numeral = Numeral.from_quotient(quotient, base: rounding.base)
71
- unless rounding.exact?
123
+ numeral = Numerals::Numeral.from_quotient(quotient, base: rounding.base)
124
+ unless rounding.free?
72
125
  numeral = rounding.round(numeral)
73
126
  end
74
127
  numeral
75
128
  end
76
129
 
77
130
  def approximate_num_to_numeral(number, rounding)
78
- all_digits = !rounding.exact?
131
+ all_digits = !rounding.free?
79
132
  general_num_to_numeral(number, rounding, all_digits)
80
133
  end
81
134
 
82
135
  def general_num_to_numeral(x, rounding, all_digits)
83
- sign, coefficient, exponent = x.split
136
+ sign, coefficient, exponent = x.split # @context.split(x)
84
137
  precision = x.number_of_digits
85
138
  output_base = rounding.base
86
139
 
87
- # here rounding_mode should be not the output rounding mode, but the rounding mode used for input
88
- # we'll assume rounding.mode will be used for input unless it is exact
89
- rounding_mode = rounding.exact? ? @context.rounding : rounding.mode
140
+ # here rounding_mode is not the output rounding mode, but the rounding mode used for input
141
+ rounding_mode = (@input_rounding || rounding).mode
142
+
90
143
  formatter = Flt::Support::Formatter.new(
91
144
  @context.radix, @context.etiny, output_base, raise_on_repeat: false
92
145
  )
@@ -96,10 +149,13 @@ class Numerals::FltConversion
96
149
 
97
150
  dec_pos, digits = formatter.digits
98
151
  rep_pos = formatter.repeat
99
- numeral = Numeral[digits, sign: sign, point: dec_pos, rep_pos: formatter.repeat, base: output_base]
100
- if all_digits
101
- numeral = rounding.round(numeral, formatter.round_up)
102
- end
152
+
153
+ normalization = :approximate
154
+
155
+ numeral = Numerals::Numeral[digits, sign: sign, point: dec_pos, rep_pos: formatter.repeat, base: output_base, normalize: normalization]
156
+
157
+ numeral = rounding.round(numeral, round_up: formatter.round_up)
158
+
103
159
  numeral
104
160
  end
105
161
 
@@ -116,10 +172,17 @@ class Numerals::FltConversion
116
172
  # consider:
117
173
  # return exact_numeral_to_num(numeral) if numeral.exact?
118
174
  if numeral.base == @context.radix
119
- numeral = numeral.approximate(@context.precision) unless @context.exact?
175
+ unless @context.exact?
176
+ rounding = Rounding[@context.rounding, precision: @context.precision, base: @context.radix]
177
+ numeral = rounding.round(numeral)
178
+ end
120
179
  same_base_numeral_to_num numeral
121
180
  else
122
- general_numeral_to_num numeral, :fixed
181
+ if numeral.repeating? # numeral.exact?
182
+ exact_numeral_to_num(numeral)
183
+ else
184
+ general_numeral_to_num numeral, :fixed
185
+ end
123
186
  end
124
187
  end
125
188
 
@@ -133,16 +196,24 @@ class Numerals::FltConversion
133
196
  end
134
197
 
135
198
  def free_numeral_to_num(numeral)
136
- general_numeral_to_num numeral, :free
199
+ if numeral.base == @context.radix
200
+ same_base_numeral_to_num numeral
201
+ else
202
+ general_numeral_to_num numeral, :free
203
+ end
204
+ end
205
+
206
+ def short_numeral_to_num(numeral)
207
+ general_numeral_to_num numeral, :short
137
208
  end
138
209
 
139
210
  def general_numeral_to_num(numeral, mode)
140
211
  sign, coefficient, scale = numeral.split
141
212
  reader = Flt::Support::Reader.new(mode: mode)
142
- if mode == :fixed
143
- rounding_mode = @context.rounding
213
+ if @input_rounding
214
+ rounding_mode = @input_rounding.mode
144
215
  else
145
- rounding_mode = @rounding_mode
216
+ rounding_Mode = @context.rounding
146
217
  end
147
218
  reader.read(@context, rounding_mode, sign, coefficient, scale, numeral.base).tap do
148
219
  # @exact = reader.exact?
@@ -151,12 +222,12 @@ class Numerals::FltConversion
151
222
 
152
223
  end
153
224
 
154
- def (Flt::Num).numerals_conversion
155
- Numerals::FltConversion.new(self)
225
+ def (Flt::Num).numerals_conversion(options = {})
226
+ Numerals::FltConversion.new(self, options)
156
227
  end
157
228
 
158
229
  class Flt::Num::ContextBase
159
- def numerals_conversion
160
- Numerals::FltConversion.new(self)
230
+ def numerals_conversion(options = {})
231
+ Numerals::FltConversion.new(self, options)
161
232
  end
162
233
  end
@@ -8,6 +8,10 @@ class Numerals::IntegerConversion
8
8
  class InvalidConversion < RuntimeError
9
9
  end
10
10
 
11
+ def type
12
+ Integer
13
+ end
14
+
11
15
  def order_of_magnitude(value, options={})
12
16
  base = options[:base] || 10
13
17
  if base == 2 && value.respond_to?(:bit_length)
@@ -17,10 +21,19 @@ class Numerals::IntegerConversion
17
21
  end
18
22
  end
19
23
 
24
+ def number_of_digits(value, options={})
25
+ # order_of_magnitude(value, options)
26
+ 0 # this is needed only for non-exact values
27
+ end
28
+
29
+ def exact?(value, options={})
30
+ true
31
+ end
32
+
20
33
  def number_to_numeral(number, mode, rounding)
21
34
  # Rational.numerals_conversion Rational(number), mode, rounding
22
- numeral = Numeral.from_quotient(number, 1)
23
- numeral = rounding.round(numeral) unless rounding.exact?
35
+ numeral = Numerals::Numeral.from_quotient(number, 1)
36
+ numeral = rounding.round(numeral) # unless rounding.free?
24
37
  numeral
25
38
  end
26
39
 
@@ -32,8 +45,24 @@ class Numerals::IntegerConversion
32
45
  rational.numerator
33
46
  end
34
47
 
48
+ def write(number, exact_input, output_rounding)
49
+ output_base = output_rounding.base
50
+ numeral = Numerals::Numeral.from_quotient(number, 1, base: output_base)
51
+ numeral = output_rounding.round(numeral) # unless output_rounding.free?
52
+ numeral
53
+ end
54
+
55
+ def read(numeral, exact_input, approximate_simplified)
56
+ rational = Rational.numerals_conversion.read numeral, exact_input, approximate_simplified
57
+ if rational.denominator != 1
58
+ raise InvalidConversion, "Invalid numeral to rational conversion"
59
+ end
60
+ rational.numerator
61
+ end
62
+
63
+
35
64
  end
36
65
 
37
- def Integer.numerals_conversion
66
+ def Integer.numerals_conversion(options = {})
38
67
  Numerals::IntegerConversion.instance
39
68
  end
@@ -5,6 +5,10 @@ class Numerals::RationalConversion
5
5
 
6
6
  include Singleton
7
7
 
8
+ def type
9
+ Rational
10
+ end
11
+
8
12
  def order_of_magnitude(value, options={})
9
13
  base = options[:base] || 10
10
14
  if base == 10
@@ -14,10 +18,18 @@ class Numerals::RationalConversion
14
18
  end
15
19
  end
16
20
 
21
+ def number_of_digits(value, options={})
22
+ return 0 # this is needed only for non-exact values
23
+ end
24
+
25
+ def exact?(value, options={})
26
+ true
27
+ end
28
+
17
29
  def number_to_numeral(number, mode, rounding)
18
30
  q = [number.numerator, number.denominator]
19
- numeral = Numeral.from_quotient(q)
20
- numeral = rounding.round(numeral) unless rounding.exact?
31
+ numeral = Numerals::Numeral.from_quotient(q)
32
+ numeral = rounding.round(numeral) # unless rounding.free?
21
33
  numeral
22
34
  end
23
35
 
@@ -25,8 +37,20 @@ class Numerals::RationalConversion
25
37
  Rational(*numeral.to_quotient)
26
38
  end
27
39
 
40
+ def write(number, exact_input, output_rounding)
41
+ output_base = output_rounding.base
42
+ q = [number.numerator, number.denominator]
43
+ numeral = Numerals::Numeral.from_quotient(q, base: output_base)
44
+ numeral = output_rounding.round(numeral) # unless output_rounding.free?
45
+ numeral
46
+ end
47
+
48
+ def read(numeral, exact_input, approximate_simplified)
49
+ Rational(*numeral.to_quotient)
50
+ end
51
+
28
52
  end
29
53
 
30
- def Rational.numerals_conversion
54
+ def Rational.numerals_conversion(options = {})
31
55
  Numerals::RationalConversion.instance
32
56
  end