module Nth

Constants

FRACTION_UTF
NUMBERS
ORDINALS

might go to just under Duomillinillion, but not until next revision to go higher, need the definitive naming scheme.

ORD_FORMAT_ERROR

NUMBERS.each_pair { |key,val| ords = val + “th” if ords.nil? } ORDINALS = ords.freeze

PLURAL_EXCPETIONS
PRE_TABLE
see

en.wikipedia.org/wiki/Names_of_large_numbers

SET_NOVE
SET_TRE
VERSION

Public Class Methods

calculate_prefix(str) click to toggle source
# File lib/nth.rb, line 272
def self.calculate_prefix(str)   # see: https://en.wikipedia.org/wiki/Names_of_large_numbers
  raise ORD_FORMAT_ERROR unless str.index('ii').nil?
  str = str.dup
  m = 0
  if (str[0..4]=='milli')
    m = 1000
    str = str[5..-1]
  end
  n = 0
  pre, props = lookup_prefix(str)
  raise ORD_FORMAT_ERROR if pre.nil?
  if props[:val] < 10
    n += props[:val]
    if props[:if].empty? # simple case
      str = str[pre.length..-1]
      pre, props = lookup_prefix(str)
    else  # tricky case
      str = str[pre.length..-1]
      pre, sfx = lookup_prefix(str)
      if pre.nil?
        xs = str[0]
        str = str[1..-1]
        pre, sfx = lookup_prefix(str)
        raise ORD_FORMAT_ERROR if pre.nil?
        ch = (props[:if] & sfx[:rpl]).first.to_s
        unless ch==xs
          ch = ch.to_sym
          raise ORD_FORMAT_ERROR unless props[:if].include? ch
          raise ORD_FORMAT_ERROR unless props[:sfx].include? xs.to_sym
        end
      else
        raise ORD_FORMAT_ERROR unless (props[:if] & sfx[:rpl]).empty?
      end
      props = sfx       
    end
  end
  if pre && (props[:val] < 100)
    str = str[pre.length..-1]
    n += props[:val]
    pre, sfx = lookup_prefix(str)
    if pre.nil?
      ch = str[0]
      str = str[1..-1]
      pre, sfx = lookup_prefix(str)
      unless pre.nil?
        cch = (sfx[:rpl] & props[:sfx]).first.to_s
        raise ORD_FORMAT_ERROR unless cch == ch
      end
      props = sfx
    end
  end
  if pre && (props[:val] < 1000)
    str = str[pre.length..-1]
    n += props[:val]
    pre, sfx = lookup_prefix(str)
    raise ORD_FORMAT_ERROR unless pre.nil?  # too many!
  end
  n += m    
  raise ORD_FORMAT_ERROR if n <= 10
  n *= 3
  n = 10 ** (n+3)
  return n
