Aller au contenu

Module:Chiffres romains

Cette page fait l’objet d’une mesure de semi-protection étendue.
Une page de Wikipédia, l'encyclopédie libre.
Ceci est une version archivée de cette page, en date du 9 décembre 2017 à 19:27 et modifiée en dernier par Od1n (discuter | contributions) (tests superflus ; erreur en amont si input vide, et résultat toujours très bien dans un nowrap). Elle peut contenir des erreurs, des inexactitudes ou des contenus vandalisés non présents dans la version actuelle.

 Documentation[voir] [modifier] [historique] [purger]

Ce module est utilisé par (non exhaustif) :

Voir aussi :

local p = {}
local Outils = require( 'Module:Outils' )
local trim = Outils.trim
local typeromains = "[IVXLCDM]+"
local typearabes = "[0-9]+"
local niveau_par_chiffre = {
	['I'] = {1, false},
	['IV'] = {4, false},
	['V'] = {5, false},
	['IX'] = {9, false},
	['X'] = {10, false},
	['XL'] = {40, false},
	['L'] = {50, false},
	['XC'] = {90, false},
	['C'] = {100, false},
	['CD'] = {400, false},
	['D'] = {500, false},
	['CM'] = {900, false},
	['M'] = {1000, false}
}

-- Restitution de la valeur d'une suite de chiffres romains identiques : XXX..., III..
-- On récupère le chiffre romain composant cette séquence, le nombre d'occurrence et la chaîne de départ tronquée de la séquence traitée
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 (depart, romains, chaine)
	local nb = 0
	local resultat = 0
	local test = true

	romains, nb = string.gsub(romains, chaine, '')
	if nb > 1 then
		test = false
		message = Outils.erreur('Nombre romain incorrect, répétition de séquence incohérente ('..nb..' '..chaine..')')
	else
		if nb == 1 then
			resultat = niveau_par_chiffre[chaine][1]
		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
function p.conversion (romains)
	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 (depart, romains, 'IX')
		if not test then return test, message, resultat end
	else
		if string.match(romains, 'IV') then
			test, message, resultat, romains = p.uniques (depart, romains, 'IV')
			if not test then return test, message, resultat end
			niveau_par_chiffre['V'][2] = true
		end
	end

-- B  Dizaines : XL et XC
	if string.match(romains, 'XL') then
		test, message, valeur, romains = p.uniques (depart, romains, 'XL')
		if not test then return test, message, resultat end
		niveau_par_chiffre['L'][2] = true
	else
		if string.match(romains, 'XC') then
			test, message, valeur, romains = p.uniques (depart, 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 (depart, romains, 'CD')
		if not test then return test, message, resultat end
		niveau_par_chiffre['D'][2] = true
	else
		if string.match(romains, 'CM') then
			test, message, valeur, romains = p.uniques (depart, 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 not(romains=='') and test do
		romains, rang, valeur = p.niveau (romains)
		if not string.match(rang, typeromains) then
			test = false
			message = Outils.erreur('Chiffre romain incorrect ('..rang..')')
		else
			if ((valeur > 4) and not (rang == 'M'))
			or niveau_par_chiffre[rang][2]
			or (((rang == 'V') or (rang == 'L') or (rang == 'D')) and (valeur > 1)) then
				test = false
				message = Outils.erreur('Nombre romain incorrect, répétiion de chiffre ('..rang..')')
			else
				if niveau_par_chiffre[rang][1] > niveau then
					test = false
					message = Outils.erreur('Nombre romain incorrect, séquence incohérente ('..depart..')')
				else
					niveau_par_chiffre[rang][2] = true
					niveau = niveau_par_chiffre[rang][1]
					resultat = resultat + valeur*niveau
				end
			end
		end
	end
	return test, message, resultat
end

-- A partir d'un nombre romain, fournit une chaine de caractères composée de ce nombre son infobulle donnant sa valeur en chiffres arabes
function p.RomainsInfobulle(frame)
	local args = Outils.extractArgs(frame)
	if args[1] then
		test, message, nombre = p.conversion(args[1])
		if not test then
			resultat = message
		else
			resultat = '<abbr class="abbr" title="'..nombre..'" ><span class="romain" style="text-transform:uppercase">'..args[1]..'</span></abbr>'
		end
	end
	return resultat
end

-- Décompose la chaîne transmise en nom/chiffres romains/complément
function p.chaine(chaine)
	local nom = ''
	local quantieme = ''
	local complement = ''
	local presence_romains = false

	chaine = ' ' .. chaine .. ' '

	if chaine:match(' ' .. typeromains .. ' ') then
		presence_romains = true
		position = chaine:find(' ' .. typeromains .. ' ')
		quantieme = chaine:match(' ' .. typeromains .. ' ')
		nom = chaine:sub(1, position - 1)
		if (position + #quantieme) < #chaine then
			complement = trim(chaine:sub(position + #quantieme))
		end
		nom = trim(nom)
		quantieme = trim(quantieme)
	else
		nom = trim(chaine)
	end
	return presence_romains, nom, quantieme, complement
end

function p.NobrRomains(frame)
	local chaine = frame.args[1]

	-- Si le paramètre passé est vide, retour
	if #chaine == 0 then
		return Outils.erreur('Aucun paramètre')
	end

	local presence_romains, nom, quantieme, complement = p.chaine(chaine)
	local parts = {}

	if nom and nom ~= '' then
		parts[#parts + 1] = nom
	end

	if presence_romains then
		parts[#parts + 1] = p.RomainsInfobulle(quantieme)
	end

	if complement and complement ~= '' then
		parts[#parts + 1] = trim(complement)
	end

	local texte = table.concat(parts, ' ')

	return '<span class="nowrap">' .. texte .. '</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 >= 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

function p.ChiffresRomains(frame)
	local args = frame:getParent().args
	if args[1] then
		if string.match(args[1], typearabes) == args[1] then
			return p._ChiffresRomains(tonumber(args[1]))
		end
	end
end

return p