numbers_in_words 0.1.1 → 0.5.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.
- checksums.yaml +7 -0
 - data/.codeclimate.yml +14 -0
 - data/.gitignore +7 -0
 - data/.rspec +2 -0
 - data/.rubocop.yml +58 -0
 - data/.travis.yml +15 -0
 - data/Gemfile +8 -0
 - data/LICENSE.txt +22 -0
 - data/README.md +47 -0
 - data/Rakefile +2 -43
 - data/bin/spec +2 -0
 - data/lib/numbers_in_words.rb +55 -4
 - data/lib/numbers_in_words/duck_punch.rb +23 -0
 - data/lib/numbers_in_words/exceptional_numbers.rb +115 -0
 - data/lib/numbers_in_words/fraction.rb +136 -0
 - data/lib/numbers_in_words/number_group.rb +64 -0
 - data/lib/numbers_in_words/parsing/fraction_parsing.rb +34 -0
 - data/lib/numbers_in_words/parsing/number_parser.rb +98 -0
 - data/lib/numbers_in_words/parsing/pair_parsing.rb +64 -0
 - data/lib/numbers_in_words/parsing/parse_fractions.rb +45 -0
 - data/lib/numbers_in_words/parsing/parse_individual_number.rb +68 -0
 - data/lib/numbers_in_words/parsing/parse_status.rb +17 -0
 - data/lib/numbers_in_words/parsing/special.rb +67 -0
 - data/lib/numbers_in_words/parsing/to_number.rb +77 -0
 - data/lib/numbers_in_words/powers_of_ten.rb +49 -0
 - data/lib/numbers_in_words/to_word.rb +84 -0
 - data/lib/numbers_in_words/version.rb +5 -0
 - data/lib/numbers_in_words/writer.rb +69 -0
 - data/numbers_in_words.gemspec +20 -27
 - data/spec/exceptional_numbers_spec.rb +26 -0
 - data/spec/fraction_spec.rb +152 -0
 - data/spec/fractions_spec.rb +31 -0
 - data/spec/non_monkey_patch_spec.rb +51 -0
 - data/spec/number_group_spec.rb +17 -0
 - data/spec/number_parser_spec.rb +31 -0
 - data/spec/numbers_in_words_spec.rb +69 -83
 - data/spec/numerical_strings_spec.rb +35 -0
 - data/spec/spec_helper.rb +26 -0
 - data/spec/to_word_spec.rb +18 -0
 - data/spec/words_in_numbers_spec.rb +137 -119
 - data/spec/writer_spec.rb +26 -0
 - data/spec/years_spec.rb +27 -0
 - metadata +95 -45
 - data/CHANGELOG +0 -1
 - data/Manifest +0 -11
 - data/README +0 -84
 - data/examples/display_numbers_in_words.rb +0 -22
 - data/init.rb +0 -8
 - data/lib/numbers.rb +0 -260
 - data/lib/words.rb +0 -221
 
| 
         @@ -0,0 +1,136 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module NumbersInWords
         
     | 
| 
      
 4 
     | 
    
         
            +
              class Fraction
         
     | 
| 
      
 5 
     | 
    
         
            +
                attr_reader :denominator, :numerator, :attributes
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                def self.in_words(that)
         
     | 
| 
      
 8 
     | 
    
         
            +
                  r = that.rationalize(EPSILON)
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  NumbersInWords
         
     | 
| 
      
 11 
     | 
    
         
            +
                    .fraction(denominator: r.denominator, numerator: r.numerator)
         
     | 
| 
      
 12 
     | 
    
         
            +
                    .in_words
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def initialize(denominator:, numerator: 1, attributes: nil)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @denominator = denominator
         
     | 
| 
      
 17 
     | 
    
         
            +
                  @numerator = numerator
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @attributes = attributes || NumbersInWords::ExceptionalNumbers::DEFINITIONS[denominator] || {}
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def lookup_keys
         
     | 
| 
      
 22 
     | 
    
         
            +
                  key = in_words
         
     | 
| 
      
 23 
     | 
    
         
            +
                  key2 = strip_punctuation(key.split(' ')).join(' ')
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  key3 = "a #{key}"
         
     | 