end
create_big_number_name(mult) click to toggle source
# File lib/nth.rb, line 455
def self.create_big_number_name(mult)
  pre = ""
  num = (mult-3) / 3
  if (num >= 1000)
    idx = "10**#{mult}"
    rtn = NUMBERS[idx]
    return rtn unless rtn.nil?
    pre = "milli"
    num -= 1000
    raise "Number too big" if num >= 1000  # next revision may address larger numbers
  end
  huns, dec  = num.divmod 100
  tens, ones = dec.divmod 10
  str = ""
  unless ones.zero?
    if    (1==ones)
      str = "un"
    elsif (2==ones)
      str = "duo"
    elsif (3==ones)
      str = "tre"
      if [2,3,4,5,8].include? tens
        str += 's'
      elsif tens.zero?
        if [1,3,4,5,8].include? huns
          str += 's'
        end
      end
    elsif (4==ones)
      str = "quattuor"
    elsif (5==ones)
      str = "quinqua"
    elsif (6==ones)
      str = "se"
      if [2,3,4,5].include? tens
        str += 's'
      elsif 8==tens
        str += 'x'  
      elsif tens.zero?
        if [3,4,5].include? huns
          str += 's'
        elsif [1,8].include? huns  
          str += 'x'
        end
      end
    elsif (7==ones)
      str = "septe"
      if [1,8].include? tens
        str += 'm'
      elsif [1,3,4,5,6,7].include? tens
        str += 'n'
      elsif tens.zero?
        if (1..7).include? huns
          str += 'n'
        elsif 8 == huns
          str += 'm'
        end
      end
    elsif (8==ones)
      str = "octo"
    elsif (9==ones)
      str = "nove"
      if [1,8].include? tens
        str += 'm'
      elsif [1,3,4,5,6,7].include? tens
        str += 'n'
      elsif tens.zero?
        if (1..7).include? huns
          str += 'n'
        elsif 8 == huns
          str += 'm'
        end
      end
    end  
  end
  unless tens.zero?
    if    (1==tens)
      str += 'dec' 
      str += 'i' unless huns.zero?  
    elsif (2==tens)    
      str += 'vigint'   
      str += 'i' unless huns.zero?  
    elsif (3==tens)    
      str += 'trigint'   
      str += 'a' unless huns.zero?  
    elsif (4==tens)    
      str += 'quadragint'   
      str += 'a' unless huns.zero?  
    elsif (5==tens)    
      str += 'quinquagint'   
      str += 'a' unless huns.zero?  
    elsif (6==tens)    
      str += 'sexagint'   
      str += 'a' unless huns.zero?  
    elsif (7==tens)    
      str += 'septuagint'   
      str += 'a' unless huns.zero?  
    elsif (8==tens)    
      str += 'octogint'   
      str += 'a' unless huns.zero?  
    elsif (9==tens)    
      str += 'nonagint'   
      str += 'a' unless huns.zero?  
    end
  end
  unless huns.zero?
    if    (1==huns)
      str += 'cent'   
    elsif (2==huns)    
      str += 'ducent'   
    elsif (3==huns)    
      str += 'trecent'   
    elsif (4==huns)    
      str += 'quadringent'   
    elsif (5==huns)    
      str += 'quingent'   
    elsif (6==huns)    
      str += 'sescent'   
    elsif (7==huns)    
      str += 'septingent'   
    elsif (8==huns)    
      str += 'octingent'   
    elsif (9==huns)    
      str += 'nongent'   
    end
  end
  return pre + str + 'illion'
end
get_denominator_name(int) click to toggle source
# File lib/nth.rb, line 688
def self.get_denominator_name(int)
  return "half"    if 2==int
  return "quarter" if 4==int
  return get_ordinal_name(int, :-)
end
get_n_from_array(ary) click to toggle source

these may need to be redone …

# File lib/nth.rb, line 411
def self.get_n_from_array(ary)
  if("minus"==ary.first)
    n = get_n_from_array(ary[1..-1])
    return nil if n.nil?
    return -n
  end
  a = lookup_number(ary[0])
  if a.nil?
    a = lookup_ordinal(a[0])
    return nil if a.nil?
    raise ORD_FORMAT_ERROR if is ary.size > 1
    return a
  end
  b = lookup_number(ary[1])
  if b.nil?
    b = lookup_ordinal(ary[1])
    raise ORD_FORMAT_ERROR if b.nil?
    raise ORD_FORMAT_ERROR unless ary[2].nil?
    if a > b  # forty_fourth
    else
    end
  else
  end
  nil
end
get_n_from_string(ordinal) click to toggle source
# File lib/nth.rb, line 437
def self.get_n_from_string(ordinal)
  n = lookup_ordinal(ordinal)
  return n unless n.nil? 
  return get_n_from_array(ordinal.split('_'))
