numerals 0.0.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/.gitignore +22 -0
- data/Gemfile +3 -0
- data/LICENSE +22 -0
- data/README.md +24 -0
- data/Rakefile +19 -0
- data/lib/numerals/conversions/bigdecimal.rb +30 -0
- data/lib/numerals/conversions/float.rb +226 -0
- data/lib/numerals/conversions/flt.rb +162 -0
- data/lib/numerals/conversions/integer.rb +39 -0
- data/lib/numerals/conversions/rational.rb +32 -0
- data/lib/numerals/conversions.rb +57 -0
- data/lib/numerals/digits.rb +99 -0
- data/lib/numerals/formatting/digits_definition.rb +75 -0
- data/lib/numerals/formatting/options.rb +84 -0
- data/lib/numerals/numeral.rb +650 -0
- data/lib/numerals/rounding.rb +229 -0
- data/lib/numerals/support.rb +10 -0
- data/lib/numerals/version.rb +3 -0
- data/lib/numerals.rb +12 -0
- data/numerals.gemspec +26 -0
- data/test/data.yaml +101 -0
- data/test/helper.rb +40 -0
- data/test/test_digits_definition.rb +110 -0
- data/test/test_float_conversions.rb +58 -0
- data/test/test_flt_conversions.rb +277 -0
- data/test/test_integer_conversions.rb +50 -0
- data/test/test_numeral.rb +366 -0
- data/test/test_rational_conversions.rb +75 -0
- data/test/test_rounding.rb +77 -0
- metadata +138 -0
| @@ -0,0 +1,229 @@ | |
| 1 | 
            +
            # Rounding of Numerals
         | 
| 2 | 
            +
            class Rounding
         | 
| 3 | 
            +
             | 
| 4 | 
            +
              # Rounding is defined by the rounding mode and the precision,
         | 
| 5 | 
            +
              # and is used to stablish the desired accuracy of a Numeral result.
         | 
| 6 | 
            +
              #
         | 
| 7 | 
            +
              # The rounding mode may be any of the valid Flt rounding modes
         | 
| 8 | 
            +
              # (:half_even, :half_down, :half_up, :floor, :ceiling, :down, :up or :up05)
         | 
| 9 | 
            +
              # or :exact for no rounding at all (to represent the situation where
         | 
| 10 | 
            +
              # we want to get an exact numeral result.
         | 
| 11 | 
            +
              #
         | 
| 12 | 
            +
              # The precision may be defined either as a relative :precision value
         | 
| 13 | 
            +
              # (number of significant digits of the result numeral) or as an :absolute
         | 
| 14 | 
            +
              # :places that indicate the number of digits to the right of the fractional
         | 
| 15 | 
            +
              # point. Negative values of :places will round to digits in integral positions.
         | 
| 16 | 
            +
              #
         | 
| 17 | 
            +
              # The base of the numerals to be rounded must also be defined (10 by default)
         | 
| 18 | 
            +
              #
         | 
| 19 | 
            +
              def initialize(*args)
         | 
| 20 | 
            +
                if Hash === args.last
         | 
| 21 | 
            +
                  options = args.pop.dup
         | 
| 22 | 
            +
                else
         | 
| 23 | 
            +
                  options = {}
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
                args.each do |arg|
         | 
| 26 | 
            +
                  if Symbol === arg
         | 
| 27 | 
            +
                    options[:mode] = arg
         | 
| 28 | 
            +
                  elsif Integer === arg
         | 
| 29 | 
            +
                    options[:precision] = arg
         | 
| 30 | 
            +
                  else
         | 
| 31 | 
            +
                    raise "Invalid Rounding definition"
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
                assign! options
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
              attr_reader :mode, :base
         | 
| 38 | 
            +
             | 
| 39 | 
            +
              include ModalSupport::BracketConstructor
         | 
| 40 | 
            +
              include ModalSupport::StateEquivalent
         | 
| 41 | 
            +
             | 
| 42 | 
            +
              def [](options={})
         | 
| 43 | 
            +
                assign! options
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              def assign!(options)
         | 
| 47 | 
            +
                @mode      = options[:mode]      || @mode
         | 
| 48 | 
            +
                @precision = options[:precision] || @precision
         | 
| 49 | 
            +
                @places    = options[:places]    || @places
         | 
| 50 | 
            +
                @base      = options[:base]      || @base
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                if (@precision == 0 || @precision.nil?) && @places.nil?
         | 
| 53 | 
            +
                  @precision = 0
         | 
| 54 | 
            +
                  @mode = :exact
         | 
| 55 | 
            +
                else
         | 
| 56 | 
            +
                  @mode ||= :half_even
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
                if @mode == :exact
         | 
| 59 | 
            +
                  @precision = 0
         | 
| 60 | 
            +
                  @places    = nil
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
                @base ||= 10
         | 