| 
      
 26 
     | 
    
         
            +
                  key4 = "an #{key}"
         
     | 
| 
      
 27 
     | 
    
         
            +
                  key5 = "a #{key2}"
         
     | 
| 
      
 28 
     | 
    
         
            +
                  key6 = "an #{key2}"
         
     | 
| 
      
 29 
     | 
    
         
            +
                  [key, key2, key3, key4, key5, key6].uniq
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                def in_words
         
     | 
| 
      
 33 
     | 
    
         
            +
                  NumbersInWords.in_words(numerator) + ' ' + fraction
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                def ordinal
         
     | 
| 
      
 37 
     | 
    
         
            +
                  pluralize? ? pluralized_ordinal_in_words : singular_ordinal_in_words
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                def fraction
         
     | 
| 
      
 41 
     | 
    
         
            +
                  if denominator == Float::INFINITY
         
     | 
| 
      
 42 
     | 
    
         
            +
                    # We've reached the limits of ruby's number system
         
     | 
| 
      
 43 
     | 
    
         
            +
                    # by the time we get to a googolplex (10 ** (10 ** 100))
         
     | 
| 
      
 44 
     | 
    
         
            +
                    return pluralize? ? 'infinitieths' : 'infinitieth'
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                  pluralize? ? pluralized_fraction : singular_fraction
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                private
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                def strip_punctuation(words)
         
     | 
| 
      
 53 
     | 
    
         
            +
                  words.map { |w| w.gsub(/^a-z/, ' ') }
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                def pluralized_fraction
         
     | 
| 
      
 57 
     | 
    
         
            +
                  fraction_plural || pluralized_ordinal_in_words
         
     | 
| 
      
 58 
     | 
    
         
            +
                end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                def singular_fraction
         
     | 
| 
      
 61 
     | 
    
         
            +
                  fraction_singular || singular_ordinal_in_words
         
     | 
| 
      
 62 
     | 
    
         
            +
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                def pluralized_ordinal_in_words
         
     | 
| 
      
 65 
     | 
    
         
            +
                  pluralized_ordinal || denominator_ordinal_in_words
         
     | 
| 
      
 66 
     | 
    
         
            +
                end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                def singular_ordinal_in_words
         
     | 
| 
      
 69 
     | 
    
         
            +
                  singular_ordinal || denominator_ordinal_in_words
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                def singular_ordinal
         
     | 
| 
      
 73 
     | 
    
         
            +
                  attributes[:ordinal]
         
     | 
| 
      
 74 
     | 
    
         
            +
                end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                def pluralized_ordinal
         
     | 
| 
      
 77 
     | 
    
         
            +
                  singular_ordinal && singular_ordinal + 's'
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                def pluralize?
         
     | 
| 
      
 81 
     | 
    
         
            +
                  numerator > 1
         
     | 
| 
      
 82 
     | 
    
         
            +
                end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                def denominator_ordinal_in_words
         
     | 
| 
      
 85 
     | 
    
         
            +
                  if denominator > 100
         
     | 
| 
      
 86 
     | 
    
         
            +
                    # one hundred and second
         
     | 
| 
      
 87 
     | 
    
         
            +
                    with_remainder(100, ' and ')
         
     | 
| 
      
 88 
     | 
    
         
            +
                  elsif denominator > 19
         
     | 
| 
      
 89 
     | 
    
         
            +
                    # two thirty-fifths
         
     | 
| 
      
 90 
     | 
    
         
            +
                    with_remainder(10, '-')
         
     | 
| 
      
 91 
     | 
    
         
            +
                  else
         
     | 
| 
      
 92 
     | 
    
         
            +
                    # one seventh
         
     | 
| 
      
 93 
     | 
    
         
            +
                    singular = NumbersInWords.in_words(denominator) + 'th'
         
     | 
| 
      
 94 
     | 
    
         
            +
                    pluralize? ? singular + 's' : singular
         
     | 
| 
      
 95 
     | 
    
         
            +
                  end
         
     | 
| 
      
 96 
     | 
    
         
            +
                end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                def with_remainder(mod, join_word)
         
     | 