end
get_number_name(int, *flags) click to toggle source
# File lib/nth.rb, line 584
def self.get_number_name(int, *flags)    
  ary = get_number_struct(int)
  neg = ary.pop == :neg
  return NUMBERS[0] if ary.empty?
  ary.reverse!
  mult = 0
  str = ""
  ch  = flags.include?(:_) ? "_" : " "
  tch = flags.include?(:-) ? "-" : ch
  loop do
    break if ary.empty?
    num = ary.pop
    hun, dec = num.divmod 100
    tstr = ""
    unless hun.zero?
      tstr = NUMBERS[hun] + ch + NUMBERS[100]
      unless dec.zero?
        tstr += ch
      end
    end
    unless dec.zero?
      if NUMBERS[dec].nil?
        tens, ones = dec.divmod 10
        tstr += NUMBERS[tens*10] + tch + NUMBERS[ones]
      else
        tstr += NUMBERS[dec]
      end
    end
    unless tstr.empty?
      if mult.zero?
        str = tstr
      else
        ms = "10**#{mult}"
        mm = NUMBERS[ms]  # if you run out, you will need a generator
        mm = create_big_number_name(mult) if mm.nil?
        if str.empty?
          str = tstr + ch + mm
        else
          str = tstr + ch + mm + ch + str
        end
      end        
    end
    mult += 3
  end
  if flags.include? :plus_minus
    if neg # use default
      return "minus" + ch + str
    end
    return "plus" + ch + str
  end
  if flags.include? :positive_negative
    if neg # use default
      return "negative" + ch + str
    end
    return "positive" + ch + str
  end
  if neg # use default
    return "minus" + ch + str
  end
  return str    
end
get_number_struct(int) click to toggle source
# File lib/nth.rb, line 444
def self.get_number_struct(int)
  neg = int < 0
  int = -int if neg
  dat = []
  while(int > 0)
    int, triplet = int.divmod 1000
    dat.push triplet
  end
  dat.push neg ? :neg : :pos
end
get_ordinal_name(int, *flags) click to toggle source
# File lib/nth.rb, line 646
def self.get_ordinal_name(int, *flags)
  return ORDINALS[0] if int.zero?
  ch  = flags.include?(:_) ? "_" : " "
  tch = flags.include?(:-) ? "-" : ch
  if int.negative?
    if flags.include? :positive_negative
      pre = "negative" + ch
    else flags.include? :plus_minus
      pre = "minus" + ch
    end
  else
    if flags.include? :positive_negative
      pre = "positive" + ch
    elsif flags.include? :plus_minus
      pre = "plus" + ch
    else 
      pre = ""  
    end
  end
  flags = flags - [:positive_negative, :plus_minus]
  int = int.abs
  n1,n2 = int.divmod 100
  unless n2.zero?
    n1 *= 100
    if n1.zero?
      str = pre
    else
      str = pre + get_number_name(n1, *flags)
      str += ch
    end
    if ORDINALS.include? n2
      str += ORDINALS[n2]
      return str
    end
    n1,n2 = n2.divmod 10
    n1 *= 10
    str += NUMBERS[n1] + tch + ORDINALS[n2]
    return str
  end
  return pre + get_number_name(int, *flags) + "th"
