/// <summary> /// Defines an alias. /// The same alias can be registered multiple times but it has to exactly match the previously registered one. /// </summary> /// <param name="abbreviation"> /// The unit of measure abbreviation. /// This is the key that is used. It must not be null or empty. /// </param> /// <param name="name">The full name. Must not be null or empty.</param> /// <param name="definitionFactor"> /// The factor that applies to the <see cref="AliasMeasureUnit.Definition"/>. /// Must not be <see cref="FullFactor.Zero"/>. /// </param> /// <param name="definition">The definition. Can be any <see cref="MeasureUnit"/>.</param> /// <param name="autoStandardPrefix"> /// Whether standard metric and/or binary prefixes can be applied to the unit. /// </param> /// <returns>The alias unit of measure.</returns> public AliasMeasureUnit DefineAlias( string abbreviation, string name, FullFactor definitionFactor, MeasureUnit definition, AutoStandardPrefix autoStandardPrefix = AutoStandardPrefix.None) { if (!IsValidNewAbbreviation(abbreviation, autoStandardPrefix)) { throw new ArgumentException($"Invalid abbreviation {abbreviation}.", nameof(abbreviation)); } if (String.IsNullOrWhiteSpace(name)) { throw new ArgumentException("Must not be null or white space.", nameof(name)); } if (definitionFactor.IsZero) { throw new ArgumentException("Must not be zero.", nameof(definitionFactor)); } if (definition == null) { throw new ArgumentNullException(nameof(definition)); } if (definition != MeasureUnit.None && definition.Context != this) { throw new Exception("Units' Context mismatch."); } return(RegisterAlias(abbreviation, name, definitionFactor, definition, autoStandardPrefix)); }
/// <summary> /// Define a new fundamental unit of measure. /// Just like <see cref="DefineAlias"/>, the same fundamental unit can be redefined multiple times /// as long as it is actually the same: for fundamental units, the <see cref="Name"/> /// (and the normalizedPrefix if any) must be exaclty the same. /// </summary> /// <param name="abbreviation"> /// The unit of measure abbreviation. /// This is the key that is used. It must not be null or empty. /// </param> /// <param name="name">The full name. Must not be null or empty.</param> /// <param name="autoStandardPrefix"> /// Whether standard metric and/or binary prefixes can be applied to the unit. /// If a <paramref name="normalizedPrefix"/> is used, this must necessarily define the /// corresponding prefix kind. /// </param> /// <param name="normalizedPrefix"> /// Optional prefix to be used for units where the normalized unit should not be the <see cref="FundamentalMeasureUnit"/> but one of its /// <see cref="PrefixedMeasureUnit"/>. This is the case for the "g"/"Gram" and the "kg"/"Kilogram". /// Defaults to <see cref="MeasureStandardPrefix.None"/>: by default a fundamental unit is the normalized one. /// </param> /// <returns>The fundamental unit of measure.</returns> public FundamentalMeasureUnit DefineFundamental( string abbreviation, string name, AutoStandardPrefix autoStandardPrefix = AutoStandardPrefix.None, MeasureStandardPrefix normalizedPrefix = null) { if (!IsValidNewAbbreviation(abbreviation, autoStandardPrefix)) { throw new ArgumentException($"Invalid abbreviation {abbreviation}.", nameof(abbreviation)); } if (String.IsNullOrWhiteSpace(name)) { throw new ArgumentException("Must not be null or white space.", nameof(name)); } if (normalizedPrefix == null || normalizedPrefix == MeasureStandardPrefix.None) { return(RegisterFundamental(abbreviation, name, autoStandardPrefix, true)); } if (normalizedPrefix.Base == 2 && (autoStandardPrefix & AutoStandardPrefix.Binary) == 0) { throw new ArgumentException($"Since the normalization is {normalizedPrefix.Name}, {abbreviation} unit must support AutoStandardPrefix.Binary.", nameof(autoStandardPrefix)); } if (normalizedPrefix.Base == 10 && (autoStandardPrefix & AutoStandardPrefix.Metric) == 0) { throw new ArgumentException($"Since the normalization is {normalizedPrefix.Name}, {abbreviation} unit must support AutoStandardPrefix.Metric.", nameof(autoStandardPrefix)); } var f = RegisterFundamental(abbreviation, name, autoStandardPrefix, false); f.SetPrefixedNormalization(RegisterPrefixed(ExpFactor.Neutral, normalizedPrefix, f, true)); return(f); }
void CheckArgumentAutoStdPrefix(AtomicMeasureUnit m, AutoStandardPrefix stdPrefix) { if (m.AutoStandardPrefix != stdPrefix) { ThrowArgumentException(m, $"new AutoStandardPrefix '{stdPrefix}' differ from '{m.AutoStandardPrefix}'."); } }
void CheckArgumentAbbreviation(string abbreviation, AutoStandardPrefix autoStandardPrefix) { var conflict = CheckValidNewAbbreviation(abbreviation, autoStandardPrefix); if (conflict != NewAbbreviationConflict.None && conflict != NewAbbreviationConflict.Exists) { throw new ArgumentException($"Invalid abbreviation {abbreviation} ({conflict}).", nameof(abbreviation)); } }
private protected AtomicMeasureUnit( MeasureContext ctx, string abbreviation, string name, AutoStandardPrefix stdPrefix, bool isNormalized) : base(ctx, abbreviation, name, isNormalized) { AutoStandardPrefix = stdPrefix; }
internal AliasMeasureUnit( MeasureContext ctx, string abbreviation, string name, FullFactor definitionFactor, MeasureUnit definition, AutoStandardPrefix stdPrefix) : base(ctx, abbreviation, name, stdPrefix, false) { DefinitionFactor = definitionFactor; Definition = definition; }
/// <summary> /// Gets all the prefixes for a <see cref="AutoStandardPrefix"/>. /// </summary> /// <param name="a">The auto standard prefix.</param> /// <returns>An empty set, <see cref="All"/>, <see cref="MetricPrefixes"/> or <see cref="BinaryPrefixes"/>.</returns> public static IEnumerable <MeasureStandardPrefix> GetPrefixes(AutoStandardPrefix a) { switch (a) { case AutoStandardPrefix.Both: return(All); case AutoStandardPrefix.Binary: return(BinaryPrefixes); case AutoStandardPrefix.Metric: return(MetricPrefixes); } return(Enumerable.Empty <MeasureStandardPrefix>()); }
FundamentalMeasureUnit RegisterFundamental(string abbreviation, string name, AutoStandardPrefix stdPrefix, bool isNormalized) { return(Register(abbreviation, name, creator: () => new FundamentalMeasureUnit(this, abbreviation, name, stdPrefix, isNormalized), checker: m => { if (m.IsNormalized != isNormalized) { ThrowArgumentException(m, $"new IsNormalized {isNormalized} differ from {m.IsNormalized}."); } CheckArgumentAutoStdPrefix(m, stdPrefix); })); }
/// <summary> /// Checks whether a new unit abbreviation can be defined (or may be redefined) in this context. /// </summary> /// <param name="a">The proposed abbreviation.</param> /// <param name="autoStandardPrefix"> /// Whether the new unit must support automatic metric and/or binary standard prefixes. /// </param> /// <returns>The <see cref="NewAbbreviationConflict"/> flag.</returns> public NewAbbreviationConflict CheckValidNewAbbreviation(string a, AutoStandardPrefix autoStandardPrefix) { if (_allUnits.ContainsKey(a)) { return(NewAbbreviationConflict.Exists); } if (String.IsNullOrEmpty(a) || !a.All(c => Char.IsLetter(c) || Char.IsSymbol(c) || c == '#' || c == '%' || c == '‰' || c == '‱' || c == '㏙')) { return(NewAbbreviationConflict.InvalidCharacters); } foreach (var withPrefix in MeasureStandardPrefix.GetPrefixes(autoStandardPrefix) .Select(p => p.Abbreviation + a)) { if (_allUnits.ContainsKey(withPrefix)) { return(NewAbbreviationConflict.AmbiguousAutoStandardPrefix); } } // Optimization: if the new abbreviation does not start with a // standard prefix, it is useless to challenge it against // existing units. var prefix = MeasureStandardPrefix.FindPrefix(a); if (prefix != MeasureStandardPrefix.None && _allUnits.Values .OfType <AtomicMeasureUnit>() .Where(u => u.AutoStandardPrefix != AutoStandardPrefix.None) .SelectMany(u => MeasureStandardPrefix.GetPrefixes(u.AutoStandardPrefix) .Where(p => p != MeasureStandardPrefix.None) .Select(p => p.Abbreviation + u.Abbreviation)) .Any(exists => exists == a)) { return(NewAbbreviationConflict.MatchPotentialAutoStandardPrefixedUnit); } return(NewAbbreviationConflict.None); }
/// <summary> /// Checks whether a new unit abbreviation can be defined in this context. /// </summary> /// <param name="a">The proposed abbreviation.</param> /// <param name="autoStandardPrefix"> /// Whether the new unit must support automatic metric and/or binary standard prefixes. /// </param> /// <returns>True if the abbreviation can be used, false otherwise.</returns> public bool IsValidNewAbbreviation(string a, AutoStandardPrefix autoStandardPrefix) { if (String.IsNullOrEmpty(a) || !a.All(c => Char.IsLetter(c) || Char.IsSymbol(c) || c == '#' || c == '%' || c == '‰' || c == '‱' || c == '㏙')) { return(false); } foreach (var withPrefix in MeasureStandardPrefix.GetPrefixes(autoStandardPrefix) .Select(p => p.Abbreviation + a)) { if (_allUnits.ContainsKey(withPrefix)) { return(false); } } var prefix = MeasureStandardPrefix.FindPrefix(a); // Optimization: if the new abbreviation does not start with a // standard prefix, it is useless to challenge it against // existing units. if (prefix != MeasureStandardPrefix.None) { return(!_allUnits.Values .OfType <AtomicMeasureUnit>() .Where(u => u.AutoStandardPrefix != AutoStandardPrefix.None) .SelectMany(u => MeasureStandardPrefix.GetPrefixes(u.AutoStandardPrefix) .Where(p => p != MeasureStandardPrefix.None) .Select(p => p.Abbreviation + u.Abbreviation)) .Any(exists => exists == a)); } return(true); }
internal FundamentalMeasureUnit(MeasureContext ctx, string abbreviation, string name, AutoStandardPrefix stdPrefix, bool isNormalized) : base(ctx, abbreviation, name, stdPrefix, isNormalized) { }
/// <summary> /// Finds the best prefix given a <see cref="ExpFactor"/>. /// If both metric and binary exponents exist (ie. are not zero), the metric prefix /// is selected (the Exp2 is injected in the adjustment factor). /// </summary> /// <param name="newExp">Must not be the neutral factor.</param> /// <param name="stdPrefix"> /// Whether the actual standard prefixes are allowed. When not, it is the adjustemnt factor /// that handles the exponent and the prefix None is used. /// </param> /// <returns>The best prefix with the required adjustment factor.</returns> internal static (ExpFactor Adjustment, MeasureStandardPrefix Prefix) FindBest(ExpFactor newExp, AutoStandardPrefix stdPrefix) { // Privilegiates metric prefix if any and if metric is allowed. Debug.Assert(!newExp.IsNeutral); if (newExp.Exp10 != 0 && (stdPrefix & AutoStandardPrefix.Metric) != 0) { var b10 = FindBest(newExp.Exp10, _allMetricIndex, _allMetric); return(new ExpFactor(newExp.Exp2, b10.Item1), b10.Item2); } if (newExp.Exp2 != 0 && (stdPrefix & AutoStandardPrefix.Binary) != 0) { Debug.Assert(newExp.Exp2 != 0); var b2 = FindBest(newExp.Exp2, _allBinaryIndex, _allBinary); return(new ExpFactor(b2.Item1, 0), b2.Item2); } return(newExp, None); }
FundamentalMeasureUnit RegisterFundamental(string abbreviation, string name, AutoStandardPrefix stdPrefix, bool isNormalized) { return(Register(abbreviation, name, creator: () => new FundamentalMeasureUnit(this, abbreviation, name, stdPrefix, isNormalized), checker: m => m.IsNormalized == isNormalized && m.AutoStandardPrefix == stdPrefix)); }
AliasMeasureUnit RegisterAlias(string a, string n, FullFactor f, MeasureUnit d, AutoStandardPrefix stdPrefix) { if (d == MeasureUnit.None) { if (f.Factor != 1.0) { throw new Exception("Only exponential factor is allowed for dimensionless units."); } d = RegisterPrefixed(f.ExpFactor, MeasureStandardPrefix.None, MeasureUnit.None, false); f = FullFactor.Neutral; } return(Register(abbreviation: a, name: n, creator: () => new AliasMeasureUnit(this, a, n, f, d, stdPrefix), checker: m => m.Definition.Normalization == d.Normalization && m.NormalizationFactor == d.NormalizationFactor.Multiply(f) && m.AutoStandardPrefix == stdPrefix)); }
AliasMeasureUnit RegisterAlias(string a, string n, FullFactor f, MeasureUnit d, AutoStandardPrefix stdPrefix) { if (d == MeasureUnit.None) { if (f.Factor != 1.0) { throw new ArgumentException("Only exponential factor is allowed for dimensionless units."); } d = RegisterPrefixed(f.ExpFactor, MeasureStandardPrefix.None, MeasureUnit.None, false); f = FullFactor.Neutral; } return(Register(abbreviation: a, name: n, creator: () => new AliasMeasureUnit(this, a, n, f, d, stdPrefix), checker: m => { if (m.Definition.Normalization != d.Normalization) { ThrowArgumentException(m, $"new definition unit '{d}' is not compatible with '{m.Definition}'."); } if (m.NormalizationFactor != d.NormalizationFactor.Multiply(f)) { ThrowArgumentException(m, $"new normalization factor '{f}' should be '{m.NormalizationFactor.DivideBy(d.NormalizationFactor)}'."); } CheckArgumentAutoStdPrefix(m, stdPrefix); })); }