Module:Chiffres romains
Aller à la navigation
Aller à la recherche
La documentation pour ce module peut être créée à Module:Chiffres romains/doc
-- luacheck: globals mw, no max line length local p = {} local typeromains = "[IVXLCDM]+" local typearabes = "[0-9]+" local niveau_par_chiffre = { ['I'] = 1, ['IV'] = 4, ['V'] = 5, ['IX'] = 9, ['X'] = 10, ['XL'] = 40, ['L'] = 50, ['XC'] = 90, ['C'] = 100, ['CD'] = 400, ['D'] = 500, ['CM'] = 900, ['M'] = 1000, } -- Restitution de la valeur d'une suite de chiffres romains identiques : XXX..., III.. -- On récupère la chaîne de départ tronquée de la séquence traitée, le chiffre romain composant cette séquence et le nombre d'occurrences function p._niveau(romains) local longueur_chaine = string.len(romains) local valeur = 0 local rang = string.sub(romains, 1, 1) local caractere = rang while (valeur < longueur_chaine) and (caractere == rang) do valeur = valeur + 1 romains = string.sub(romains, 2) caractere = string.sub(romains, 1, 1) end return romains, rang, valeur end -- Fonction destinée à gérer les séquences particulières (IV, IX, XL, XC, CD, CM), avec test de cohérence function p._uniques(romains, chaine) local nb = 0 local resultat = 0 local test = true local message = '' romains, nb = string.gsub(romains, chaine, '') if nb > 1 then test = false message = '<span class="error">Nombre romain incorrect, répétition de séquence incohérente ('..nb..' '..chaine..')</span>' else if nb == 1 then resultat = niveau_par_chiffre[chaine] end end return test, message, resultat, romains end -- Conversion d'un nombre romain et entier du système décimal, avec tests de cohérence -- (fonction aussi utilisée dans [[Module:Nom dynastique]]) function p.conversion(romains) local rangs_atteints = {} for k, _ in pairs(niveau_par_chiffre) do rangs_atteints[k] = false end local test = true local message = '' local resultat, valeur = 0, 0 local rang = '' local depart = romains local niveau = 10000 -- Cas des valeurs obtenues par soustraction du chiffre placé à gauche du chiffre significatif, séquences qui doivent être uniques -- A/ Unités : IV et IX if string.match(romains, 'IX') then test, message, resultat, romains = p._uniques(romains, 'IX') if not test then return test, message, resultat end else if string.match(romains, 'IV') then test, message, resultat, romains = p._uniques(romains, 'IV') if not test then return test, message, resultat end rangs_atteints['V'] = true end end -- B Dizaines : XL et XC if string.match(romains, 'XL') then test, message, valeur, romains = p._uniques(romains, 'XL') if not test then return test, message, resultat end rangs_atteints['L'] = true else if string.match(romains, 'XC') then test, message, valeur, romains = p._uniques(romains, 'XC') if not test then return test, message, resultat end end end resultat = resultat + valeur -- C/ centaines : CD et CM valeur = 0 if string.match(romains, 'CD') then test, message, valeur, romains = p._uniques(romains, 'CD') if not test then return test, message, resultat end rangs_atteints['D'] = true else if string.match(romains, 'CM') then test, message, valeur, romains = p._uniques(romains, 'CM') if not test then return test, message, resultat end end end resultat = resultat + valeur -- Une fois les cas particuliers traités, la chaine ne contient plus que des "séquences" de chiffres identiques -- Ces séquences sont limitées à 4 occurrences (écriture simplifiée), sauf pour les milliers -- Contrôle de l'unicité de présence des chiffres V, L et D -- Contrôle de cohérence (on ne peut pas avoir une séquence du chiffre n si une séquence de chiffres d'un niveau inférieur à n a déjà été traitée) valeur = 0 test = true while romains ~= '' do romains, rang, valeur = p._niveau(romains) if not string.match(rang, typeromains) then test = false message = '<span class="error">Chiffre romain incorrect ('..rang..')</span>' break end if ((valeur > 4) and not (rang == 'M')) or rangs_atteints[rang] or (((rang == 'V') or (rang == 'L') or (rang == 'D')) and (valeur > 1)) then test = false message = '<span class="error">Nombre romain incorrect, répétition de chiffre ('..rang..')</span>' break end if niveau_par_chiffre[rang] > niveau then test = false message = '<span class="error">Nombre romain incorrect, séquence incohérente ('..depart..')</span>' break end rangs_atteints[rang] = true niveau = niveau_par_chiffre[rang] resultat = resultat + valeur*niveau end return test, message, resultat end -- À partir d'un nombre romain, fournit une chaine de caractères composée de ce nombre avec une infobulle donnant sa valeur en chiffres arabes function p._RomainsInfobulle(romains) local test, message, nombre = p.conversion(romains) if not test then return message else return '<abbr class="abbr" title="' .. nombre .. '"><span class="romain" style="text-transform:uppercase">' .. romains .. '</span></abbr>' end end -- Décompose la chaîne transmise en nom/chiffres romains/complément function p._chaine(chaine) local haystack = ' ' .. chaine .. ' ' local offsetStart, offsetEnd, capture = mw.ustring.find(haystack, '%W(' .. typeromains .. ')%W') if offsetStart then return mw.ustring.sub(haystack, 2, offsetStart) .. p._RomainsInfobulle(capture) .. mw.ustring.sub(haystack, offsetEnd, -2) else return chaine end end -- Fonction utilisée par le modèle {{nobr romains}} function p.NobrRomains(frame) local chaine = frame.args[1] -- Si le paramètre passé est vide, retour if chaine == '' then return '<span class="error">Aucun paramètre</span>' end return '<span class="nowrap">' .. p._chaine(chaine) .. '</span>' end -- Convertit le nombre passé en paramètre en chiffres romains function p._ChiffresRomains(chiffre) local u = { "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" } local d = { "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" } local c = { "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM" } local m = { "", "M", "MM", "MMM", "MMMM" } local ret = "" if (chiffre < 0) then ret = "-" chiffre = -chiffre end if (chiffre >= 5000) then return nil end if (chiffre >= 1000) then local mil = math.floor(chiffre / 1000) ret = ret .. m[mil + 1] chiffre = chiffre % 1000 end if (chiffre >= 100) then local cen = math.floor (chiffre / 100) ret = ret .. c[cen + 1] chiffre = chiffre % 100 end if (chiffre >= 10) then local diz = math.floor (chiffre / 10) ret = ret .. d[diz + 1] chiffre = chiffre % 10 end return ret .. u[chiffre + 1] end -- notes : -- * cette fonction est actuellement inutilisée -- * il existe un modèle au rôle similaire : [[Modèle:Nombre en romain]] function p.ChiffresRomains(frame) local args = frame:getParent().args if args[1] then if args[1]:match('^%-?' .. typearabes .. '$') then return p._ChiffresRomains(tonumber(args[1])) end end end return p