end
install(pn = :None, *plist) click to toggle source
# File lib/nth.rb, line 1009
def self.install(pn = :None, *plist)
  if    (pn==:IntMethods)
    Integer.class_eval do
      unless self.instance_methods.include? :nth
        def nth(comma=true)
          Nth::IntMethods.to_nth_string(self, comma)
        end
      end
      unless self.instance_methods.include? :to_ordinal
        def to_ordinal
          Nth::IntMethods.to_ordinal_string(self)        
        end
      end
      unless self.instance_methods.include? :to_number
        def to_number
          Nth::IntMethods.to_number_string(self)        
        end
      end  
      unless self.instance_methods.include? :cents_to_dollars
        def cents_to_dollars
          Nth::IntMethods.penny_to_dollar(self)
        end
      end
      unless self.instance_methods.include? :pence_to_pounds
        def pence_to_pounds
          Nth::IntMethods.pence_to_pound(self)
        end
      end
    end
    # add to wrapper classes ...
    meths = [:nth, :to_ordinal, :to_number, :cents_to_dollars, :pence_to_pounds]
    Int.bestow_methods(*meths)
    Number.bestow_methods(*meths)
    Datum.bestow_methods(*meths)
  elsif (pn==:FloatMethods)
    Float.class_eval do
      unless self.instance_methods.include? :to_fractional_unit
        def to_fractional_unit(unit_name, mode = :descriptive, tar=0.01)
          Nth::FloatMethods.to_fractional_unit(self, unit_name, mode, tar)
        end
      end
      unless self.instance_methods.include? :inches_to_feet
        def inches_to_feet(mode = :descriptive, denom=16)
          Nth::FloatMethods.inches_to_feet(self, mode, denom)
        end
      end
      unless self.instance_methods.include? :inches_to_yards
        def inches_to_yards(mode = :descriptive, denom=16)
          Nth::FloatMethods.inches_to_yards(self, mode, denom)
        end
      end
      unless self.instance_methods.include? :to_dms
        def to_dms(sec_frac=2)
          Nth::FloatMethods.to_dms(self, sec_frac)
        end
      end
    end
  elsif (pn==:AccessAllOrdinals)
    plist[0].class_eval do
      @default_insert = plist[1]
      if @default_insert.nil?
        if self==String
          @default_insert = ' '
        end
      end
      def respond_to_missing?(method_name, include_private = false)
        begin
          meth = method_name.to_s
          str = meth[-1]=="=" ? meth[0..-2] : meth
          num = Nth::ordinal_string_to_number(str)
          return num.type_of? Integer
        rescue
          return false
        end
        return false
      end
      def method_missing(method_name, *prms, &block)
        meth = method_name.to_s
        num = nil
        assign = false
        str = ""
        begin
          if meth[-1]=="="
            str = meth[0..-2]
            assign = true
          else
            str = meth
          end
          num = Nth::ordinal_string_to_number(str)
        rescue
          super
        end
        super if num.nil?
        if assign
          if 1==prms.count
            if num >= size
              rpl = self.class.instance_variable_get :@default_insert
              (size..(num-1)).each { |t| self[t] = rpl }
            end
            self[num] = prms[0]
            return self
          else
            raise "*** ArgumentError Exception: wrong number of arguments (given #{prms.count}, expected 1)"
          end
        else
          raise "*** ArgumentError Exception: wrong number of arguments (given #{prms.count}, expected 0)" if prms.count > 0
          return self[num]
        end          
      end
    end
  elsif (pn==:AccessOrdinals)
    plist[0].class_eval do
      @default_insert = plist[2]
      if @default_insert.nil?
        if self==String
          @default_insert = ' '
        end
      end
      unless self.instance_methods.include? :last
        def last
          self[-1]
        end
      end
      unless self.instance_methods.include? :last=
        def last=(val)
          self[-1]=val
        end
      end
      ran = (1..plist[1])
      ran.each do |num|
        meth = Nth::get_ordinal_name(num, :_)
        unless self.instance_methods.include? meth.to_sym
          define_method meth.to_sym do
            self[num-1]
          end
        end
        meth += '='
        unless self.instance_methods.include? meth.to_sym
          define_method meth.to_sym do |val| 
            if num >= size
              rpl = self.class.instance_variable_get :@default_insert
              (size..(num-1)).each { |t| self[t] = rpl }
            end
            self[num-1] = val
            self
          end
        end    
      end
    end
  else
    raise "unrecognized install parameter #{pn}"  
  end
end
lookup_number(num) click to toggle source

this only looks up a single item… need to expand for other prefixes/

# File lib/nth.rb, line 337
def self.lookup_number(num)
  return eval(NUMBERS.key(num).to_s) if (NUMBERS.values.include? num)
  if ("illion" == num[-6..-1])
    # str = num[0..-7]
    return calculate_prefix(num)
  end
  return nil
end
lookup_ordinal(ordinal) click to toggle source
# File lib/nth.rb, line 346
def self.lookup_ordinal(ordinal)
  return 0 if (ordinal=="last")
  return eval(ORDINALS.key(ordinal).to_s) if (ORDINALS.values.include? ordinal)
  str = ordinal[0..-3]
  return lookup_number(str)