| 
      
 99 
     | 
    
         
            +
                  rest = denominator % mod
         
     | 
| 
      
 100 
     | 
    
         
            +
                  main = denominator - rest
         
     | 
| 
      
 101 
     | 
    
         
            +
                  main = NumbersInWords.in_words(main)
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                  main = main.gsub(/^one /, '') if pluralize?
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                  rest_zero(rest, main) || joined(main, rest, join_word)
         
     | 
| 
      
 106 
     | 
    
         
            +
                end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                def joined(main, rest, join_word)
         
     | 
| 
      
 109 
     | 
    
         
            +
                  main +
         
     | 
| 
      
 110 
     | 
    
         
            +
                    join_word +
         
     | 
| 
      
 111 
     | 
    
         
            +
                    self.class.new(numerator: numerator, denominator: rest).ordinal
         
     | 
| 
      
 112 
     | 
    
         
            +
                end
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                def rest_zero(rest, main)
         
     | 
| 
      
 115 
     | 
    
         
            +
                  return unless rest.zero?
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
                  if pluralize?
         
     | 
| 
      
 118 
     | 
    
         
            +
                    main + 'ths'
         
     | 
| 
      
 119 
     | 
    
         
            +
                  else
         
     | 
| 
      
 120 
     | 
    
         
            +
                    main + 'th'
         
     | 
| 
      
 121 
     | 
    
         
            +
                  end
         
     | 
| 
      
 122 
     | 
    
         
            +
                end
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                def exception
         
     | 
| 
      
 125 
     | 
    
         
            +
                  attributes[:fraction]
         
     | 
| 
      
 126 
     | 
    
         
            +
                end
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                def fraction_singular
         
     | 
| 
      
 129 
     | 
    
         
            +
                  exception && exception[:singular]
         
     | 
| 
      
 130 
     | 
    
         
            +
                end
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
                def fraction_plural
         
     | 
| 
      
 133 
     | 
    
         
            +
                  exception && exception[:plural]
         
     | 
| 
      
 134 
     | 
    
         
            +
                end
         
     | 
| 
      
 135 
     | 
    
         
            +
              end
         
     | 
| 
      
 136 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,64 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module NumbersInWords
         
     | 
| 
      
 4 
     | 
    
         
            +
              class NumberGroup
         
     | 
| 
      
 5 
     | 
    
         
            +
                include Enumerable
         
     | 
| 
      
 6 
     | 
    
         
            +
                attr_accessor :number
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                def self.groups_of(number, size)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  new(number).groups(size)
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                def initialize(number)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @number = number
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                # split into groups this gives us 1234567 => 123 456 7
         
     | 
| 
      
 17 
     | 
    
         
            +
                # so we need to reverse first
         
     | 
| 
      
 18 
     | 
    
         
            +
                # in stages
         
     | 
| 
      
 19 
     | 
    
         
            +
                def groups(size)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # 1234567   => %w(765 432 1)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @array = in_groups_of(@number.to_s.reverse.split(''), size)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  # %w(765 432 1) => %w(1 432 765)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  @array.reverse!
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  # %w(1 432 765) => [1, 234, 567]
         
     | 
| 
      
 26 
     | 
    
         
            +
                  @array.map! { |group| group.reverse.join('').to_i }
         
     | 
| 
      
 27 
     | 
    
         
            +
                  @array.reverse! # put in ascending order of power of ten
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  power = 0
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  # [1, 234, 567] => {6 => 1, 3 => 234, 0 => 567}
         
     | 
| 
      
 32 
     | 
    
         
            +
                  @array.each_with_object({}) do |digits, o|
         
     | 
| 
      
 33 
     | 
    
         
            +
                    o[power] = digits
         
     | 
| 
      
 34 
     | 
    
         
            +
                    power += size
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                def split_decimals
         
     | 
