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:30 et modifiée en dernier par Od1n (discuter | contributions) (tests et trim superflus, normalisé dans le p.chaine() en amont). 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 ~= '' then
		parts[#parts + 1] = nom
	end

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

	if complement ~= '' then
		parts[#parts + 1] = 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