/// <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;
 }
Exemple #7
0
        /// <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);
        }
Exemple #11
0
 internal FundamentalMeasureUnit(MeasureContext ctx, string abbreviation, string name, AutoStandardPrefix stdPrefix, bool isNormalized)
     : base(ctx, abbreviation, name, stdPrefix, isNormalized)
 {
 }
Exemple #12
0
 /// <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);
     }));
 }