| 63 | 
            +
                self
         | 
| 64 | 
            +
              end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
              def parameters
         | 
| 67 | 
            +
                if exact?
         | 
| 68 | 
            +
                  { mode: :exact }
         | 
| 69 | 
            +
                elsif relative?
         | 
| 70 | 
            +
                  { mode: @mode, precision: @precision }
         | 
| 71 | 
            +
                elsif absolute?
         | 
| 72 | 
            +
                  { mode: @mode, places: @places }
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
              end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
              def to_s
         | 
| 77 | 
            +
                if exact?
         | 
| 78 | 
            +
                  "Rounding[:exact]"
         | 
| 79 | 
            +
                elsif relative?
         | 
| 80 | 
            +
                  "Rounding[#{@mode.inspect}, precision: #{@precision}]"
         | 
| 81 | 
            +
                elsif absolute?
         | 
| 82 | 
            +
                  "Rounding[#{@mode.inspect}, places: #{@places}]"
         | 
| 83 | 
            +
                end
         | 
| 84 | 
            +
              end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
              def inspect
         | 
| 87 | 
            +
                to_s
         | 
| 88 | 
            +
              end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
              def exact?
         | 
| 91 | 
            +
                @mode == :exact
         | 
| 92 | 
            +
              end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
              def absolute?
         | 
| 95 | 
            +
                !exact? && !@precision
         | 
| 96 | 
            +
              end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
              def relative?
         | 
| 99 | 
            +
                !exact? && !absolute?
         | 
| 100 | 
            +
              end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
              # Number of significant digits for a given numerical/numeral value
         | 
| 103 | 
            +
              def precision(value)
         | 
| 104 | 
            +
                if relative? || exact?
         | 
| 105 | 
            +
                  @precision
         | 
| 106 | 
            +
                else
         | 
| 107 | 
            +
                  @places + num_integral_digits(value)
         | 
| 108 | 
            +
                end
         | 
| 109 | 
            +
              end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
              # Number of fractional placesfor a given numerical/numeral value
         | 
| 112 | 
            +
              def places(value)
         | 
| 113 | 
            +
                if absolute? || exact?
         | 
| 114 | 
            +
                  @places
         | 
| 115 | 
            +
                else
         | 
| 116 | 
            +
                  @precision - num_integral_digits(value)
         | 
| 117 | 
            +
                end
         | 
| 118 | 
            +
              end
         | 
| 119 | 
            +
             | 
| 120 | 
            +
              # Round a numeral. If the numeral has been truncated
         | 
| 121 | 
            +
              # the :round_up option must be used to pass the information
         | 
| 122 | 
            +
              # about the discarded digits:
         | 
| 123 | 
            +
              # * nil if all discarded digits where 0 (the truncated value is exact)
         | 
| 124 | 
            +
              # * :lo if there where non-zero discarded digits, but the first discarded digit
         | 
| 125 | 
            +
              #   is below half the base.
         | 
| 126 | 
            +
              # * :tie if the first discarded was half the base and there where no more nonzero digits,
         | 
| 127 | 
            +
              #   i.e. the original value was a 'tie', exactly halfway between the truncated value
         | 
| 128 | 
            +
              #   and the next value with the same number of digits.
         | 
| 129 | 
            +
              # * :hi if the original value was above the tie value.
         | 
| 130 | 
            +
              def round(numeral, options={})
         | 
| 131 | 
            +
                round_up = options[:round_up]
         | 
| 132 | 
            +
                numeral, round_up = truncate(numeral, round_up)
         | 
| 133 | 
            +
                adjust(numeral, round_up)
         | 
| 134 | 
            +
              end
         | 
| 135 | 
            +
             | 
| 136 | 
            +
              private
         | 
| 137 | 
            +
             | 
| 138 | 
            +
              def check_base(numeral)
         | 
| 139 | 
            +
                if numeral.base != @base
         | 
| 140 | 
            +
                  raise "Invalid Numeral (base #{numeral.base}) for a base #{@base} Rounding"
         | 
| 141 | 
            +
                end
         | 
| 142 | 
            +
              end
         | 
| 143 | 
            +
             | 
| 144 | 
            +
              # Truncate a numeral and return also a round_up value with information about
         | 
| 145 | 
            +
              # the digits beyond the truncation point that can be used to round the truncated
         | 
| 146 | 
            +
              # numeral. If the numeral has already been truncated, the round_up result of
         | 
| 147 | 
            +
              # that truncation should be passed as the second argument.
         | 
| 148 | 
            +
              def truncate(numeral, round_up=nil)
         | 
| 149 | 
            +
                check_base numeral
         | 
| 150 | 
            +
                if exact?
         | 
| 151 | 
            +
                  round_up = nil
         | 
| 152 | 
            +
                else
         | 
| 153 | 
            +
                  n = precision(numeral)
         | 
