Module: Seimi::Kanji
- Defined in:
- lib/seimi/kanji.rb
Constant Summary collapse
- DIGITS =
%w[〇 一 二 三 四 五 六 七 八 九].freeze
- PARSE_DIGITS =
DIGITS.each_with_index.to_h.merge("零" => 0).freeze
- SMALL_UNITS =
{ "十" => 10, "百" => 100, "千" => 1000 }.freeze
- LARGE_UNITS =
[["兆", 1_000_000_000_000], ["億", 100_000_000], ["万", 10_000]].freeze
Class Method Summary collapse
- .decimal(value, digits = 3) ⇒ Object
- .from_i(number) ⇒ Object
- .large_unit_at(index) ⇒ Object
- .rational(value) ⇒ Object
- .to_i(text) ⇒ Object
- .under_10000(number) ⇒ Object
- .under_10000_to_i(text) ⇒ Object
- .unit_part(value, unit) ⇒ Object
Class Method Details
.decimal(value, digits = 3) ⇒ Object
58 59 60 61 62 |
# File 'lib/seimi/kanji.rb', line 58 def decimal(value, digits = 3) formatted = format("%.#{digits}f", value) integer, fraction = formatted.split(".", 2) "#{from_i(integer.to_i)}・#{fraction.chars.map { |char| DIGITS[char.to_i] }.join}" end |
.from_i(number) ⇒ Object
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/seimi/kanji.rb', line 12 def from_i(number) integer = Integer(number) return "零" if integer.zero? return "負#{from_i(-integer)}" if integer.negative? groups = [] while integer.positive? groups << integer % 10_000 integer /= 10_000 end groups.each_with_index.filter_map do |group, index| next if group.zero? "#{under_10000(group)}#{large_unit_at(index)}" end.reverse.join end |
.large_unit_at(index) ⇒ Object
80 81 82 83 84 85 86 87 88 89 |
# File 'lib/seimi/kanji.rb', line 80 def large_unit_at(index) case index when 0 then "" when 1 then "万" when 2 then "億" when 3 then "兆" else raise ArgumentError, "number is too large" end end |
.rational(value) ⇒ Object
49 50 51 52 53 54 55 56 |
# File 'lib/seimi/kanji.rb', line 49 def rational(value) rational = Rational(value) return from_i(rational.numerator) if rational.denominator == 1 numerator = rational.numerator sign = numerator.negative? ? "負" : "" "#{sign}#{from_i(rational.denominator)}分の#{from_i(numerator.abs)}" end |
.to_i(text) ⇒ Object
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/seimi/kanji.rb', line 30 def to_i(text) source = String(text) return 0 if source == "零" || source == "〇" negative = source.start_with?("負") source = source.delete_prefix("負") total = 0 LARGE_UNITS.each do |unit, scale| next unless source.include?(unit) head, source = source.split(unit, 2) total += under_10000_to_i(head) * scale end total += under_10000_to_i(source) negative ? -total : total end |
.under_10000(number) ⇒ Object
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/seimi/kanji.rb', line 64 def under_10000(number) integer = Integer(number) raise ArgumentError, "out of range" if integer.negative? || integer >= 10_000 thousands, rem = integer.divmod(1000) hundreds, rem = rem.divmod(100) tens, ones = rem.divmod(10) [ unit_part(thousands, "千"), unit_part(hundreds, "百"), unit_part(tens, "十"), ones.positive? ? DIGITS[ones] : nil ].compact.join end |
.under_10000_to_i(text) ⇒ Object
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/seimi/kanji.rb', line 98 def under_10000_to_i(text) return 0 if text.empty? total = 0 current = nil text.each_char do |char| if PARSE_DIGITS.key?(char) current = PARSE_DIGITS.fetch(char) elsif SMALL_UNITS.key?(char) total += (current || 1) * SMALL_UNITS.fetch(char) current = nil else raise ArgumentError, "unknown kanji digit: #{char}" end end total + (current || 0) end |
.unit_part(value, unit) ⇒ Object
91 92 93 94 95 96 |
# File 'lib/seimi/kanji.rb', line 91 def unit_part(value, unit) return nil if value.zero? return unit if value == 1 "#{DIGITS[value]}#{unit}" end |