end
lookup_prefix(str) click to toggle source
# File lib/nth.rb, line 235
def self.lookup_prefix(str)
  if (PRE_TABLE.include? str[0..11])    
    return [str[0..11], PRE_TABLE[str[0..11]]]
  end
  if (PRE_TABLE.include? str[0..10])    
    return [str[0..10], PRE_TABLE[str[0..10]]]
  end
  if (PRE_TABLE.include? str[0..9])    
    return [str[0..9], PRE_TABLE[str[0..9]]]
  end
  if (PRE_TABLE.include? str[0..8])    
    return [str[0..8], PRE_TABLE[str[0..8]]]
  end
  if (PRE_TABLE.include? str[0..7])    
    return [str[0..7], PRE_TABLE[str[0..7]]]
  end
  if (PRE_TABLE.include? str[0..6])
    return [str[0..6], PRE_TABLE[str[0..6]]]
  end
  if (PRE_TABLE.include? str[0..5])
    return [str[0..5], PRE_TABLE[str[0..5]]]
  end
  if (PRE_TABLE.include? str[0..4])
    return [str[0..4], PRE_TABLE[str[0..4]]]
  end
  if (PRE_TABLE.include? str[0..3])
    return [str[0..3], PRE_TABLE[str[0..3]]]
  end  
  if (PRE_TABLE.include? str[0..2])
    return [str[0..2], PRE_TABLE[str[0..2]]]
  end
  if (PRE_TABLE.include? str[0..1])
    return [str[0..1], PRE_TABLE[str[0..1]]]
  end
  return [nil,nil]
end
num_ord_string_to_ary(str) click to toggle source
# File lib/nth.rb, line 353
def self.num_ord_string_to_ary(str)
  return str.split('_') if str.index(' ').nil?  # underscore format
  ary = []
  aaa = str.split ' '
  aaa.each do |elm|
    ary += elm.split '-'
  end
  return ary
end
number_string_to_number(str_ary) click to toggle source
# File lib/nth.rb, line 363
def self.number_string_to_number(str_ary)
  sum = 0
  psum = 0
  neg = false
  if str_ary.kind_of? String
    ary = num_ord_string_to_ary(str_ary)
  else
    ary = str_ary
  end    
  ary.each do |elm|
    if elm == 'minus'
      neg = true
      break
    end 
    num = lookup_number(elm)
    return nil if num.nil?
    if num < 1000
      if psum==0
        psum = num
      elsif num > psum
        psum *= num  
      else
        psum += num
      end
    else
      sum += num * psum
      psum = 0
    end
  end
  sum += psum
  sum = -sum if neg
  return sum
end
ordinal_string_to_number(str) click to toggle source
# File lib/nth.rb, line 397
def self.ordinal_string_to_number(str)
  return -1 if str=="last"
  sum = 0
  ary = num_ord_string_to_ary(str)
  nth = ary.pop
  val = lookup_ordinal(nth)
  return nil if val.nil?
  ary.push get_number_name(val).split(' ').last
  num = number_string_to_number(ary)
  return nil if num.nil?
  return num-1
end
pluralize(str) click to toggle source

utility methods: the gem pluralize too simple … maybe you should fix this …

::TODO

move to new gem, then update this gem

# File lib/nth.rb, line 205
def self.pluralize(str)
  str = str.rstrip
  tst = PLURAL_EXCPETIONS[str]
  return tst unless tst.nil?
  ch  = str[-1]
  sfx = str[-2..-1]  
  if ['ay','ey','oy','uy','iy'].include? sfx
    return str + 's'
  elsif ['is'].include? sfx  # analysis analyses
    return str[0..-3] +'es'   
  elsif ['us'].include? sfx  # focus foci
    return str[0..-3] +'i'
  elsif sfx=='fe'
    return str[0..-3] + 'ves'
  elsif ch=='f'
    return str[0..-2] + 'ves'
  elsif ch=='y'
    return str[0..-2] +'ies'
  elsif ['s','x','z','o'].include? ch
    return str +'es'
  elsif ['ch', 'sh'].include? sfx
    return str +'es'    
  elsif ['um'].include? sfx  # datum data
    return str[0..-3] +'a'   
  #elsif ['on'].include? sfx  # criterion criteria  <<< more the exception than the rule
  #  return str[0..-2] +'a' unless str[-3]=='o'  # moon, spoon     ...  pion  peon  son won ton
  end    
  return str + 's'