| 154 | 
            +
                  unless n==numeral.digits.size && numeral.approximate?
         | 
| 155 | 
            +
                    if n < numeral.digits.size - 1
         | 
| 156 | 
            +
                      rest_digits = numeral.digits[n+1..-1]
         | 
| 157 | 
            +
                    else
         | 
| 158 | 
            +
                      rest_digits = []
         | 
| 159 | 
            +
                    end
         | 
| 160 | 
            +
                    if numeral.repeating? && numeral.repeat < numeral.digits.size && n >= numeral.repeat
         | 
| 161 | 
            +
                      rest_digits += numeral.digits[numeral.repeat..-1]
         | 
| 162 | 
            +
                    end
         | 
| 163 | 
            +
                    digits = numeral.digits[0, n]
         | 
| 164 | 
            +
                    if digits.size < n
         | 
| 165 | 
            +
                      digits += (digits.size...n).map{|i| numeral.digit_value_at(i)}
         | 
| 166 | 
            +
                    end
         | 
| 167 | 
            +
                    if numeral.base % 2 == 0
         | 
| 168 | 
            +
                      tie_digit = numeral.base / 2
         | 
| 169 | 
            +
                      max_lo = tie_digit - 1
         | 
| 170 | 
            +
                    else
         | 
| 171 | 
            +
                      max_lo = numeral.base / 2
         | 
| 172 | 
            +
                    end
         | 
| 173 | 
            +
                    next_digit = numeral.digit_value_at(n)
         | 
| 174 | 
            +
                    if next_digit == 0
         | 
| 175 | 
            +
                      unless round_up.nil? && rest_digits.all?{|d| d == 0}
         | 
| 176 | 
            +
                        round_up = :lo
         | 
| 177 | 
            +
                      end
         | 
| 178 | 
            +
                    elsif next_digit <= max_lo # next_digit < tie_digit
         | 
| 179 | 
            +
                      round_up = :lo
         | 
| 180 | 
            +
                    elsif next_digit == tie_digit
         | 
| 181 | 
            +
                      if round_up || rest_digits.any?{|d| d != 0}
         | 
| 182 | 
            +
                        round_up = :hi
         | 
| 183 | 
            +
                      else
         | 
| 184 | 
            +
                        round_up = :tie
         | 
| 185 | 
            +
                      end
         | 
| 186 | 
            +
                    else # next_digit > tie_digit
         | 
| 187 | 
            +
                      round_up = :hi
         | 
| 188 | 
            +
                    end
         | 
| 189 | 
            +
                    numeral = Numeral[digits, point: numeral.point, sign: numeral.sign, normalize: :approximate]
         | 
| 190 | 
            +
                  end
         | 
| 191 | 
            +
                end
         | 
| 192 | 
            +
                [numeral, round_up]
         | 
| 193 | 
            +
              end
         | 
| 194 | 
            +
             | 
| 195 | 
            +
              # Adjust a truncated numeral using the round-up information
         | 
| 196 | 
            +
              def adjust(numeral, round_up)
         | 
| 197 | 
            +
                check_base numeral
         | 
| 198 | 
            +
                point, digits = Flt::Support.adjust_digits(
         | 
| 199 | 
            +
                  numeral.point, numeral.digits.digits_array,
         | 
| 200 | 
            +
                  round_mode: @mode,
         | 
| 201 | 
            +
                  negative: numeral.sign == -1,
         | 
| 202 | 
            +
                  round_up: round_up,
         | 
| 203 | 
            +
                  base: numeral.base
         | 
| 204 | 
            +
                )
         | 
| 205 | 
            +
                Numeral[digits, point: point, base: numeral.base, sign: numeral.sign, normalize: :approximate]
         | 
| 206 | 
            +
              end
         | 
| 207 | 
            +
             | 
| 208 | 
            +
              ZERO_DIGITS = 0 # 1?
         | 
| 209 | 
            +
             | 
| 210 | 
            +
              # Number of digits in the integer part of the value (excluding leading zeros).
         | 
| 211 | 
            +
              def num_integral_digits(value)
         | 
| 212 | 
            +
                case value
         | 
| 213 | 
            +
                when 0
         | 
| 214 | 
            +
                  ZERO_DIGITS
         | 
| 215 | 
            +
                when Numeral
         | 
| 216 | 
            +
                  if value.zero?
         | 
| 217 | 
            +
                    ZERO_DIGITS
         | 
| 218 | 
            +
                  else
         | 
| 219 | 
            +
                    if @base != value.base
         | 
| 220 | 
            +
                      value = value.to_base(@base)
         | 
| 221 | 
            +
                    end
         | 
| 222 | 
            +
                    value.normalized(remove_trailing_zeros: true).point
         | 
| 223 | 
            +
                  end
         | 
| 224 | 
            +
                else
         | 
