Esempio n. 1
0
 static (ExpFactor Adjustment, AtomicMeasureUnit Result) Combine(MeasureStandardPrefix prefix, AtomicMeasureUnit u, bool allowAdjustmentPrefix)
 {
     if (prefix.Base == 0)
     {
         return(ExpFactor.Neutral, u);
     }
     if (u is PrefixedMeasureUnit p)
     {
         var newExp = prefix.Factor.Multiply(p.Prefix.Factor).Multiply(p.AdjustmentFactor);
         if (newExp.IsNeutral)
         {
             return(ExpFactor.Neutral, p.AtomicMeasureUnit);
         }
         var best = FindBest(newExp, p.AtomicMeasureUnit.AutoStandardPrefix);
         if (!allowAdjustmentPrefix)
         {
             return(best.Adjustment, u.Context.RegisterPrefixed(ExpFactor.Neutral, best.Prefix, p.AtomicMeasureUnit));
         }
         return(ExpFactor.Neutral, u.Context.RegisterPrefixed(best.Adjustment, best.Prefix, p.AtomicMeasureUnit));
     }
     if ((u.AutoStandardPrefix & AutoStandardPrefix.Binary) == 0 && prefix.Base == 2 ||
         (u.AutoStandardPrefix & AutoStandardPrefix.Metric) == 0 && prefix.Base == 10)
     {
         return(ExpFactor.Neutral, u.Context.RegisterPrefixed(prefix.Factor, None, u));
     }
     return(ExpFactor.Neutral, u.Context.RegisterPrefixed(ExpFactor.Neutral, prefix, u));
 }
        /// <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);
        }
Esempio n. 3
0
        ExponentMeasureUnit CreateExponentPrefixed(int exp, ExpFactor adjustment, AtomicMeasureUnit atomic)
        {
            ExponentMeasureUnit u;

            if (!adjustment.IsNeutral)
            {
                var best = MeasureStandardPrefix.FindBest(adjustment, atomic.AutoStandardPrefix);
                atomic = RegisterPrefixed(best.Adjustment, best.Prefix, atomic);
            }
            u = RegisterExponent(exp, atomic);
            return(u);
        }
        /// <summary>
        /// The isNormalized is nullable: when automatically creating PrefixedUnit this is null and:
        ///  - if the unit must be created it won't be the normalized one.
        ///  - if the unit is found, we don't check the potential race condition.
        /// Only when explicitly registering/creating the unit with true or false, the resulting unit IsNormalized
        /// property is checked to prevent race conditions.
        /// </summary>
        internal PrefixedMeasureUnit RegisterPrefixed(ExpFactor adjustment, MeasureStandardPrefix p, AtomicMeasureUnit u, bool?isNormalized = null)
        {
            var names = PrefixedMeasureUnit.ComputeNames(adjustment, p, u);

            return(Register(names.A, names.N, () => new PrefixedMeasureUnit(this, names, adjustment, p, u, isNormalized ?? false), m =>
            {
                if (isNormalized.HasValue && m.IsNormalized != isNormalized)
                {
                    ThrowArgumentException(m, $"new IsNormalized '{isNormalized}' differ from '{m.IsNormalized}'.");
                }
            }));
        }
Esempio n. 5
0
        static MeasureStandardPrefix()
        {
            None = new MeasureStandardPrefix("", "", 0, 0);

            Yocto = new MeasureStandardPrefix("y", "Yocto", 10, -24);
            Zepto = new MeasureStandardPrefix("z", "Zepto", 10, -21);
            Atto  = new MeasureStandardPrefix("a", "Atto", 10, -18);
            Femto = new MeasureStandardPrefix("f", "Femto", 10, -15);
            Pico  = new MeasureStandardPrefix("p", "Pico", 10, -12);
            Nano  = new MeasureStandardPrefix("n", "Nano", 10, -9);
            Micro = new MeasureStandardPrefix("µ", "Micro", 10, -6);
            Milli = new MeasureStandardPrefix("m", "Milli", 10, -3);
            Centi = new MeasureStandardPrefix("c", "Centi", 10, -2);
            Deci  = new MeasureStandardPrefix("d", "Deci", 10, -1);
            Deca  = new MeasureStandardPrefix("da", "Deca", 10, 1);
            Hecto = new MeasureStandardPrefix("h", "Hecto", 10, 2);
            Kilo  = new MeasureStandardPrefix("k", "Kilo", 10, 3);
            Mega  = new MeasureStandardPrefix("M", "Mega", 10, 6);
            Giga  = new MeasureStandardPrefix("G", "Giga", 10, 9);
            Tera  = new MeasureStandardPrefix("T", "Tera", 10, 12);
            Peta  = new MeasureStandardPrefix("P", "Peta", 10, 15);
            Exa   = new MeasureStandardPrefix("E", "Exa", 10, 18);
            Zetta = new MeasureStandardPrefix("Z", "Zetta", 10, 21);
            Yotta = new MeasureStandardPrefix("Y", "Yotta", 10, 24);

            Kibi = new MeasureStandardPrefix("Ki", "Kibi", 2, 10);
            Mebi = new MeasureStandardPrefix("Mi", "Mebi", 2, 20);
            Gibi = new MeasureStandardPrefix("Gi", "Gibi", 2, 30);
            Tebi = new MeasureStandardPrefix("Ti", "Tebi", 2, 40);
            Pebi = new MeasureStandardPrefix("Pi", "Pebi", 2, 50);
            Exbi = new MeasureStandardPrefix("Ei", "Exbi", 2, 60);
            Zebi = new MeasureStandardPrefix("Zi", "Zebi", 2, 70);
            Yobi = new MeasureStandardPrefix("Yi", "Yobi", 2, 80);

            _allMetric = new MeasureStandardPrefix[] {
                Yocto, Zepto, Atto, Femto, Pico, Nano, Micro, Milli, Centi, Deci, Deca,
                Hecto, Kilo, Mega, Giga, Tera, Peta, Exa, Zetta, Yotta
            };
            _allMetricIndex = _allMetric.Select(p => (int)p.Factor.Exp10).ToArray();

            _allBinary      = new MeasureStandardPrefix[] { Kibi, Mebi, Gibi, Tebi, Pebi, Exbi, Zebi, Yobi };
            _allBinaryIndex = _allBinary.Select(p => (int)p.Factor.Exp2).ToArray();

            _prefixes = new Dictionary <string, MeasureStandardPrefix>();
            _prefixes.Add(String.Empty, None);
            foreach (var p in _allMetric.Concat(_allBinary))
            {
                _prefixes.Add(p.Abbreviation, p);
                _prefixes.Add(p.Name, p);
            }
        }
        /// <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);
        }
