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
@@ -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