| 225 | 
            +
                  Conversions.order_of_magnitude(value, base: @base)
         | 
| 226 | 
            +
                end
         | 
| 227 | 
            +
              end
         | 
| 228 | 
            +
             | 
| 229 | 
            +
            end
         | 
    
        data/lib/numerals.rb
    ADDED
    
    | @@ -0,0 +1,12 @@ | |
| 1 | 
            +
            require 'modalsupport'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "numerals/version"
         | 
| 4 | 
            +
            require 'numerals/numeral'
         | 
| 5 | 
            +
            require 'numerals/rounding'
         | 
| 6 | 
            +
            require 'numerals/conversions/float'
         | 
| 7 | 
            +
            require 'numerals/conversions/integer'
         | 
| 8 | 
            +
            require 'numerals/conversions/rational'
         | 
| 9 | 
            +
            require 'numerals/conversions/bigdecimal'
         | 
| 10 | 
            +
            require 'numerals/conversions/flt'
         | 
| 11 | 
            +
            require 'numerals/formatting/digits_definition'
         | 
| 12 | 
            +
            require 'numerals/formatting/options'
         | 
    
        data/numerals.gemspec
    ADDED
    
    | @@ -0,0 +1,26 @@ | |
| 1 | 
            +
            # coding: utf-8
         | 
| 2 | 
            +
            lib = File.expand_path('../lib', __FILE__)
         | 
| 3 | 
            +
            $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
         | 
| 4 | 
            +
            require 'numerals/version'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            Gem::Specification.new do |spec|
         | 
| 7 | 
            +
              spec.name          = "numerals"
         | 
| 8 | 
            +
              spec.version       = Numerals::VERSION
         | 
| 9 | 
            +
              spec.authors       = ["Javier Goizueta"]
         | 
| 10 | 
            +
              spec.email         = ["jgoizueta@gmail.com"]
         | 
| 11 | 
            +
              spec.summary       = %q{Number representation as text.}
         | 
| 12 | 
            +
              spec.description   = %q{Number formatting and reading.}
         | 
| 13 | 
            +
              spec.homepage      = "https://github.com/jgoizueta/numerals"
         | 
| 14 | 
            +
              spec.license       = "MIT"
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              spec.files         = `git ls-files -z`.split("\x0")
         | 
| 17 | 
            +
              spec.executables   = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
         | 
| 18 | 
            +
              spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
         | 
| 19 | 
            +
              spec.require_paths = ["lib"]
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              spec.add_dependency 'flt', "~> 1.3.4"
         | 
| 22 | 
            +
              spec.add_dependency 'modalsupport', "~> 0.9.2"
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              spec.add_development_dependency "bundler", "~> 1.6"
         | 
| 25 | 
            +
              spec.add_development_dependency "rake"
         | 
| 26 | 
            +
            end
         | 
    
        data/test/data.yaml
    ADDED
    
    | @@ -0,0 +1,101 @@ | |
| 1 | 
            +
            --- 
         | 
| 2 | 
            +
            - A91C2BB757F72141
         | 
| 3 | 
            +
            - D84014519CD8253F
         | 
| 4 | 
            +
            - BF30A46FFF51ED3F
         | 
| 5 | 
            +
            - 91D03B54623CC03E
         | 
| 6 | 
            +
            - D403F243699B4B3F
         | 
| 7 | 
            +
            - 6D1F99449A26D540
         | 
| 8 | 
            +
            - 92817CDBC7715E3F
         | 
| 9 | 
            +
            - 72696E8F13BB323F
         | 
| 10 | 
            +
            - 683C16C10F79FE3E
         | 
| 11 | 
            +
            - 22801BA12A074740
         | 
| 12 | 
            +
            - 7081D3172A53E5BF
         | 
| 13 | 
            +
            - C0DE790FC0FBE9BF
         | 
| 14 | 
            +
            - 3AE5F0923E55D33F
         | 
| 15 | 
            +
            - 92B6BEEC140A8FBF
         | 
| 16 | 
            +
            - CE66ABACDBD80F3F
         | 
| 17 | 
            +
            - ACC70B95DCFF6BC0
         | 
| 18 | 
            +
            - 1461EF81707378BF
         | 
| 19 | 
            +
            - AE09EC08D0F1ADBF
         | 
| 20 | 
            +
            - 34DA3FFF72E9D83F
         | 
| 21 | 
            +
            - C664AD3F9AC4BFBF
         | 
| 22 | 
            +
            - 2101BE78DFDC74BE
         | 
| 23 | 
            +
            - 7EE9B042D4628140
         | 
| 24 | 
            +
            - 90368189CC8D753F
         | 
| 25 | 
            +
            - 337C9D874DC4C5BE
         | 
| 26 | 
            +
            - 0CFDA79A7FDCED3F
         | 
| 27 | 
            +
            - EC94C739F2CFB3BF
         | 