Esempio n. 8
0
        bool TryParseExponent(Match match, out ExponentMeasureUnit u, bool skipFirstLookup = false)
        {
            if (!skipFirstLookup && TryGetExistingExponent(match.ToString(), out u))
            {
                return(true);
            }
            u = null;
            // Handling dimensionless unit.
            var factor = match.Groups[4].Value;

            if (factor.Length > 0)
            {
                var f = ParseFactor(factor, ExpFactor.Neutral);
                u = RegisterPrefixed(f, MeasureStandardPrefix.None, MeasureUnit.None);
                return(true);
            }
            string sExp = match.Groups[3].Value;
            int    exp  = sExp.Length > 0 ? Int32.Parse(sExp) : 1;

            if (exp == 0)
            {
                u = MeasureUnit.None;
                return(true);
            }
            string withoutExp = match.Groups[1].Value + match.Groups[2].Value;

            if (_allUnits.TryGetValue(withoutExp, out var unit))
            {
                if (exp == 1 && unit is ExponentMeasureUnit direct)
                {
                    u = direct;
                    return(true);
                }
                if (!(unit is AtomicMeasureUnit atomic))
                {
                    return(false);
                }
                u = RegisterExponent(exp, atomic);
                return(true);
            }
            ExpFactor adjustment = ExpFactor.Neutral;

            foreach (Capture f in match.Groups[1].Captures)
            {
                adjustment = ParseFactor(f.Value, adjustment);
            }
            string withoutAdjustment = match.Groups[2].Value;

            if (_allUnits.TryGetValue(withoutAdjustment, out unit))
            {
                if (!(unit is AtomicMeasureUnit basic))
                {
                    return(false);
                }
                if (basic is PrefixedMeasureUnit prefix)
                {
                    adjustment = adjustment.Multiply(prefix.AdjustmentFactor).Multiply(prefix.Prefix.Factor);
                    basic      = prefix.AtomicMeasureUnit;
                }
                u = CreateExponentPrefixed(exp, adjustment, basic);
                return(true);
            }
            var p = MeasureStandardPrefix.FindPrefix(withoutAdjustment);

            if (p == MeasureStandardPrefix.None)
            {
                return(false);
            }
            withoutAdjustment = withoutAdjustment.Substring(p.Abbreviation.Length);
            if (withoutAdjustment.Length == 0)
            {
                return(false);
            }
            if (_allUnits.TryGetValue(withoutAdjustment, out unit))
            {
                if (!(unit is AtomicMeasureUnit basic))
                {
                    return(false);
                }
                if (basic is PrefixedMeasureUnit prefix)
                {
                    return(false);
                }
                adjustment = adjustment.Multiply(p.Factor);
                u          = CreateExponentPrefixed(exp, adjustment, basic);
                return(true);
            }
            return(false);
        }
        /// <summary>
        /// The isNormalized is nullable: when automatically creating PrefixedUnit this is null and :
        ///  - if the unit must be created it won't be the normalized one.
        ///  - if the unit is found, we don't check the potential race condition.
        /// Only when explicitly registering/creating the unit with true or false, the resulting unit IsNormalized
        /// property is checked to prevent race conditions.
        /// </summary>
        internal PrefixedMeasureUnit RegisterPrefixed(ExpFactor adjustment, MeasureStandardPrefix p, AtomicMeasureUnit u, bool?isNormalized = null)
        {
            var names = PrefixedMeasureUnit.ComputeNames(adjustment, p, u);

            return(Register(names.A, names.N, () => new PrefixedMeasureUnit(this, names, adjustment, p, u, isNormalized ?? false), m => !isNormalized.HasValue || m.IsNormalized == isNormalized));
        }