lib/extenso_pt.rb in extenso_pt-0.5.7 vs lib/extenso_pt.rb in extenso_pt-0.5.8
- old
+ new
@@ -1,273 +1,41 @@
# frozen_string_literal: true
-require 'extenso_pt/version'
require 'bigdecimal/util'
+require 'extenso_pt/version'
+require 'extenso_pt/constantes'
+require 'extenso_pt/module'
-LC = %i[pt br].freeze
-A0020 = {
- pt: ['', 'UM', 'DOIS', 'TRÊS', 'QUATRO', 'CINCO', 'SEIS', 'SETE',
- 'OITO', 'NOVE', 'DEZ', 'ONZE', 'DOZE', 'TREZE', 'CATORZE',
- 'QUINZE', 'DEZASSEIS', 'DEZASSETE', 'DEZOITO', 'DEZANOVE'],
- br: ['', 'UM', 'DOIS', 'TRES', 'QUATRO', 'CINCO', 'SEIS', 'SETE',
- 'OITO', 'NOVE', 'DEZ', 'ONZE', 'DOZE', 'TREZE', 'QUATORZE',
- 'QUINZE', 'DEZESSEIS', 'DEZESSETE', 'DEZOITO', 'DEZENOVE']
-}.freeze
-A0100 = {
- pt: ['', '', 'VINTE', 'TRINTA', 'QUARENTA', 'CINQUENTA', 'SESSENTA',
- 'SETENTA', 'OITENTA', 'NOVENTA'],
- br: ['', '', 'VINTE', 'TRINTA', 'QUARENTA', 'CINQUENTA', 'SESSENTA',
- 'SETENTA', 'OITENTA', 'NOVENTA']
-}.freeze
-A1000 = {
- pt: ['', 'CEM', 'CENTO', 'DUZENTOS', 'TREZENTOS', 'QUATROCENTOS',
- 'QUINHENTOS', 'SEISCENTOS', 'SETECENTOS', 'OITOCENTOS', 'NOVECENTOS'],
- br: ['', 'CEM', 'CENTO', 'DUZENTOS', 'TREZENTOS', 'QUATROCENTOS',
- 'QUINHENTOS', 'SEISCENTOS', 'SETECENTOS', 'OITOCENTOS', 'NOVECENTOS']
-}.freeze
-S1E24 = { pt: ['', 'MIL', ' MILHÃO', ' MIL MILHÃO', ' BILIÃO',
- ' MIL BILIÃO', ' TRILIÃO', ' MIL TRILIÃO'],
- br: ['', 'MIL', ' MILHÃO', ' BILHÃO', ' TRILHÃO',
- ' QUADRILHÃO', ' QUINTILHÃO', ' SEXTILHÃO'] }.freeze
-P1E24 = { pt: ['', ' MIL', ' MILHÕES', ' MIL MILHÕES', ' BILIÕES',
- ' MIL BILIÕES', ' TRILIÕES', ' MIL TRILIÕES'],
- br: ['', ' MIL', ' MILHÕES', ' BILHÕES', ' TRILHÕES',
- ' QUADRILHÕES', ' QUINTILHÕES', ' SEXTILHÕES'] }.freeze
-
# @author Hernani Rodrigues Vaz
module ExtensoPt
class Error < StandardError; end
- # Produz o extenso das centenas em portugues de portugal ou brasil.
+ # Produz extenso(s)) em portugues de portugal ou brasil
#
- # @param [Integer] mil o valor dum grupo 3 digitos a converter
- # @return [String] o extenso das centenas
- def self.e900(mil)
- A1000[@lc][(mil > 100 ? 1 : 0) + mil / 100] +
- (mil > 100 && (mil % 100).positive? ? ' E ' : '') # proposicao
- end
-
- # Produz o extenso das dezenas em portugues de portugal ou brasil.
- #
- # @param [Integer] mil o valor dum grupo 3 digitos a converter
- # @return [String] o extenso das dezenas
- def self.e90(mil)
- A0100[@lc][mil % 100 / 10] +
- (mil > 20 && (mil % 10).positive? ? ' E ' : '') # proposicao
- end
-
- # Produz o extenso das unidades em portugues de portugal ou brasil.
- #
- # @param [Integer] cem o valor dum grupo 3 digitos a converter
- # @return [String] o extenso das unidades
- def self.e9(cem)
- A0020[@lc][(cem < 20 ? cem : cem % 10)]
- end
-
- # Produz extenso parte fracionaria em portugues de portugal ou brasil.
- #
- # @return [String] o extenso da parte fracionaria dum valor monetario
- def self.ef99
- if @nf.positive?
- e90(@nf) + e9(@nf) + (@nf > 1 ? ' ' + @cp : ' ' + @cs)
- else
- ''
- end
- end
-
- # Produz final da moeda em portugues de portugal ou brasil.
- #
- # @return [String] o final da moeda
- def self.efim
- # proposicao DE entre parte inteira e moeda
- emo = @de ? ' DE' : ''
- # moeda singular/plural
- emo += @tt > 1 ? ' ' + @mp : ' ' + @ms if @tt.positive?
- # proposicao E entre moeda e parte fracionaria
- # extenso da parte fracionaria
- emo + (@tt.positive? && @nf.positive? ? ' E ' : '') + ef99
- end
-
- # Produz separador entre grupos 3 digitos
- #
- # @param [Integer] pos posicao actual nos grupos 3 digitos do valor monetario
- # @return [String] separador entre grupos 3 digitos
- def self.esep(pos)
- if pos.positive? && @ai[pos - 1].positive?
- @ai[pos - 1] > 100 ? ' ' : ' E '
- else
- ''
- end
- end
-
- # Produz qualificador grupo de 3 digitos em portugues de portugal ou brasil.
- #
- # @param [Integer] pos posicao actual nos grupos 3 digitos do valor monetario
- # @return [String] qualificador grupo de 3 digitos
- def self.e1e24(pos)
- if @ai[pos].positive?
- @ai[pos] > 1 ? P1E24[@lc][pos] : S1E24[@lc][pos]
- else
- ''
- end
- end
-
- # Produz extenso grupo 3 digitos em portugues de portugal ou brasil.
- #
- # @param [Integer] pos posicao actual nos grupos 3 digitos do valor monetario
- # @return [String] extenso grupo 3 digitos
- def self.edg3(pos)
- dg3 = if pos == 1 && @ai[pos] == 1
- # caso especial MIL EUROS
- ''
- else
- e900(@ai[pos]) + e90(@ai[pos]) + e9(@ai[pos] % 100)
- end
- # qualificador grupo de 3 digitos
- dg3 + e1e24(pos)
- end
-
- # Parametrizar controle singular/plural & proposicoes
- #
- # @return [void]
- def self.pcontrolo
- # soma grupos 1,2 (primeiros 6 digitos)
- @s6 = @ai[0].to_i + @ai[1].to_i * 2
- # soma grupos 3.. (digitos acima de 6)
- @m6 = @ai[2..-1].to_a.inject(:+).to_i * 2
- @tt = @s6 + @m6 # proposicao E & singular/plural
- @de = @s6.zero? && @m6.positive? # proposicao DE
- end
-
- # Produz o extenso dum valor monetario em portugues de portugal ou brasil.
- #
- # @param [Integer] pos posicao actual nos grupos 3 digitos do valor monetario
- # @param [String] ext extenso em construcao
- # @return [String] o extenso dum valor monetario
- def self.enumerico(pos = 0, ext = '')
- # testa fim do valor monetario
- if pos >= @ai.count
- # parametrizar controle singular/plural & proposicoes
- pcontrolo
-
- # caso especial zero
- (@tt + @nf).zero? ? 'ZERO ' + @mp : ext + efim
- else
- # tratamento do proximo grupo 3 digitos
- enumerico(pos + 1, edg3(pos) + esep(pos) + ext)
- end
- end
-
- # Parametrizar parte inteira/fracionaria do valor monetario
- #
- # @param [String] dig string de digitos rdo valor monetario
- # @return [void]
- def self.pintfra(dig)
- # parte inteira do valor monetario => array grupos 3 digitos
- # ex: 123022.12 => [22, 123]
- @ai = dig[/^\d+/].to_s.reverse.scan(/\d{1,3}/).map { |i| i.reverse.to_i }
-
- # parte fracionaria do valor monetario
- # ex: 123022.12 => 12
- # arredondada a 2 casas decimais (centimos/centavos)
- @nf = (dig[/\.\d*/].to_f * 100).round
- end
-
- # Converte um objeto criando extenso(s) em portugues de portugal ou brasil.
- #
- # @param [Object] objeto objeto a converter
- # (String, Float, Integer, Array, Range, Hash)
- # @return [String, Array<extensos>, Hash<extensos>] string extenso
- # se objecto for (String, Float, Integer),
- # array se objecto for (Array, Range),
- # hash se objecto for (Hash)
- def self.eobjeto(obj)
- if obj.is_a?(Hash)
- # converte os valores do Hash nos seus extensos - devolve um Hash
- obj.map { |k, v| [k, eobjeto(v)] }.to_h
- elsif obj.respond_to?(:to_a)
- # converte o objecto num Array com os extensos dos valores
- obj.to_a.map { |v| eobjeto(v) }
- else
- # converte objeto numa string de digitos
- # usa bigdecimal/util para evitar aritmetica binaria
- # (tem problemas com valores >1e12)
- # qualquer valor nao convertivel (ex: texto) resulta em "0.0"
- sdigitos = obj.to_d.to_s('F')
-
- # parametrizar parte inteira/fracionaria (@ai, @nf) do valor monetario
- pintfra(sdigitos)
-
- # processar extenso - valores superiores a 1e24 nao sao tratados
- sdigitos[/^\d+/].length <= 24 ? enumerico : ''
- end
- end
-
- # Parametrizar moeda inferindo singular a partir do plural
- #
# @param [Hash] moeda as opcoes para parametrizar a moeda/fracao
# @option moeda [Symbol] :lc locale do extenso -
# portugues de portugal (:pt) ou brasil (:br)
- # @option moeda [String] :msingular moeda no singular -
- # inferido do plural menos "S"
- # @option moeda [String] :fsingular fracao no singular -
- # inferido do plural menos "S"
- # @option moeda [String] :mplural moeda no plural
- # @option moeda [String] :fplural fracao no plural
- # @return [void]
- def self.psingular(moeda)
- @ms = moeda[:msingular] ||
- (moeda[:mplural].to_s[-1] == 'S' ? moeda[:mplural][0..-2] : 'EURO')
- @cs = moeda[:fsingular] ||
- (moeda[:fplural].to_s[-1] == 'S' ? moeda[:fplural][0..-2] : 'CÊNTIMO')
- end
-
- # Parametrizar moeda inferindo plural a partir do singular
- #
- # @param [Hash] moeda as opcoes para parametrizar a moeda/fracao
- # @option moeda [Symbol] :lc locale do extenso -
- # portugues de portugal (:pt) ou brasil (:br)
# @option moeda [String] :msingular moeda no singular
# @option moeda [String] :fsingular fracao no singular
- # @option moeda [String] :mplural moeda no plural -
- # inferido do singular mais "S"
- # @option moeda [String] :fplural fracao no plural -
- # inferido do singular mais "S"
- # @return [void]
- def self.pplural(moeda)
- # somente [:pt, :br]
- @lc = LC.include?(moeda[:lc]) ? moeda[:lc] : :pt
-
- @mp = moeda[:mplural] || @ms + 'S'
- @cp = moeda[:fplural] || @cs + 'S'
- end
-
- # Produz extenso(s) de objeto(s) em portugues de portugal ou brasil.
- #
- # @param [Hash] moeda as opcoes para parametrizar a moeda/fracao
- # @option moeda [Symbol] :lc locale do extenso -
- # portugues de portugal (:pt) ou brasil (:br)
- # @option moeda [String] :msingular moeda no singular
- # @option moeda [String] :fsingular fracao no singular
# @option moeda [String] :mplural moeda no plural
# @option moeda [String] :fplural fracao no plural
- # @return [String, Array<extensos>, Hash<extensos>] string extenso
+ # @return [String, Array, Hash] string extenso
# se objecto for (String, Float, Integer),
- # array se objecto for (Array, Range),
- # hash se objecto for (Hash)
+ # array<extensos> se objecto for (Array, Range),
+ # hash<extensos> se objecto for (Hash)
def extenso(moeda = { lc: :pt, msingular: 'EURO', fsingular: 'CÊNTIMO' })
# parametrizacao por defeito para :br
if moeda[:lc] == :br && !moeda[:msingular] && !moeda[:mplural]
moeda.merge!(msingular: 'REAL', mplural: 'REAIS', fsingular: 'CENTAVO')
end
# parametrizar moeda
ExtensoPt.psingular(moeda)
ExtensoPt.pplural(moeda)
- # extenso do objeto
- ExtensoPt.eobjeto(self)
+ # cria extenso(s) em portugues de portugal ou brasil
+ ExtensoPt.o2e(self)
end
end
# permite obter um Hash com valores numericos convertidos nos seus extensos
class Hash
@@ -282,19 +50,70 @@
# permite obter um Array com valores numericos convertidos nos seus extensos
class Range
include ExtensoPt
end
-# permite obter o extenso dum Float
+# permite obter o extenso de Float
class Float
include ExtensoPt
end
-# permite obter o extenso dum Integer
+# permite obter o extenso ou numeral romano de Integer
class Integer
include ExtensoPt
+
+ # Produz numeracao romana a partir do inteiro
+ #
+ # @return [String] numeracao romana
+ def romana
+ return "-#{i2r(-self)}" if negative?
+
+ i2r(self)
+ end
+
+ # Recursivamente produz numeral romano
+ #
+ # @param [Integer] numero a converter em numeral romano
+ # @return [String] numeral romano
+ def i2r(numero)
+ return '' if numero.zero?
+
+ ROMAN.each { |r, v| return r.to_s + i2r(numero - v) if v <= numero }
+ end
end
-# permite obter o extenso duma String de digitos
+# permite obter o extenso duma string de digitos ou
+# inteiro duma string numeral romano
class String
include ExtensoPt
+
+ # Testa se string contem numeracao romana
+ #
+ # @return [true, false] sim ou nao numeracao romana
+ def romana?
+ RO_RE.match?(upcase)
+ end
+
+ # Produz inteiro a partir da numeracao romana
+ # ou numeracao romana a partir de string digitos
+ #
+ # @return [Integer, String] inteiro ou numeracao romana
+ def romana
+ return -self[/[^-]+/].romana if /-+/.match?(self)
+ return self[/^\d+/].to_i.romana if /^\d+/.match?(self)
+ return 0 unless romana?
+
+ r2i(upcase, 0)
+ end
+
+ # Recursivamente produz inteiro
+ #
+ # @param [String] numeral romano em convercao
+ # @param [Integer] ultimo numeral convertido
+ # @return [Integer] inteiro do numeral romano
+ def r2i(numeral, ultimo)
+ return 0 if numeral.empty?
+
+ v = ROMAN[numeral[-1].to_sym]
+ v < ultimo ? (r2i(numeral.chop, v) - v) : (r2i(numeral.chop, v) + v)
+ end
end