| 28 | 
            +
            - 1D5436848A1AEBBE
         | 
| 29 | 
            +
            - B725B6C420034640
         | 
| 30 | 
            +
            - 48C692FEAA59DEBF
         | 
| 31 | 
            +
            - BC4CB816B0BAB5BF
         | 
| 32 | 
            +
            - C2D835BC80B99A40
         | 
| 33 | 
            +
            - 6A565C2E720847BF
         | 
| 34 | 
            +
            - 050189AF84C84EC1
         | 
| 35 | 
            +
            - 0060B28B63B5DC3F
         | 
| 36 | 
            +
            - 18A387F2A64E5FC0
         | 
| 37 | 
            +
            - FBC8B0D428E302C1
         | 
| 38 | 
            +
            - A30DD43ED762B4C0
         | 
| 39 | 
            +
            - 3D85522EC70AFDBE
         | 
| 40 | 
            +
            - FCF62D2923F0583F
         | 
| 41 | 
            +
            - DB588C2A9C26FBC0
         | 
| 42 | 
            +
            - BA4E1863DBE6FCBE
         | 
| 43 | 
            +
            - 5C34946E1A12D33F
         | 
| 44 | 
            +
            - 90E63D7E81888740
         | 
| 45 | 
            +
            - 82541C0DE9F1443F
         | 
| 46 | 
            +
            - 9F2A7781EE4B683F
         | 
| 47 | 
            +
            - 0082C4B6393EA7BF
         | 
| 48 | 
            +
            - 768EDD6B97D19540
         | 
| 49 | 
            +
            - 87BA76258BEBFBBE
         | 
| 50 | 
            +
            - 7A6580A6639DD7BF
         | 
| 51 | 
            +
            - 26DDD435F13B12BF
         | 
| 52 | 
            +
            - 1AF857C69682DBBF
         | 
| 53 | 
            +
            - E731C0DCDAAE51BF
         | 
| 54 | 
            +
            - DB9DB0551A511F3F
         | 
| 55 | 
            +
            - 04977B9C4C73D13F
         | 
| 56 | 
            +
            - 44D5BB96C84587C0
         | 
| 57 | 
            +
            - CE3DE8C2DF9942BF
         | 
| 58 | 
            +
            - 23F12DE055F30C3F
         | 
| 59 | 
            +
            - C10951998E1B7440
         | 
| 60 | 
            +
            - 1CC3F51802F309C0
         | 
| 61 | 
            +
            - FB97E7DB6B081DC1
         | 
| 62 | 
            +
            - 5C811354280FEA3F
         | 
| 63 | 
            +
            - A405ED0A749718BF
         | 
| 64 | 
            +
            - 322939F71F7A51BF
         | 
| 65 | 
            +
            - F3E06C6EEB2342BF
         | 
| 66 | 
            +
            - 5A6D9DF3F879D3C0
         | 
| 67 | 
            +
            - BC466E35C5AF9940
         | 
| 68 | 
            +
            - EB4571E54EB7CD3F
         | 
| 69 | 
            +
            - BBA9A8401CD4D13F
         | 
| 70 | 
            +
            - 6A565DEA5E4F8A40
         | 
| 71 | 
            +
            - 20586794310B93BF
         | 
| 72 | 
            +
            - 80B8449C203B9BC0
         | 
| 73 | 
            +
            - 0E9C461358F4AABF
         | 
| 74 | 
            +
            - 469E0A1D42DEA73F
         | 
| 75 | 
            +
            - 711DA636DD2C41BF
         | 
| 76 | 
            +
            - 414A9E166BE2A0C0
         | 
| 77 | 
            +
            - 407FF9F39253F2BE
         | 
| 78 | 
            +
            - BAD158256942D4C0
         | 
| 79 | 
            +
            - 2D4424FE12BE94BF
         | 
| 80 | 
            +
            - 7F0F15B537E997C0
         | 
| 81 | 
            +
            - 4ADF1BD93C66F1BF
         | 
| 82 | 
            +
            - 2DAF002ADBC1E0BF
         | 
| 83 | 
            +
            - 3739FF3C2BE601C0
         | 
| 84 | 
            +
            - 38ACCEAE7B88083F
         | 
| 85 | 
            +
            - 7C2D9D7FA150C23F
         | 
| 86 | 
            +
            - B726087920E8EA3F
         | 
| 87 | 
            +
            - C075B44F459250BE
         | 
| 88 | 
            +
            - 9EDAB9C61F7D783F
         | 
| 89 | 
            +
            - 2FE6362CACE5153F
         | 
| 90 | 
            +
            - 98A24B93A2F4C33E
         | 
| 91 | 
            +
            - 6FD39CEB5C7F15C0
         | 
| 92 | 
            +
            - C5BE550840E4503F
         | 
