Módulo:Roman

Origem: Wikipédia, a enciclopédia livre.
Documentação do módulo[ver] [editar] [histórico] [purgar]

Este módulo implementa a predefinição {{Roman}}. Para a documentação comportamental, ver a página da predefinição. Para casos de teste, ver Predefinição:Roman/Exemplos para testes.

  • O módulo suporta 0 como um numeral romano, exibido como "N".
  • Antes de 25 de abril de 2016, era usado para exibir 69105 como LXVMMMMCV. Com a adição de IX e IV sendo 9000 e 4000 respectivamente, agora exibimos 69105 como LXIXCV.
  • O módulo lida com expressões decimais, fracionárias e aritméticas com uma precisão de 1/1728. Usa #expr: na linha 126.

Lidando com casos complicados (como 0,00001 e 99,99999)[editar código-fonte]

  1. Encontre os algarismos romanos para a parte inteira do número.
  2. Se o número não for um inteiro:
    • Adicione metade da menor unidade (1/1728) para simular arredondamento em vez de truncamento.
    • Certifique-se de que este novo resultado esteja entre 1/1728 e 1727/1728. (na verdade 1,1/1728 e 1727,1/1728 devido a problemas de arredondamento de ponto flutuante)
  3. Portanto, é garantido que 0,00001 tenha pelo menos o menor símbolo de unidade (em vez de estar em branco ou 0) e 99,99999 não é exibido como 100 ou 99 e 2 metades.

Validação[editar código-fonte]

-- Este módulo implementa {{Roman}}.
require[[strict]]

local p = {}

-- Esta função implementa, internamente, a predefinição {{Sobrelinhado}}.
local function overline(s)
    return mw.ustring.format( '<span style="text-decoration:overline;">%s</span>', s )
end

-- Obtém os numerais romanos para uma determinada tabela numeral. Retorna tanto
-- a sequência ('string') de numerais e o valor do número depois que ele 
-- terminar de ser processado.
local function getLetters(num, t)
    local ret = {}
    for _, v in ipairs(t) do
        local val, letter = unpack(v)
        while num >= val do
            num = num - val
            table.insert(ret, letter)
        end
    end

    return table.concat(ret), num
end

-- O fluxo de controle principal do módulo.
local function _main(args)
    -- Obtém a entrada e sai sem exibir nada se a entrada estiver vazia.
    if args[1] == nil then return end
    local num = tonumber(args[1])
    if not num or num < 0 or num == math.huge then
    	error('Número inválido ' .. args[1], 2)
    elseif num == 0 then
        return 'N'
    end

    -- Retorna uma mensagem para números muito grandes para serem expressos 
    -- em algarismos romanos.
    if num >= 5000000 then
        return args[2] or 'N/A'
    end

    local ret = ''
    -- Encontra os algarismos romanos para a maior parte dos números.
    -- 23 de abril de 2016 - ajustado para >= 4000 para aceitar o romano "IV" grande
    -- A instrução "if" não é estritamente necessária, mas torna o algoritmo 
    -- mais eficiente para números menores.
    if num >= 4000 then
        local bigRomans = {
            { 1000000, 'M' },
            { 900000, 'CM' }, { 500000, 'D' }, { 400000, 'CD' }, { 100000, 'C' },
            {  90000, 'XC' }, {  50000, 'L' }, {  40000, 'XL' }, {  10000, 'X' },
            {   9000, 'IX' }, {   5000, 'V' }, {   4000, 'IV' },
        }
        local bigLetters
        bigLetters, num = getLetters(num, bigRomans)
        ret = overline(bigLetters)
    end

    -- Encontra os numerais romanos para números menores que o limite romano grande.
    local smallRomans = {
        { 1000, 'M' },
        { 900, 'CM' }, { 500, 'D' }, { 400, 'CD' }, { 100, 'C' },
        {  90, 'XC' }, {  50, 'L' }, {  40, 'XL' }, {  10, 'X' },
        {   9, 'IX' }, {   5, 'V' }, {   4, 'IV' }, {   1, 'I' }
    }
    local smallLetters = getLetters( num, smallRomans )
    ret = ret .. smallLetters

    if args.fraction == 'yes' then
        -- Encontra os numerais romanos para as partes fracionárias dos números.
        -- Se num não for um número inteiro, adiciona a metade de 1/1728 
        -- (a menor unidade) para igualar ao arredondamento.
        -- Certifica-se de que não somos menores que a menor unidade ou maiores 
        -- que 1 - a menor unidade para evitar obter dois símbolos "meio" 
        -- ou nenhum símbolo
        num = num - math.floor(num)
        if num ~= 0 then
            num = math.max(1.1/1728, math.min(1727.1/1728, num + 1/3456))
        end
        local fractionalRomans = {
            { 1/2, 'S' }, { 5/12, "''':'''•''':'''" }, { 1/3, "'''::'''" },
            { 1/4, "''':'''•" }, { 1/6, "''':'''" }, { 1/12, '•' },
            { 1/24, 'Є' }, { 1/36, 'ƧƧ' }, { 1/48, 'Ɔ' }, { 1/72, 'Ƨ' }, { 1/144, '<s>Ƨ</s>' },
            { 1/288, '℈' }, { 1/1728, '»' },
        }
        local fractionalLetters = getLetters(num, fractionalRomans)
        
        ret = ret .. fractionalLetters
    end

    return ret
end

function p.main(frame)
    -- Se chamado via "#invoke", usa os argumentos passados na chamada da
    -- predefinição, ou os argumentos passados para "#invoke" se existirem. 
    -- De outra forma assume que os argumentos estão sendo passados diretamente
    -- do console de depuração ou de outro módulo Lua.
    local origArgs
    if frame == mw.getCurrentFrame() then
        origArgs = frame:getParent().args
        for k, v in pairs(frame.args) do
            origArgs = frame.args
            break
        end
    else
        origArgs = frame
    end
    -- Corta os espaços em branco e remove os argumentos em branco.
    local args = {}
    for k, v in pairs(origArgs) do
        if type( v ) == 'string' then
            v = mw.text.trim(v)
        end
        if v ~= '' then
            args[k] = v
        end
    end
    
    -- Saída se não for dado nada.
    if args == nil or args == {} then return end
    -- Dada a expressão matemática, simplifica para um número.
    if type(args[1]) == 'string' then
        local success, result = pcall(mw.ext.ParserFunctions.expr, args[1])
        if success then
            args[1] = result
        end -- Caso não, passa para a rotina "_main" e tenta deixar a "tonumber"
            -- de Lua lidar com isso.
    end
    return _main(args)
end

return p