module ZhongwenTools::Number

Constants

NUMBERS_TABLE

TODO: Add huge numbers. en.wikipedia.org/wiki/Chinese_numerals#Large_numbers 垓=亿 (archaic) 秭: 1B or 100B or 1T or 10000T (archaic) 穰 溝 澗 正 載 –> beyond 100,000,000! NOTE: financial numbers i == 0 ? NT.select{ |x| x == i }.last : NT.find{ |x| x = i }

Public Class Methods

number?(obj) click to toggle source
# File lib/zhongwen_tools/number.rb, line 17
def self.number?(obj)
  case obj
  when String
    regex = /([\d]|#{ZhongwenTools::Regex.zh_numbers}){1,}/
    "#{obj}".gsub(regex, '') == ''
  when Integer, Float
    true
  end
end
to_zh(obj, type = :zhs, from = nil) click to toggle source
# File lib/zhongwen_tools/number.rb, line 36
def self.to_zh(obj, type = :zhs, from = nil)
  case type.to_sym
  when :zht
    to_zht(obj, from)
  else
    to_zhs(obj, from)
  end
end

Private Class Methods

adjust_integer(number, curr_num) click to toggle source
# File lib/zhongwen_tools/number.rb, line 156
def self.adjust_integer(number, curr_num)
  number_multiplier?(curr_num) ? number * curr_num : number + curr_num
end
combine_integer(integers, result, curr_num, i) click to toggle source
# File lib/zhongwen_tools/number.rb, line 149
def self.combine_integer(integers, result, curr_num, i)
  next_number = integers[i + 1]
  result += next_number * curr_num if number_multiplier?(next_number)

  [result, i]
end
combine_integers(integers) click to toggle source
# File lib/zhongwen_tools/number.rb, line 120
def self.combine_integers(integers)
  return combine_year(integers) if year?(integers)

  number = 0
  length = integers.size
  skipped = false

  integers.each_with_index do |curr_num, i|
    next if skipped == i

    if (i + 2) <= length
      number, i = combine_integer(integers, number, curr_num, i)
      skipped = i + 1
    else
      number = adjust_integer(number, curr_num)
    end
  end

  number
end
combine_year(integers) click to toggle source
# File lib/zhongwen_tools/number.rb, line 145
def self.combine_year(integers)
  integers.map(&:to_s).join.to_i
end
convert(obj, to, from, separator = '') click to toggle source
# File lib/zhongwen_tools/number.rb, line 47
def self.convert(obj, to, from, separator = '')
  fail ArgumentError unless [:zhs, :zht, :i, :pyn].include?(to.to_sym)
  fail ArgumentError unless CONVERTIBLE_OBJECTS.include?(obj.class)

  number =   convert_from from, to, obj

  if to == :i
    combine_integers(number)
  elsif to == :pyn
    regex = /#{ %w(yi4 wan4 qian1 bai2 shi2).map { |x| 'ling2\-' + x }.join('|')}/
    finalize_number(number, '-').gsub(regex, '').gsub(/\-+/, '-').gsub(/\-$/, '')
  else
    finalize_number(number)
  end
end
convert_from(from, to, number) click to toggle source
# File lib/zhongwen_tools/number.rb, line 90
def self.convert_from(from, to, number)
  if from == :zht
    convert_from_zh(to, number)
  elsif from == :zhs
    convert_from_zh(to, number)
  elsif from == :i
    convert_from_integer(to, number)
  elsif from == :pyn
    convert_from_pyn(to, number)
  end
end
convert_from_integer(to, int) click to toggle source
# File lib/zhongwen_tools/number.rb, line 164
def self.convert_from_integer(to, int)
  # FIXME: this will fail for numbers over 1 billion.
  result = []
  nums = convert_integer_to_reversed_array_of_integers(int)

  nums.each_with_index do |num, i|
    wan =  wan_level(wan, i)

    if i == 0
      result << convert_integer(num, to) unless num == 0
    else
      if i < 5
        result << convert_wan_level(i, to)
      elsif i == 8
        result << convert_wan_level(i, to)
      else
        result << "#{ convert_wan_level(i - (i / 4 * 4), to) }#{ convert_wan_level((i / 4 * 4), to) }"
      end
      # checks the wan level and ...
      result << convert_integer(num, to) if wan_ok?(num, wan, i)
    end
  end

  result.reverse!
end
convert_from_pyn(to, pyn) click to toggle source
# File lib/zhongwen_tools/number.rb, line 102
def self.convert_from_pyn(to, pyn)
  # convert to pyn
  # split the pyn and then
  pyns = ZhongwenTools::Romanization::Pinyin.split_pyn(pyn)

  pyns.map do |p|
    convert_number(p).fetch(to) { p }
  end
end
convert_from_zh(to, number) click to toggle source
# File lib/zhongwen_tools/number.rb, line 112
def self.convert_from_zh(to, number)
  converted_number = number.chars.map do |zh|
    convert_number(zh).fetch(to) { zh }
  end

  converted_number
end
convert_integer(int, to) click to toggle source
# File lib/zhongwen_tools/number.rb, line 209
def self.convert_integer(int, to)
  NUMBERS_TABLE.find { |x| x[:i] == int }.fetch(to) { 0 }
end
convert_integer_to_reversed_array_of_integers(int) click to toggle source
# File lib/zhongwen_tools/number.rb, line 190
def self.convert_integer_to_reversed_array_of_integers(int)
  int.to_s.chars.to_a.reverse.map(&:to_i)
end
convert_number(number) click to toggle source
# File lib/zhongwen_tools/number.rb, line 213
def self.convert_number(number)
  NUMBERS_TABLE.find { |x|  x[:zhs] == number || x[:zht] == number || x[:pyn] == number }
end
convert_wan_level(i, to) click to toggle source
# File lib/zhongwen_tools/number.rb, line 205
def self.convert_wan_level(i, to)
  convert_integer((10**(i)), to) || convert_integer((10**(i) / 10_000), to) || convert_integer((10**(i) / 10_000**2), to)
end
finalize_number(number, separator = '') click to toggle source
# File lib/zhongwen_tools/number.rb, line 217
def self.finalize_number(number, separator = '')
  # FIXME: is finalize_number the best name you can think of?
  # NOTE: Figuring out usage of "liang" vs. "er" is pretty
  #       difficult, so always use "er" instead.
  number.join(separator).gsub(/零#{ Regex.zh_number_multiple }*/u, '')
end
number_multiplier?(number) click to toggle source
# File lib/zhongwen_tools/number.rb, line 160
def self.number_multiplier?(number)
  [10, 100, 1_000, 10_000, 100_000_000].include? number
end
number_type(obj) click to toggle source
# File lib/zhongwen_tools/number.rb, line 63
def self.number_type(obj)
  klass = obj.class

  if NUMBER_CLASSES.include?(klass)
    :i
  else
    if ZhongwenTools::Zhongwen.zh?(obj)
      if zht?(obj)
        :zht
      else
        :zhs
      end
    else # assume it is pyn
      # FIXME: might need to convert to pyn
      :pyn
    end
  end
end
wan_level(wan, i) click to toggle source
# File lib/zhongwen_tools/number.rb, line 198
def self.wan_level(wan, i)
  wan ||= 0
  wan += 1 if (i + 1) % 5 == 0

  wan
end
wan_ok?(num, wan, i) click to toggle source
# File lib/zhongwen_tools/number.rb, line 194
def self.wan_ok?(num, wan, i)
  (num == 1 && (10**(i) / 10_000**wan) != 10) || num != 1
end
year?(integers) click to toggle source
# File lib/zhongwen_tools/number.rb, line 141
def self.year?(integers)
  integers.select { |i| i < 10 }.size == integers.size
end
zhs?(str) click to toggle source
# File lib/zhongwen_tools/number.rb, line 86
def self.zhs?(str)
  !zht?(str)
end
zht?(str) click to toggle source
# File lib/zhongwen_tools/number.rb, line 82
def self.zht?(str)
  str[/#{ZhongwenTools::Regex.zht_numbers }*/] == str
end