| 93 | 
            +
            - 214EA7E4F9619640
         | 
| 94 | 
            +
            - 4DEF9253DE1C10C0
         | 
| 95 | 
            +
            - B8C76C5B85E4FDBF
         | 
| 96 | 
            +
            - 07D21EB7A400F13F
         | 
| 97 | 
            +
            - DB103669202EAEBF
         | 
| 98 | 
            +
            - 9F59E7B3EA71E63F
         | 
| 99 | 
            +
            - 83712959474BF440
         | 
| 100 | 
            +
            - 8D5B7FA245A650BF
         | 
| 101 | 
            +
            - 16FAF25CCBB8ECBE
         | 
    
        data/test/helper.rb
    ADDED
    
    | @@ -0,0 +1,40 @@ | |
| 1 | 
            +
            require 'test/unit'
         | 
| 2 | 
            +
            # require 'minitest/spec'
         | 
| 3 | 
            +
            # require 'minitest/unit'
         | 
| 4 | 
            +
            $: << "." unless $:.include?(".") # for Ruby 1.9.2
         | 
| 5 | 
            +
            require File.expand_path(File.join(File.dirname(__FILE__),'/../lib/numerals'))
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            require 'yaml'
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            module PrepareData
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                @@data = []
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def self.add(x)
         | 
| 14 | 
            +
                  @@data << [x].pack('E').unpack('H*')[0].upcase
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def self.init
         | 
| 18 | 
            +
                  unless File.exist?('test/data.yaml')
         | 
| 19 | 
            +
                    100.times do
         | 
| 20 | 
            +
                       x = rand
         | 
| 21 | 
            +
                       x *= rand(1000) if rand<0.5
         | 
| 22 | 
            +
                       x /= rand(1000) if rand<0.5
         | 
| 23 | 
            +
                       x *= rand(9999) if rand<0.5
         | 
| 24 | 
            +
                       x /= rand(9999) if rand<0.5
         | 
| 25 | 
            +
                       x = -x if rand<0.5
         | 
| 26 | 
            +
                       #puts x
         | 
| 27 | 
            +
                       add x
         | 
| 28 | 
            +
                     end
         | 
| 29 | 
            +
                     add 1.0/3
         | 
| 30 | 
            +
                     add 0.1
         | 
| 31 | 
            +
                     File.open('test/data.yaml','w') { |out| out << @@data.to_yaml }
         | 
| 32 | 
            +
                   end
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
            end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
            PrepareData.init
         | 
| 37 | 
            +
             | 
| 38 | 
            +
            def BigDec(x)
         | 
| 39 | 
            +
              BigDecimal.new(x.to_s)
         | 
| 40 | 
            +
            end
         | 
| @@ -0,0 +1,110 @@ | |
| 1 | 
            +
            # encoding: utf-8
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require File.expand_path(File.join(File.dirname(__FILE__),'helper.rb'))
         | 
| 4 | 
            +
            require 'test/unit'
         | 
| 5 | 
            +
            include Numerals
         | 
| 6 | 
            +
            require 'yaml'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            class TestDigitsDefinition < Test::Unit::TestCase
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              DEFAULT_DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
         | 
| 11 | 
            +
              MAX_TEST_BASE  = DEFAULT_DIGITS.size
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              def default_digits(b)
         | 
| 14 | 
            +
                DEFAULT_DIGITS[0,b]
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              def define_from_digits(b)
         | 
| 18 | 
            +
                DigitsDefinition[default_digits(b)]
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              def define_from_base(b)
         | 
| 22 | 
            +
                DigitsDefinition[base: b]
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
              def check_base(b, digits)
         | 
| 26 | 
            +
                assert_equal b, digits.radix
         | 
| 27 | 
            +
                (0...b).each do |digit_value|
         | 
| 28 | 
            +
                  digit_char = default_digits(b)[digit_value]
         | 
| 29 | 
            +
                  assert_equal digit_char, digits.digit_char(digit_value)
         | 
| 30 | 
            +
                  assert_equal digit_value, digits.digit_value(digit_char)
         | 
| 31 | 
            +
                  assert digits.is_digit?(digit_char)
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
                [-10,-5,-2,-1,b,b+1,b+2,b+10].each do |invalid_digit_value|
         | 
| 34 | 
            +
                  assert_nil digits.digit_char(invalid_digit_value)
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
                invalid_chars = %w(- / & ñ)
         | 
| 37 | 
            +
                if b < MAX_TEST_BASE
         | 
| 38 | 
            +
                  (b+1...MAX_TEST_BASE).each do |i|
         | 
| 39 | 
            +
                    invalid_chars << DEFAULT_DIGITS[i]
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
                invalid_chars.each do |invalid_digit_char|
         | 
| 43 | 
            +
                  assert_nil digits.digit_value(invalid_digit_char)
         | 
