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 } }