| 
      
 39 
     | 
    
         
            +
                  return unless @number.is_a? Float
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  int, decimal = @number.to_s.split '.'
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  [int.to_i, decimal.split(//).map(&:to_i)]
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                def split_googols
         
     | 
| 
      
 47 
     | 
    
         
            +
                  googols   = @number.to_s[0..-LENGTH_OF_GOOGOL].to_i
         
     | 
| 
      
 48 
     | 
    
         
            +
                  remainder = @number.to_s[(1 - LENGTH_OF_GOOGOL)..].to_i
         
     | 
| 
      
 49 
     | 
    
         
            +
                  [googols, remainder]
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                private
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                def in_groups_of(array, number, fill_with = nil)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  # size % number gives how many extra we have;
         
     | 
| 
      
 56 
     | 
    
         
            +
                  # subtracting from number gives how many to add;
         
     | 
| 
      
 57 
     | 
    
         
            +
                  # modulo number ensures we don't add group of just fill.
         
     | 
| 
      
 58 
     | 
    
         
            +
                  padding = (number - array.size % number) % number
         
     | 
| 
      
 59 
     | 
    
         
            +
                  collection = array.dup.concat(Array.new(padding, fill_with))
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                  collection.each_slice(number).to_a
         
     | 
| 
      
 62 
     | 
    
         
            +
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
              end
         
     | 
| 
      
 64 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,34 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module NumbersInWords
         
     | 
| 
      
 4 
     | 
    
         
            +
              module FractionParsing
         
     | 
| 
      
 5 
     | 
    
         
            +
                def fraction(text)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  return unless possible_fraction?(text)
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                  NumbersInWords.exceptional_numbers.lookup_fraction(text)
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def strip_punctuation(text)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  text = text.downcase.gsub(/[^a-z 0-9]/, ' ')
         
     | 
| 
      
 13 
     | 
    
         
            +
                  to_remove = true
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  to_remove = text.gsub! '  ', ' ' while to_remove
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  text
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                def possible_fraction?(text)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  words = text.split(' ')
         
     | 
| 
      
 22 
     | 
    
         
            +
                  result = words & NumbersInWords.exceptional_numbers.fraction_names
         
     | 
| 
      
 23 
     | 
    
         
            +
                  result.length.positive?
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                def text_including_punctuation
         
     | 
| 
      
 27 
     | 
    
         
            +
                  to_s.strip
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                def text
         
     | 
| 
      
 31 
     | 
    
         
            +
                  strip_punctuation text_including_punctuation
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,98 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'forwardable'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require_relative 'parse_fractions'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require_relative 'parse_status'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require_relative 'parse_individual_number'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require_relative 'pair_parsing'
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            module NumbersInWords
         
     | 
| 
      
 10 
     | 
    
         
            +
              class NumberParser
         
     | 
| 
      
 11 
     | 
    
         
            +
                # Example: 364,895,457,898
         
     | 
| 
      
 12 
     | 
    
         
            +
                # three hundred and sixty four billion eight hundred and ninety five million
         
     | 
| 
      
 13 
     | 
    
         
            +
                # four hundred and fifty seven thousand eight hundred and ninety eight
         
     | 
| 
      
 14 
     | 
    
         
            +
                #
         
     | 
| 
      
 15 
     | 
    
         
            +
                # 3 100 60 4 10^9, 8 100 90 5 10^6, 4 100 50 7 1000, 8 100 90 8
         
     | 
| 
      
 16 
     | 
    
         
            +
                #                                                                    memory answer
         
     | 
| 
      
 17 
     | 
    
         
            +
                # x1. 3 add to memory because answer and memory are zero                 3     0
         
     | 
| 
      
 18 
     | 
    
         
            +
                # x2. memory * 100 (because memory<100)                                  300   0
         
     | 
| 
      
 19 
     | 
    
         
            +
                # x3. 60 add to memory because memory > 60                               360   0
         
     | 
| 
      
 20 
     | 
    
         
            +
                # x3. 4 add to memory because memory > 4                                364    0
         
     | 
| 
      
 21 
     | 
    
         
            +
                # x4. multiply memory by 10^9 because memory < power of ten          364*10^9  0
         
     | 
| 
      
 22 
     | 
    
         
            +
                # x5. add memory to answer  (and reset)memory > 8 (memory pow of ten > 2) 0   364*10^9
         
     | 
| 
      
 23 
     | 
    
         
            +
                # x6. 8 add to memory because not finished                                8    ''
         
     | 
| 
      
 24 
     | 
    
         
            +
                # x7. multiply memory by 100 because memory < 100                        800   ''
         
     | 
| 
      
 25 
     | 
    
         
            +
                # x8. add 90 to memory because memory > 90                               890   ''
         
     | 
| 
      
 26 
     | 
    
         
            +
                # x9. add 5 to memory because memory > 5                                 895   ''
         
     | 
| 
      
 27 
     | 
    
         
            +
                # x10. multiply memory by 10^6 because memory < power of ten        895*10^6    ''
         
     | 
| 
      
 28 
     | 
    
         
            +
                # x11. add memory to answer (and reset) because memory power ten > 2       0    364895 * 10^6
         
     | 
| 
      
 29 
     | 
    
         
            +
                # x12. 4 add to memory because not finished                                4    ''
         
     | 
| 
      
 30 
     | 
    
         
            +
                # x13. memory * 100 because memory < 100                                 400    ''
         
     | 
| 
      
 31 
     | 
    
         
            +
                # x14. memory + 50 because memory > 50                                   450    ''
         
     | 
| 
      
 32 
     | 
    
         
            +
                # x15. memory + 7  because memory > 7                                    457    ''
         
     | 
| 
      
 33 
     | 
    
         
            +
                # x16. memory * 1000 because memory < 1000                            457000    ''
         
     | 
| 
      
 34 
     | 
    
         
            +
                # x17. add memory to answer  (and reset)memory > 8 (memory pow of ten > 2) 0   364895457000
         
     | 
| 
      
 35 
     | 
    
         
            +
                # x18. 8 add to memory because not finished                                8    ''
         
     | 
| 
      
 36 
     | 
    
         
            +
                # x19. memory * 100 because memory < 100                                 800    ''
         
     | 
| 
      
 37 
     | 
    
         
            +
                # x14. memory + 90 because memory > 90                                   890    ''
         
     | 
| 
      
 38 
     | 
    
         
            +
                # x15. memory + 8  because memory > 8                                    898    ''
         
     | 
| 
      
 39 
     | 
    
         
            +
                # 16. finished so add memory to answer
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                # Example
         
     | 
| 
      
 42 
     | 
    
         
            +
                # 2001
         
     | 
| 
      
 43 
     | 
    
         
            +
                # two thousand and one
         
     | 
| 
      
 44 
     | 
    
         
            +
                # 2 1000 1
         
     | 
| 
      
 45 
     | 
    
         
            +
                #                                                        memory answer
         
     | 
| 
      
 46 
     | 
    
         
            +
                # 1. add 2 to memory because first                          2       0
         
     | 
| 
      
 47 
     | 
    
         
            +
                # 2. multiply memory by 1000 because memory < 1000        2000      0
         
     | 
| 
      
 48 
     | 
    
         
            +
                # 3. add memory to answer,reset,  because power of ten>2    0      2000
         
     | 
| 
      
 49 
     | 
    
         
            +
                # 4. add 1 to memory                                        1      2000
         
     | 
| 
      
 50 
     | 
    
         
            +
                # 5. finish - add memory to answer                          0      2001
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                SCALES_N = [10**2, 10**3, 10**6, 10**9, 10**12, 10**100].freeze
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                def parse(nums, only_compress: false)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  fractions(nums) ||
         
     | 
| 
      
 56 
     | 
    
         
            +
                    small_numbers(nums, only_compress) ||
         
     | 
| 
      
 57 
     | 
    
         
            +
                    pair_parsing(nums, only_compress) ||
         
     | 
| 
      
 58 
     | 
    
         
            +
                    parse_each(nums)
         
     | 
| 
      
 59 
     | 
    
         
            +
                end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                private
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                def fractions(nums)
         
     | 
| 
      
 64 
     | 
    
         
            +
                  ParseFractions.new(nums).call
         
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                # 7 0.066666666666667 => 0.46666666666666
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                # 15 => 15
         
     | 
| 
      
 70 
     | 
    
         
            +
                def small_numbers(nums, only_compress)
         
     | 
| 
      
 71 
     | 
    
         
            +
                  return unless nums.length < 2
         
     | 
| 
      
 72 
     | 
    
         
            +
                  return nums if only_compress
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                  nums.empty? ? 0 : nums[0]
         
     | 
| 
      
 75 
     | 
    
         
            +
                end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                # 15 75 => 1,575
         
     | 
| 
      
 78 
     | 
    
         
            +
                def pair_parsing(nums, only_compress)
         
     | 
| 
      
 79 
     | 
    
         
            +
                  return if (SCALES_N & nums).any?
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                  pair_parse(nums, only_compress)
         
     | 
| 
      
 82 
     | 
    
         
            +
                end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                def parse_each(nums)
         
     | 
| 
      
 85 
     | 
    
         
            +
                  status = ParseStatus.new
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                  nums.each do |num|
         
     | 
| 
      
 88 
     | 
    
         
            +
                    ParseIndividualNumber.new(status, num).call
         
     | 
| 
      
 89 
     | 
    
         
            +
                  end
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                  status.calculate
         
     | 
| 
      
 92 
     | 
    
         
            +
                end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                def pair_parse(nums, only_compress)
         
     | 
| 
      
 95 
     | 
    
         
            +
                  PairParsing.new(nums, only_compress).pair_parse
         
     | 
| 
      
 96 
     | 
    
         
            +
                end
         
     | 
| 
      
 97 
     | 
    
         
            +
              end
         
     | 
| 
      
 98 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,64 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module NumbersInWords
         
     | 
| 
      
 4 
     | 
    
         
            +
              class PairParsing
         
     | 
| 
      
 5 
     | 
    
         
            +
                attr_accessor :ints
         
     | 
| 
      
 6 
     | 
    
         
            +
                attr_reader :only_compress
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                def initialize(ints, only_compress)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @ints = ints
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @only_compress = only_compress
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                # 15,16
         
     | 
| 
      
 14 
     | 
    
         
            +
                # 85,16
         
     | 
| 
      
 15 
     | 
    
         
            +
                def pair_parse
         
     | 
| 
      
 16 
     | 
    
         
            +
                  ints = compressed
         
     | 
| 
      
 17 
     | 
    
         
            +
                  return ints if only_compress
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  return ints[0] if ints.length == 1
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  sum = 0
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  ints.each do |n|
         
     | 
| 
      
 24 
     | 
    
         
            +
                    sum *= n >= 10 ? 100 : 10
         
     | 
| 
      
 25 
     | 
    
         
            +
                    sum += n
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  sum
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                private
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                # [40, 2] => [42]
         
     | 
| 
      
 34 
     | 
    
         
            +
                def compressed
         
     | 
| 
      
 35 
     | 
    
         
            +
                  return [] if ints.empty?
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                  result = []
         
     | 
| 
      
 38 
     | 
    
         
            +
                  index = 0
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  index, result = compress_numbers(result, index)
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  result << ints[-1] if index < ints.length
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  result
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                def compress_numbers(result, index)
         
     | 
| 
      
 48 
     | 
    
         
            +
                  while index < ints.length - 1
         
     | 
| 
      
 49 
     | 
    
         
            +
                    int, jump = compress_int(ints[index], ints[index + 1])
         
     | 
| 
      
 50 
     | 
    
         
            +
                    result << int
         
     | 
| 
      
 51 
     | 
    
         
            +
                    index += jump
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  [index, result]
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                def compress_int(int, next_int)
         
     | 
| 
      
 58 
     | 
    
         
            +
                  tens = (int % 10).zero? && int > 10
         
     | 
| 
      
 59 
     | 
    
         
            +
                  return [int + next_int, 2] if tens && next_int < 10
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                  [int, 1]
         
     | 
| 
      
 62 
     | 
    
         
            +
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
              end
         
     | 
| 
      
 64 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,45 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module NumbersInWords
         
     | 
| 
      
 4 
     | 
    
         
            +
              class ParseFractions
         
     | 
| 
      
 5 
     | 
    
         
            +
                attr_reader :nums
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                def initialize(nums)
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @nums = nums.map(&:to_f)
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def call
         
     | 
| 
      
 12 
     | 
    
         
            +
                  return if no_fractions?
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  just_fraction || calculate
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def calculate
         
     | 
| 
      
 18 
     | 
    
         
            +
                  (parse(numbers) * parse(fractions)).rationalize(EPSILON).to_f
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def parse(numbers)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  NumberParser.new.parse(numbers)
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def numbers
         
     | 
| 
      
 26 
     | 
    
         
            +
                  nums[0..index_of_fraction - 1]
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                def fractions
         
     | 
| 
      
 30 
     | 
    
         
            +
                  nums[index_of_fraction..]
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def just_fraction
         
     | 
| 
      
 34 
     | 
    
         
            +
                  return nums.first if index_of_fraction.zero?
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                def index_of_fraction
         
     | 
| 
      
 38 
     | 
    
         
            +
                  nums.index { |n| n < 1.0 }
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                def no_fractions?
         
     | 
| 
      
 42 
     | 
    
         
            +
                  nums.all? { |n| n.zero? || n >= 1.0 }
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
              end
         
     | 
| 
      
 45 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,68 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module NumbersInWords
         
     | 
| 
      
 4 
     | 
    
         
            +
              class ParseIndividualNumber
         
     | 
| 
      
 5 
     | 
    
         
            +
                extend Forwardable
         
     | 
| 
      
 6 
     | 
    
         
            +
                def_delegators :parse_status, :reset=, :memory=, :answer=, :reset, :memory, :answer
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                attr_reader :parse_status, :num
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def initialize(parse_status, num)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @parse_status = parse_status
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @num = num
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def call
         
     | 
| 
      
 16 
     | 
    
         
            +
                  if reset
         
     | 
| 
      
 17 
     | 
    
         
            +
                    clear
         
     | 
| 
      
 18 
     | 
    
         
            +
                  else
         
     | 
| 
      
 19 
     | 
    
         
            +
                    handle_power_of_ten
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                    update_memory
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  [reset, memory, answer]
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                private
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                def clear
         
     | 
| 
      
 30 
     | 
    
         
            +
                  self.reset = false
         
     | 
| 
      
 31 
     | 
    
         
            +
                  self.memory += num
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                def handle_power_of_ten
         
     | 
| 
      
 35 
     | 
    
         
            +
                  # x4. multiply memory by 10^9 because memory < power of ten
         
     | 
| 
      
 36 
     | 
    
         
            +
                  return unless power_of_ten?(num)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  return unless power_of_ten(num) > 2
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  self.memory *= num
         
     | 
| 
      
 40 
     | 
    
         
            +
                  # 17. add memory to answer  (and reset) (memory pow of ten > 2)
         
     | 
| 
      
 41 
     | 
    
         
            +
                  self.answer += memory
         
     | 
| 
      
 42 
     | 
    
         
            +
                  self.memory = 0
         
     | 
| 
      
 43 
     | 
    
         
            +
                  self.reset = true
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                def update_memory
         
     | 
| 
      
 47 
     | 
    
         
            +
                  self.memory = new_memory
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                def new_memory
         
     | 
| 
      
 51 
     | 
    
         
            +
                  if memory < num
         
     | 
| 
      
 52 
     | 
    
         
            +
                    memory * num
         
     | 
| 
      
 53 
     | 
    
         
            +
                  else
         
     | 
| 
      
 54 
     | 
    
         
            +
                    memory + num
         
     | 
| 
      
 55 
     | 
    
         
            +
                  end
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                def power_of_ten(integer)
         
     | 
| 
      
 59 
     | 
    
         
            +
                  Math.log10(integer)
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                def power_of_ten?(integer)
         
     | 
| 
      
 63 
     | 
    
         
            +
                  return true if integer.zero?
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                  power_of_ten(integer) == power_of_ten(integer).to_i
         
     | 
| 
      
 66 
     | 
    
         
            +
                end
         
     | 
| 
      
 67 
     | 
    
         
            +
              end
         
     | 
| 
      
 68 
     | 
    
         
            +
            end
         
     |