| 44 | 
            +
                  assert !digits.is_digit?(invalid_digit_char)
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
              def test_digits_definition
         | 
| 49 | 
            +
                (0..MAX_TEST_BASE).each do |base|
         | 
| 50 | 
            +
                  check_base base, define_from_digits(base)
         | 
| 51 | 
            +
                  check_base base, define_from_base(base)
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
              def test_digits_case
         | 
| 56 | 
            +
                uppercase_digits = DigitsDefinition[base: 16, downcase: false]
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                assert_equal 10, uppercase_digits.digit_value('A')
         | 
| 59 | 
            +
                assert_equal 11, uppercase_digits.digit_value('B')
         | 
| 60 | 
            +
                assert_equal 15, uppercase_digits.digit_value('F')
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                assert_equal 10, uppercase_digits.digit_value('a')
         | 
| 63 | 
            +
                assert_equal 11, uppercase_digits.digit_value('b')
         | 
| 64 | 
            +
                assert_equal 15, uppercase_digits.digit_value('f')
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                assert_equal 'A', uppercase_digits.digit_char(10)
         | 
| 67 | 
            +
                assert_equal 'B', uppercase_digits.digit_char(11)
         | 
| 68 | 
            +
                assert_equal 'F', uppercase_digits.digit_char(15)
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                downcase_digits = DigitsDefinition[base: 16, downcase: true]
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                assert_equal 10, downcase_digits.digit_value('A')
         | 
| 73 | 
            +
                assert_equal 11, downcase_digits.digit_value('B')
         | 
| 74 | 
            +
                assert_equal 15, downcase_digits.digit_value('F')
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                assert_equal 10, downcase_digits.digit_value('a')
         | 
| 77 | 
            +
                assert_equal 11, downcase_digits.digit_value('b')
         | 
| 78 | 
            +
                assert_equal 15, downcase_digits.digit_value('f')
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                assert_equal 'a', downcase_digits.digit_char(10)
         | 
| 81 | 
            +
                assert_equal 'b', downcase_digits.digit_char(11)
         | 
| 82 | 
            +
                assert_equal 'f', downcase_digits.digit_char(15)
         | 
| 83 | 
            +
             | 
| 84 | 
            +
             | 
| 85 | 
            +
                cs_uppercase_digits = DigitsDefinition[base: 16, downcase: false, case_sensitive: true]
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                assert_equal 10,  cs_uppercase_digits.digit_value('A')
         | 
| 88 | 
            +
                assert_equal 11,  cs_uppercase_digits.digit_value('B')
         | 
| 89 | 
            +
                assert_equal 15,  cs_uppercase_digits.digit_value('F')
         | 
| 90 | 
            +
                assert_nil        cs_uppercase_digits.digit_value('a')
         | 
| 91 | 
            +
                assert_nil        cs_uppercase_digits.digit_value('b')
         | 
| 92 | 
            +
                assert_nil        cs_uppercase_digits.digit_value('f')
         | 
| 93 | 
            +
                assert_equal 'A', cs_uppercase_digits.digit_char(10)
         | 
| 94 | 
            +
                assert_equal 'B', cs_uppercase_digits.digit_char(11)
         | 
| 95 | 
            +
                assert_equal 'F', cs_uppercase_digits.digit_char(15)
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                cs_downcase_digits = DigitsDefinition[base: 16, downcase: true, case_sensitive: true]
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                assert_equal 10,  cs_downcase_digits.digit_value('a')
         | 
| 100 | 
            +
                assert_equal 11,  cs_downcase_digits.digit_value('b')
         | 
| 101 | 
            +
                assert_equal 15,  cs_downcase_digits.digit_value('f')
         | 
| 102 | 
            +
                assert_nil        cs_downcase_digits.digit_value('A')
         | 
| 103 | 
            +
                assert_nil        cs_downcase_digits.digit_value('B')
         | 
| 104 | 
            +
                assert_nil        cs_downcase_digits.digit_value('F')
         | 
| 105 | 
            +
                assert_equal 'a', cs_downcase_digits.digit_char(10)
         | 
| 106 | 
            +
                assert_equal 'b', cs_downcase_digits.digit_char(11)
         | 
| 107 | 
            +
                assert_equal 'f', cs_downcase_digits.digit_char(15)
         | 
| 108 | 
            +
              end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
            end
         | 
| @@ -0,0 +1,58 @@ | |
| 1 | 
            +
            require File.expand_path(File.join(File.dirname(__FILE__),'helper.rb'))
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'numerals'
         | 
| 4 | 
            +
            include Numerals
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            class TestFloatConversions <  Test::Unit::TestCase # < Minitest::Test
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              def test_special
         | 
| 9 | 
            +
                assert_equal Numeral.nan, Conversions.number_to_numeral(Float.context.nan)
         | 
