Module: Numerals::Format::Input

Included in:
Numerals::Format
Defined in:
lib/numerals/format/input.rb

Overview

Formatted input implementation

Instance Method Summary collapse

Instance Method Details

#conversion_in(numeral, options) ⇒ Object



139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/numerals/format/input.rb', line 139

def conversion_in(numeral, options)
  options = options.merge(
    exact: numeral.exact?, # @exact_input, Also: if we want to force :fixed format
    simplify: @rounding.simplifying?, # applies to approx input only => :short
  )
  type_options = { input_rounding: @input_rounding || @rounding }
  if options[:type_options]
    options[:type_options] = type_options.merge(options[:type_options])
  else
    options[:type_options] = type_options
  end
  Conversions.read(numeral, options)
end

#partition_in(text_parts) ⇒ Object



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/numerals/format/input.rb', line 52

def partition_in(text_parts)
  if text_parts.special?
    # Read special number
    nan = /#{@symbols.regexp(:nan, case_sensitivity: true)}/
    inf = /
      #{@symbols.regexp(:plus, :minus, case_sensitivity: true)}?
      \s*
      #{@symbols.regexp(:infinity, case_sensitivity: true)}
    /x
    minus = /#{@symbols.regexp(:minus, case_sensitivity: true)}/
    if nan.match(text_parts.special)
      numeral = Numeral[:nan]
    elsif match = inf.match(text_parts.special)
      if match[1] && match[1] =~ minus
        sign = -1
      else
        sign = +1
      end
      numeral = Numeral[:infinity, sign: sign]
    else
      raise Format::InvaludNumberFormat, "Invalid number"
    end
  else
    # Parse and convert text parts to values
    input_rounding = @input_rounding || @rounding
    input_base = significand_base

    if !@symbols.repeating && (text_parts.repeat || text_parts.detect_repeat)
      raise Format::InvalidRepeatingNumeral, "Invalid format: unexpected repeating numeral"
    end

    minus = /#{@symbols.regexp(:minus, case_sensitivity: true)}/
    if text_parts.sign? && text_parts.sign =~ minus
      sign = -1
    else
      sign = +1
    end

    integer_digits = []
    if text_parts.integer?
      integer_digits = @symbols.digits_values(text_parts.integer, base: input_base)
    end

    fractional_digits = []
    if text_parts.fractional?
      fractional_digits = @symbols.digits_values(text_parts.fractional, base: input_base)
    end

    exponent_value = 0
    if text_parts.exponent?
      exponent_value = text_parts.exponent.to_i
    end

    point = integer_digits.size

    if text_parts.detect_repeat? # repeat_suffix found
      digits = integer_digits + fractional_digits
      digits, repeat = RepeatDetector.detect(digits, @symbols.repeat_count - 1)
    elsif text_parts.repeat?
      repeat_digits = @symbols.digits_values(text_parts.repeat, base: input_base)
      digits = integer_digits + fractional_digits
      repeat = digits.size
      digits += repeat_digits
    else
      digits = integer_digits + fractional_digits
      repeat = nil
    end

    if @mode.base_scale > 1
      # De-scale the significand base
      digits = Format::BaseScaler.ungrouped_digits(digits, base, @mode.base_scale)
      point  *= @mode.base_scale
      repeat *= @mode.base_scale if repeat
    end

    point += exponent_value

    # Generate Numeral
    if repeat || @exact_input
      normalization = :exact
    else
      normalization = :approximate
    end
    numeral = Numeral[digits, sign: sign, point: point, repeat: repeat, base: base, normalize: normalization]
  end
end

#read(*args) ⇒ Object



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/numerals/format/input.rb', line 6

def read(*args)
  options = {}
  args.each do |arg|
    case arg
    when String
      options.merge! text: arg
    when Hash
      options.merge! arg
    else
      options.merge! type: arg
    end
  end

  text = options[:text]
  options[:type] ||= options[:as]

  # 1. Obtain destination type
  selector = options[:context] || options[:type]
  conversion = Conversions[selector, options[:type_options]]
  if conversion
    type = conversion.type
  else
    type = options[:type]
    if type != Numeral
      raise Format::InvalidNumericType, "Invalid type #{selector.inspect}"
    end
  end

  # if conversion.is_a?(ContextConversion)
  #   context = conversion.context
  # end

  # 2. dissassemble (parse notation): text notation => text parts
  text_parts = Format.disassemble(@notation, self, text)

  # 3. Convert text parts to values and generate a Numeral
  numeral = partition_in(text_parts)

  # 4. Convert to requested type:
  if type == Numeral
    return numeral
  else
    conversion_in(numeral, options)
  end
end