using System;
using System.Text.RegularExpressions;
using Developpez.Dotnet.Properties;
using CH = Developpez.Dotnet.CompatibilityHelper;
namespace Developpez.Dotnet
{
///
/// Un nombre en chiffres romains.
///
#if NETFX_CORE
public struct Roman : IComparable, IComparable, IEquatable
#else
public struct Roman : IComparable, IConvertible, IComparable, IEquatable
#endif
{
#region Private fields
///
/// "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"
///
private static readonly string[] _symbols;
///
/// 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1
///
private static readonly int[] _values;
///
/// Expression régulière de validation d'un nombre romain.
///
private static readonly Regex _standardPattern;
///
/// Représente un nombre romain nul.
///
private static readonly int _nullInnerValue;
///
/// Représente le nombre romain le plus petit.
///
private static readonly int _minInnerValue;
///
/// Représente le nombre romain le plus grand.
///
private static readonly int _maxInnerValue;
///
/// La valeur interne du nombre romain.
///
private readonly int _innerValue;
#endregion
#region Public fields
///
/// Représente la valeur nulle
/// (cette valeur n'a aucun équivalent
/// en chiffres romains).
///
public static readonly Roman Null;
///
/// Représente le nombre romain le plus petit.
///
public static readonly Roman MinValue;
///
/// Représente le nombre romain le plus grand.
///
public static readonly Roman MaxValue;
#endregion
#region Constructors
static Roman()
{
_nullInnerValue = 0;
_minInnerValue = 1;
_maxInnerValue = 4999;
_standardPattern = new Regex("^(M{1,4})?(CM|CD|D?C{0,3})?(XC|XL|L?X{0,3})?(IX|IV|V?I{0,3})?$", CH.RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase | RegexOptions.Singleline);
_symbols = new string[13] { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" };
_values = new int[13] { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
Null = new Roman(_nullInnerValue);
MinValue = new Roman(_minInnerValue);
MaxValue = new Roman(_maxInnerValue);
}
///
/// Initialise une nouvelle instance de la structure
/// à partir d'un nombre entier.
///
/// Entier.
public Roman(int value)
{
CheckValue(value);
this._innerValue = value;
}
///
/// Initialise une nouvelle instance de la structure
/// à partir d'une chaîne de caractères.
///
/// Chaîne de caractères.
public Roman(string value)
{
this._innerValue = ToInt(value);
}
#endregion
#region Private static methods
///
/// Vérifie si la valeur donnée respecte les spécifications d'un nombre romain.
///
/// Valeur.
private static void CheckValue(int value)
{
CheckValue(value, true);
}
///
/// Vérifie si la valeur donnée respecte les spécifications d'un nombre romain.
///
/// Valeur.
/// Indique si la méthode doit lever une exception ou pas.
/// Vrai si la valeur donnée respecte les spécifications d'un nombre romain, faux sinon.
private static bool CheckValue(int value, bool raiseException)
{
if (value == _nullInnerValue)
{
return true;
}
if (value < _minInnerValue)
{
if (raiseException)
throw new ArgumentOutOfRangeException("value", string.Format(ExceptionMessages.RomanMinValue, _minInnerValue));
return false;
}
if (value > _maxInnerValue)
{
if (raiseException)
throw new ArgumentOutOfRangeException("value", string.Format(ExceptionMessages.RomanMaxValue, _maxInnerValue));
return false;
}
return true;
}
///
/// Vérifie si la valeur donnée respecte les spécifications d'un nombre romain.
///
/// Valeur.
private static void CheckValue(string value)
{
CheckValue(value, true);
}
///
/// Vérifie si la valeur donnée respecte les spécifications d'un nombre romain.
///
/// Valeur.
/// Indique si la méthode doit lever une exception ou pas.
/// Vrai si la valeur donnée respecte les spécifications d'un nombre romain, faux sinon.
private static bool CheckValue(string value, bool raiseException)
{
if (string.IsNullOrEmpty(value))
{
return true;
//if (raiseException)
// throw new ArgumentNullException("value");
//else
// return false;
}
if (value.Length > 16)// Le chiffre romain le plus long ne dépasse pas 16 caractères : MMMMDCCCLXXXVIII (4888)
{
if (raiseException)
throw new ArgumentOutOfRangeException("value", string.Format(ExceptionMessages.RomanMaxValue, _maxInnerValue));
return false;
}
if (!_standardPattern.Match(value).Success)
{
if (raiseException)
throw new FormatException(ExceptionMessages.BadInputStringFormat);
return false;
}
return true;
}
///
/// Retourne un entier à partir d'une chaîne de caractères donnée représentant un nombre romain.
///
/// Valeur.
/// L'entier correspondant à la chaîne de caractères.
private static int ToInt(string value)
{
CheckValue(value);
if (string.IsNullOrEmpty(value))
return _nullInnerValue;
int index = 0;
int result = 0;
for (int i = 0; i < _symbols.Length; i++)
{
while (value.Substring(index).StartsWith(_symbols[i], StringComparison.CurrentCultureIgnoreCase))
{
result += _values[i];
index += _symbols[i].Length;
}
}
if (index == value.Length)
return result;
throw new FormatException(ExceptionMessages.BadInputStringFormat);
}
///
/// Retourne une chaîne de caractères représentant un nombre romain à partir d'un entier donné.
///
/// Valeur.
/// La chaîne de caractères représentant un nombre romain.
private static string ToString(int value)
{
CheckValue(value);
if (value == _nullInnerValue)
return string.Empty;
string result = string.Empty;
int remainder = value;
for (int i = 0; i < _values.Length; i++)
{
while (remainder >= _values[i])
{
result += _symbols[i];
remainder -= _values[i];
}
}
return result;
}
#endregion
#region Public static methods
///
/// Convertit la chaîne de caractères représentant un nombre romain en nombre romain.
///
/// Chaîne de caractères représentant un nombre romain.
/// Le nombre romain correspondant à la chaîne de caractères.
public static Roman Parse(string s)
{
return new Roman(s);
}
///
/// Convertit l'entier représentant un nombre romain en nombre romain.
///
/// Entier représentant un nombre romain.
/// Le nombre romain correspondant à l'entier.
public static Roman Parse(int i)
{
return new Roman(i);
}
///
/// Indique si la chaîne de caractères représente un nombre romain.
/// Une valeur de retour renvoie ce nombre romain.
///
/// Chaîne de caractères représentant un nombre romain.
/// Le nombre romain correspondant à la chaîne de caractères.
/// Vrai si la chaîne de caractères représente un nombre romain, faux sinon.
public static bool TryParse(string s, out Roman result)
{
if (CheckValue(s, false))
{
result = new Roman(s);
return true;
}
result = Null;
return false;
}
///
/// Indique si l'entier représente un nombre romain.
/// Une valeur de retour renvoie ce nombre romain.
///
/// Entier représentant un nombre romain.
/// Le nombre romain correspondant à l'entier.
/// Vrai si l'entier représente un nombre romain, faux sinon.
public static bool TryParse(int i, out Roman result)
{
if (CheckValue(i, false))
{
result = new Roman(i);
return true;
}
result = Null;
return false;
}
#endregion
#region Public instance methods
///
/// Retourne l'entier correspondant à l'instance de la structure .
///
/// L'entier correspondant à l'instance de la structure .
public int ToInt()
{
return this._innerValue;
}
#region Object Members
///
/// Retourne le code de hachage de l'instance actuelle.
///
/// le code de hachage de l'instance actuelle
public override int GetHashCode()
{
return this.ToInt().GetHashCode();
}
///
/// Détermine si l'objet spécifié a la même valeur que l'instance actuelle.
///
/// objet à comparer
/// true si l'objet spécifié est égal à l'instance actuelle, false sinon
public override bool Equals(object obj)
{
return this.ToInt().Equals(((Roman)obj).ToInt());
}
///
/// Retourne une représentation textuelle de l'instance actuelle
///
/// une représentation textuelle de l'instance actuelle
public override string ToString()
{
return ToString(this.ToInt());
}
#endregion
#region IComparable Members
///
/// Compare cette instance à l'objet spécifié et retourne une indication de leurs valeurs relatives
///
/// objet à comparer
/// Nombre signé indiquant les valeurs relatives de cette instance et value
///
///
/// Valeur de retour
/// Description
///
/// -
/// Inférieure à zéro
/// Cette instance est inférieure à value.
///
/// -
/// Zéro
/// Cette instance est égale à value.
///
/// -
/// Supérieure à zéro
/// Cette instance est supérieure à value.
///
///
///
public int CompareTo(object value)
{
return this.ToInt().CompareTo(((Roman)value).ToInt());
}
#endregion
#if !NETFX_CORE
#region IConvertible Members
///
/// Retourne le TypeCode du type Roman.
///
/// le TypeCode du type Roman
public TypeCode GetTypeCode()
{
return TypeCode.Int32;// Correct ?
}
///
/// Pour obtenir une description de ce membre, consultez
///
/// Implémentation de l'interface IFormatProvider qui fournit des informations de mise en forme propres à la culture
/// true si la valeur de ce Roman est différente de 0, false sinon
bool IConvertible.ToBoolean(IFormatProvider provider)
{
return Convert.ToBoolean(this.ToInt(), provider);
}
///
/// Pour obtenir une description de ce membre, consultez
///
/// Implémentation de l'interface IFormatProvider qui fournit des informations de mise en forme propres à la culture
/// Valeur de l'instance actuelle, convertie en Byte
byte IConvertible.ToByte(IFormatProvider provider)
{
return Convert.ToByte(this.ToInt(), provider);
}
///
/// Pour obtenir une description de ce membre, consultez
///
/// Implémentation de l'interface IFormatProvider qui fournit des informations de mise en forme propres à la culture
/// Valeur de l'instance actuelle, convertie en Char
char IConvertible.ToChar(IFormatProvider provider)
{
return Convert.ToChar(this.ToInt(), provider);
}
///
/// Pour obtenir une description de ce membre, consultez
///
/// Implémentation de l'interface IFormatProvider qui fournit des informations de mise en forme propres à la culture
/// Valeur de l'instance actuelle, convertie en DateTime
DateTime IConvertible.ToDateTime(IFormatProvider provider)
{
throw new InvalidCastException(string.Format(ExceptionMessages.InvalidCastFromTo, typeof(Roman).FullName, typeof(DateTime).FullName));
}
///
/// Pour obtenir une description de ce membre, consultez
///
/// Implémentation de l'interface IFormatProvider qui fournit des informations de mise en forme propres à la culture
/// Valeur de l'instance actuelle, convertie en Decimal
decimal IConvertible.ToDecimal(IFormatProvider provider)
{
return Convert.ToDecimal(this.ToInt());
}
///
/// Pour obtenir une description de ce membre, consultez
///
/// Implémentation de l'interface IFormatProvider qui fournit des informations de mise en forme propres à la culture
/// Valeur de l'instance actuelle, convertie en Double
double IConvertible.ToDouble(IFormatProvider provider)
{
return Convert.ToDouble(this.ToInt());
}
///
/// Pour obtenir une description de ce membre, consultez
///
/// Implémentation de l'interface IFormatProvider qui fournit des informations de mise en forme propres à la culture
/// Valeur de l'instance actuelle, convertie en Int16
short IConvertible.ToInt16(IFormatProvider provider)
{
return Convert.ToInt16(this.ToInt());
}
///
/// Pour obtenir une description de ce membre, consultez
///
/// Implémentation de l'interface IFormatProvider qui fournit des informations de mise en forme propres à la culture
/// Valeur de l'instance actuelle, convertie en Int32
int IConvertible.ToInt32(IFormatProvider provider)
{
return this.ToInt();
}
///
/// Pour obtenir une description de ce membre, consultez
///
/// Implémentation de l'interface IFormatProvider qui fournit des informations de mise en forme propres à la culture
/// Valeur de l'instance actuelle, convertie en Int64
long IConvertible.ToInt64(IFormatProvider provider)
{
return Convert.ToInt64(this.ToInt());
}
///
/// Pour obtenir une description de ce membre, consultez
///
/// Implémentation de l'interface IFormatProvider qui fournit des informations de mise en forme propres à la culture
/// Valeur de l'instance actuelle, convertie en SByte
sbyte IConvertible.ToSByte(IFormatProvider provider)
{
return Convert.ToSByte(this.ToInt());
}
///
/// Pour obtenir une description de ce membre, consultez
///
/// Implémentation de l'interface IFormatProvider qui fournit des informations de mise en forme propres à la culture
/// Valeur de l'instance actuelle, convertie en Single
float IConvertible.ToSingle(IFormatProvider provider)
{
return Convert.ToSingle(this.ToInt());
}
///
/// Pour obtenir une description de ce membre, consultez
///
/// Implémentation de l'interface IFormatProvider qui fournit des informations de mise en forme propres à la culture
/// Valeur de l'instance actuelle, convertie en String
string IConvertible.ToString(IFormatProvider provider)
{
return Convert.ToString(this.ToString(), provider);
}
///
/// Pour obtenir une description de ce membre, consultez
///
/// Type dans lequel convertir cette valeur Roman.
/// Implémentation de l'interface IFormatProvider qui fournit des informations de mise en forme propres à la culture
/// Valeur de l'instance actuelle, convertie vers le type spécifié
object IConvertible.ToType(Type conversionType, IFormatProvider provider)
{
return Convert.ChangeType(this.ToInt(), conversionType, provider);
}
///
/// Pour obtenir une description de ce membre, consultez
///
/// Implémentation de l'interface IFormatProvider qui fournit des informations de mise en forme propres à la culture
/// Valeur de l'instance actuelle, convertie en UInt16
ushort IConvertible.ToUInt16(IFormatProvider provider)
{
return Convert.ToUInt16(this.ToInt());
}
///
/// Pour obtenir une description de ce membre, consultez
///
/// Implémentation de l'interface IFormatProvider qui fournit des informations de mise en forme propres à la culture
/// Valeur de l'instance actuelle, convertie en UInt32
uint IConvertible.ToUInt32(IFormatProvider provider)
{
return Convert.ToUInt32(this.ToInt());
}
///
/// Pour obtenir une description de ce membre, consultez
///
/// Implémentation de l'interface IFormatProvider qui fournit des informations de mise en forme propres à la culture
/// Valeur de l'instance actuelle, convertie en UInt64
ulong IConvertible.ToUInt64(IFormatProvider provider)
{
return Convert.ToUInt64(this.ToInt());
}
#endregion
#endif
#region IComparable Members
///
/// Compare cette instance au Roman spécifié et retourne une indication de leurs valeurs relatives
///
/// objet à comparer
/// Nombre signé indiquant les valeurs relatives de cette instance et other
///
///
/// Valeur de retour
/// Description
///
/// -
/// Inférieure à zéro
/// Cette instance est inférieure à other.
///
/// -
/// Zéro
/// Cette instance est égale à other.
///
/// -
/// Supérieure à zéro
/// Cette instance est supérieure à other.
///
///
///
public int CompareTo(Roman other)
{
return this.ToInt().CompareTo(other.ToInt());
}
#endregion
#region IEquatable Members
///
/// Retourne une valeur indiquant si cette instance équivaut à une valeur Roman spécifiée.
///
/// Valeur Int32 à comparer à cette instance.
/// true si other possède la même valeur que cette instance ; sinon, false.
public bool Equals(Roman other)
{
return (this.CompareTo(other) == 0);
}
#endregion
#endregion
#region Operators
///
/// Additionne deux nombres romains.
///
/// Nombre romain.
/// Nombre romain.
/// Un nombre romain dont la valeur est la somme de r1 et r2.
public static Roman operator +(Roman r1, Roman r2)
{
return new Roman(r1.ToInt() + r2.ToInt());
}
///
/// Incrémente un nombre romain de un.
///
/// Nombre romain.
/// Le nombre romain incrémenté de un.
public static Roman operator ++(Roman r)
{
return new Roman(r.ToInt() + 1);
}
///
/// Soustrait un nombre romain d'un autre nombre romain
/// pour donner un troisième nombre romain.
///
/// Nombre romain.
/// Nombre romain.
/// Un nombre romain dont la valeur est la différence de r1 par rapport à r2.
public static Roman operator -(Roman r1, Roman r2)
{
return new Roman(r1.ToInt() - r2.ToInt());
}
///
/// Décrémente un nombre romain de un.
///
/// Nombre romain.
/// Le nombre romain décrémenté de un.
public static Roman operator --(Roman r)
{
return new Roman(r.ToInt() - 1);
}
///
/// Retourne le reste d'une division d'un nombre romain par un autre nombre romain.
///
/// Nombre romain.
/// Nombre romain.
/// Un nombre romain dont la valeur est le modulo de r1 par rapport à r2.
public static Roman operator %(Roman r1, Roman r2)
{
return new Roman(r1.ToInt() % r2.ToInt());
}
///
/// Multiplie deux nombres romains
/// pour donner un troisième nombre romain.
///
/// Nombre romain.
/// Nombre romain.
/// Un nombre romain dont la valeur est le produit de r1 et r2.
public static Roman operator *(Roman r1, Roman r2)
{
return new Roman(r1.ToInt() * r2.ToInt());
}
///
/// Divise un nombre romain par un autre nombre romain
/// pour donner un troisième nombre romain (partie entière du quotient).
///
/// Nombre romain.
/// Nombre romain.
/// Un nombre romain dont la valeur est la partie entière du quotient de r1 par rapport à r2.
public static Roman operator /(Roman r1, Roman r2)
{
return new Roman(r1.ToInt() / r2.ToInt());
}
///
/// Indique si les deux nombre romains donnés sont égaux.
///
/// Nombre romain.
/// Nombre romain.
/// Vrai si les deux nombre romains donnés sont égaux, faux sinon.
public static bool operator ==(Roman r1, Roman r2)
{
return (r1.ToInt().CompareTo(r2.ToInt()) == 0);
}
///
/// Indique si r1 est strictement supérieur à r2.
///
/// Nombre romain.
/// Nombre romain.
/// Vrai si r1 est strictement supérieur à r2, faux sinon.
public static bool operator >(Roman r1, Roman r2)
{
return (r1.ToInt().CompareTo(r2.ToInt()) > 0);
}
///
/// Indique si r1 est supérieur ou égal à r2.
///
/// Nombre romain.
/// Nombre romain.
/// Vrai si r1 est supérieur ou égal à r2, faux sinon.
public static bool operator >=(Roman r1, Roman r2)
{
return (r1.ToInt().CompareTo(r2.ToInt()) >= 0);
}
///
/// Indique si r1 est différent de r2.
///
/// Nombre romain.
/// Nombre romain.
/// Vrai si r1 est différent de r2, faux sinon.
public static bool operator !=(Roman r1, Roman r2)
{
return (r1.ToInt().CompareTo(r2.ToInt()) != 0);
}
///
/// Indique si r1 est strictement inférieur à r2.
///
/// Nombre romain.
/// Nombre romain.
/// Vrai si r1 est strictement inférieur à r2, faux sinon.
public static bool operator <(Roman r1, Roman r2)
{
return (r1.ToInt().CompareTo(r2.ToInt()) < 0);
}
///
/// Indique si r1 est inférieur ou égal à r2.
///
/// Nombre romain.
/// Nombre romain.
/// Vrai si r1 est inférieur ou égal à r2, faux sinon.
public static bool operator <=(Roman r1, Roman r2)
{
return (r1.ToInt().CompareTo(r2.ToInt()) <= 0);
}
#endregion
#region Implicit conversions
///
/// Convertit un nombre romain en entier.
///
/// Nombre romain.
/// L'entier correspondant au nombre romain.
public static explicit operator int(Roman r)
{
return r.ToInt();
}
///
/// Convertit un entier en nombre romain.
///
/// Entier.
/// Le nombre romain correspondant à l'entier.
public static explicit operator Roman(int i)
{
return new Roman(i);
}
///
/// Convertit un nombre romain en entier 16 bits.
///
/// Nombre romain.
/// L'entier 16 bits correspondant au nombre romain.
public static explicit operator short(Roman r)
{
return (short)r.ToInt();
}
///
/// Convertit un entier en nombre romain.
///
/// Entier 16 bits.
/// Le nombre romain correspondant à l'entier 16 bits.
public static explicit operator Roman(short i)
{
return new Roman(i);
}
///
/// Convertit un nombre romain en entier 64 bits.
///
/// Nombre romain.
/// L'entier 64 bits correspondant au nombre romain.
public static explicit operator long(Roman r)
{
return r.ToInt();
}
///
/// Convertit un entier 64 bits en nombre romain.
///
/// Entier 64 bits.
/// Le nombre romain correspondant à l'entier 64 bits.
public static explicit operator Roman(long i)
{
return new Roman((int)i);
}
#endregion
}
}