| 10 | 
            +
                assert_equal Numeral.nan, Conversions.number_to_numeral(Float.context.nan, :fixed, Rounding[:exact, base: 2])
         | 
| 11 | 
            +
                assert_equal Numeral.nan, Conversions.number_to_numeral(Float.context.nan, :fixed, Rounding[:exact, base: 10])
         | 
| 12 | 
            +
                assert_equal Numeral.nan, Conversions.number_to_numeral(Float.context.nan, :fixed, Rounding[precision: 10, base: 10])
         | 
| 13 | 
            +
                assert_equal Numeral.nan, Conversions.number_to_numeral(Float.context.nan, :free)
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                assert_equal Numeral.infinity, Conversions.number_to_numeral(Float.context.infinity)
         | 
| 16 | 
            +
                assert_equal Numeral.infinity, Conversions.number_to_numeral(Float.context.infinity, :fixed, Rounding[:exact, base: 2])
         | 
| 17 | 
            +
                assert_equal Numeral.infinity, Conversions.number_to_numeral(Float.context.infinity, :fixed, Rounding[:exact, base: 10])
         | 
| 18 | 
            +
                assert_equal Numeral.infinity, Conversions.number_to_numeral(Float.context.infinity, :fixed, Rounding[precision: 10, base: 10])
         | 
| 19 | 
            +
                assert_equal Numeral.infinity, Conversions.number_to_numeral(Float.context.infinity, :free)
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                assert_equal Numeral.infinity(-1), Conversions.number_to_numeral(Float.context.infinity(-1))
         | 
| 22 | 
            +
                assert_equal Numeral.infinity(-1), Conversions.number_to_numeral(Float.context.infinity(-1), :fixed, Rounding[:exact, base: 2])
         | 
| 23 | 
            +
                assert_equal Numeral.infinity(-1), Conversions.number_to_numeral(Float.context.infinity(-1), :fixed, Rounding[:exact, base: 10])
         | 
| 24 | 
            +
                assert_equal Numeral.infinity(-1), Conversions.number_to_numeral(Float.context.infinity(-1), :fixed, Rounding[precision: 10, base: 10])
         | 
| 25 | 
            +
                assert_equal Numeral.infinity(-1), Conversions.number_to_numeral(Float.context.infinity(-1), :free)
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                assert       Conversions.numeral_to_number(Numeral.nan, Float).nan?
         | 
| 28 | 
            +
                assert_equal Float.context.infinity, Conversions.numeral_to_number(Numeral.infinity, Float)
         | 
| 29 | 
            +
                assert_equal Float.context.infinity(-1), Conversions.numeral_to_number(Numeral.infinity(-1), Float)
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              def test_exact
         | 
| 33 | 
            +
                assert_equal Numeral[1,point:1], Conversions.number_to_numeral(1.0, :fixed, Rounding[:exact, base: 10])
         | 
| 34 | 
            +
                assert_equal Numeral[1,point:1, sign: -1], Conversions.number_to_numeral(-1.0, :fixed, Rounding[:exact, base: 10])
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                assert_equal Numeral[1,point:1, base: 2], Conversions.number_to_numeral(1.0, :fixed, Rounding[:exact, base: 2])
         | 
| 37 | 
            +
                assert_equal Numeral[1,point:1, sign: -1, base: 2], Conversions.number_to_numeral(-1.0, :fixed, Rounding[:exact, base: 2])
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                [0.1, 0.01, 0.001, 1/3.0, 10/3.0, 100/3.0, Math::PI, 0.5, 123.0, 123.45, 1.23E32, 1.23E-32].each do |x|
         | 
| 40 | 
            +
                  [x, -x].each do |y|
         | 
| 41 | 
            +
                    numeral = exact_decimal(y)
         | 
| 42 | 
            +
                    tmp = Conversions.number_to_numeral(y, :fixed, Rounding[:exact, base: 10])
         | 
| 43 | 
            +
                    assert_equal numeral, Conversions.number_to_numeral(y, :fixed, Rounding[:exact, base: 10]), "#{y} to base 10 exact numeral"
         | 
| 44 | 
            +
                    assert_equal y, Conversions.numeral_to_number(numeral, Float), "#{x} base 10 numeral to float"
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
              end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
              def exact_decimal(x)
         | 
| 50 | 
            +
                Flt::DecNum.context(exact: true){
         | 
| 51 | 
            +
                  Flt::BinNum.context(Flt::BinNum::FloatContext){
         | 
| 52 | 
            +
                    d = Flt::BinNum(x).to_decimal_exact
         | 
| 53 | 
            +
                    Numeral[d.coefficient.to_s.chars.map(&:to_i), sign: d.sign, point: d.fractional_exponent, normalize: :exact]
         | 
| 54 | 
            +
                  }
         | 
| 55 | 
            +
                }
         | 
| 56 | 
            +
              end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
            end
         |