end

Public Instance Methods

cents_to_dollars() click to toggle source
# File lib/nth.rb, line 1028
def cents_to_dollars
  Nth::IntMethods.penny_to_dollar(self)
end
inches_to_feet(mode = :descriptive, denom=16) click to toggle source
# File lib/nth.rb, line 1051
def inches_to_feet(mode = :descriptive, denom=16)
  Nth::FloatMethods.inches_to_feet(self, mode, denom)
end
inches_to_yards(mode = :descriptive, denom=16) click to toggle source
# File lib/nth.rb, line 1056
def inches_to_yards(mode = :descriptive, denom=16)
  Nth::FloatMethods.inches_to_yards(self, mode, denom)
end
last() click to toggle source
# File lib/nth.rb, line 1128
def last
  self[-1]
end
last=(val) click to toggle source
# File lib/nth.rb, line 1133
def last=(val)
  self[-1]=val
end
method_missing(method_name, *prms, &block) click to toggle source
Calls superclass method
# File lib/nth.rb, line 1085
def method_missing(method_name, *prms, &block)
  meth = method_name.to_s
  num = nil
  assign = false
  str = ""
  begin
    if meth[-1]=="="
      str = meth[0..-2]
      assign = true
    else
      str = meth
    end
    num = Nth::ordinal_string_to_number(str)
  rescue
    super
  end
  super if num.nil?
  if assign
    if 1==prms.count
      if num >= size
        rpl = self.class.instance_variable_get :@default_insert
        (size..(num-1)).each { |t| self[t] = rpl }
      end
      self[num] = prms[0]
      return self
    else
      raise "*** ArgumentError Exception: wrong number of arguments (given #{prms.count}, expected 1)"
    end
  else
    raise "*** ArgumentError Exception: wrong number of arguments (given #{prms.count}, expected 0)" if prms.count > 0
    return self[num]
  end          
end
nth(comma=true) click to toggle source
# File lib/nth.rb, line 1013
def nth(comma=true)
  Nth::IntMethods.to_nth_string(self, comma)
end
pence_to_pounds() click to toggle source
# File lib/nth.rb, line 1033
def pence_to_pounds
  Nth::IntMethods.pence_to_pound(self)
end
respond_to_missing?(method_name, include_private = false) click to toggle source
# File lib/nth.rb, line 1074
def respond_to_missing?(method_name, include_private = false)
  begin
    meth = method_name.to_s
    str = meth[-1]=="=" ? meth[0..-2] : meth
    num = Nth::ordinal_string_to_number(str)
    return num.type_of? Integer
  rescue
    return false
  end
  return false
end
to_dms(sec_frac=2) click to toggle source
# File lib/nth.rb, line 1061
def to_dms(sec_frac=2)
  Nth::FloatMethods.to_dms(self, sec_frac)
end
to_fractional_unit(unit_name, mode = :descriptive, tar=0.01) click to toggle source
# File lib/nth.rb, line 1046
def to_fractional_unit(unit_name, mode = :descriptive, tar=0.01)
  Nth::FloatMethods.to_fractional_unit(self, unit_name, mode, tar)
end
to_number() click to toggle source
# File lib/nth.rb, line 1023
def to_number
  Nth::IntMethods.to_number_string(self)        
end
to_ordinal() click to toggle source
# File lib/nth.rb, line 1018
def to_ordinal
  Nth::IntMethods.to_ordinal_string(self)        
end