Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
Unit.cs
1using System;
3using System.Reflection;
4using System.Text;
5using Waher.Events;
9
10namespace Waher.Script.Units
11{
15 public sealed class Unit
16 {
17 private readonly Prefix prefix;
18 private readonly ICollection<UnitFactor> factors;
19 private bool hasBaseUnits = false;
20 private bool hasReferenceUnits = false;
21
27 public Unit(Prefix Prefix, ICollection<UnitFactor> Factors)
28 {
29 this.prefix = Prefix;
30 this.factors = Factors;
31 }
32
39 {
40 this.prefix = Prefix;
41 this.factors = Factors;
42 }
43
48 public Unit(params AtomicUnit[] AtomicUnits)
49 : this(Prefix.None, AtomicUnits)
50 {
51 }
52
58 public Unit(Prefix Prefix, params AtomicUnit[] AtomicUnits)
59 : this(Prefix, Prepare(AtomicUnits))
60 {
61 }
62
63 private static UnitFactor[] Prepare(AtomicUnit[] AtomicUnits)
64 {
65 int i, c = AtomicUnits.Length;
66 UnitFactor[] Result = new UnitFactor[c];
67
68 for (i = 0; i < c; i++)
69 Result[i] = new UnitFactor(AtomicUnits[i], 1);
70
71 return Result;
72 }
73
78 public Unit(params string[] AtomicUnits)
79 : this(Prefix.None, AtomicUnits)
80 {
81 }
82
88 public Unit(Prefix Prefix, params string[] AtomicUnits)
89 {
90 this.prefix = Prefix;
91
92 int i, c = AtomicUnits.Length;
93 UnitFactor[] Factors = new UnitFactor[c];
94
95 for (i = 0; i < c; i++)
96 Factors[i] = new UnitFactor(AtomicUnits[i], 1);
97
98 this.factors = Factors;
99 }
100
106 public static Unit Parse(string UnitString)
107 {
108 if (TryParse(UnitString, out Unit Unit))
109 return Unit;
110 else
111 throw new Exceptions.ScriptException("Unable to parse unit: " + UnitString);
112 }
113
120 public static bool TryParse(string UnitString, out Unit Unit)
121 {
122 int Pos = 0;
123 int Len = UnitString?.Length ?? 0;
124
125 if (!TryParse(UnitString, ref Pos, Len, true, out Unit))
126 return false;
127
128 if (Pos < Len)
129 return false;
130
131 return true;
132 }
133
134 private static bool TryParse(string UnitString, ref int Pos, int Len, bool PermitPrefix, out Unit Unit)
135 {
136 Unit = null;
137 if (Pos >= Len)
138 return false;
139
142 KeyValuePair<Prefix, UnitFactor[]> CompoundFactors;
143 bool HasCompoundFactors;
144 string Name, Name2, s;
145 int Exponent;
146 int Start = Pos;
147 int i;
148 char ch = UnitString[Pos++];
149 bool LastDivision = false;
150
151 if (PermitPrefix)
152 {
153 if (ch == 'd' && Pos < Len && UnitString[Pos] == 'a')
154 {
155 Pos++;
156 Prefix = Prefix.Deka;
157 }
158 else if (!Prefixes.TryParsePrefix(ch, out Prefix))
159 Pos--;
160
161 i = Pos;
162 ch = Pos < Len ? UnitString[Pos++] : (char)0;
163 }
164 else
165 {
166 Prefix = Prefix.None;
167 i = Pos - 1;
168 }
169
170 if (!char.IsLetter(ch) && Prefix != Prefix.None)
171 {
172 Pos = i = Start;
173 Prefix = Prefix.None;
174 ch = Pos < Len ? UnitString[Pos++] : (char)0;
175 }
176 else if (ch == '/')
177 {
178 LastDivision = true;
179 ch = Pos < Len ? UnitString[Pos++] : (char)0;
180 while (ch > 0 && (ch <= ' ' || ch == 160))
181 ch = Pos < Len ? UnitString[Pos++] : (char)0;
182 }
183
184 while (char.IsLetter(ch) || ch == '(' || ch == '°' || ch == '%' || ch == '‰' || ch == '‱' || ch == '1')
185 {
186 if (ch == '(')
187 {
188 if (!TryParse(UnitString, ref Pos, Len, false, out Unit Unit2))
189 return false;
190
191 ch = Pos < Len ? UnitString[Pos++] : (char)0;
192 while (ch > 0 && (ch <= ' ' || ch == 160))
193 ch = Pos < Len ? UnitString[Pos++] : (char)0;
194
195 if (ch != ')')
196 return false;
197
198 ch = Pos < Len ? UnitString[Pos++] : (char)0;
199 while (ch > 0 && (ch <= ' ' || ch == 160))
200 ch = Pos < Len ? UnitString[Pos++] : (char)0;
201
202 if (ch == '^')
203 {
204 ch = Pos < Len ? UnitString[Pos++] : (char)0;
205 while (ch > 0 && (ch <= ' ' || ch == 160))
206 ch = Pos < Len ? UnitString[Pos++] : (char)0;
207
208 if (ch == '-' || char.IsDigit(ch))
209 {
210 i = Pos - 1;
211
212 if (ch == '-')
213 ch = Pos < Len ? UnitString[Pos++] : (char)0;
214
215 while (char.IsDigit(ch))
216 ch = Pos < Len ? UnitString[Pos++] : (char)0;
217
218 if (ch == 0)
219 s = UnitString.Substring(i, Pos - i);
220 else
221 s = UnitString.Substring(i, Pos - i - 1);
222
223 if (!int.TryParse(s, out Exponent))
224 return false;
225 }
226 else
227 return false;
228 }
229 else if (ch == '²')
230 {
231 Exponent = 2;
232 ch = Pos < Len ? UnitString[Pos++] : (char)0;
233 }
234 else if (ch == '³')
235 {
236 Exponent = 3;
237 ch = Pos < Len ? UnitString[Pos++] : (char)0;
238 }
239 else
240 Exponent = 1;
241
242 if (LastDivision)
243 {
244 foreach (UnitFactor Factor in Unit2.Factors)
245 Factors.Add(new UnitFactor(Factor.Unit, -Factor.Exponent * Exponent));
246 }
247 else
248 {
249 foreach (UnitFactor Factor in Unit2.Factors)
250 Factors.Add(new UnitFactor(Factor.Unit, Factor.Exponent * Exponent));
251 }
252 }
253 else
254 {
255 if (char.IsLetter(ch))
256 {
257 do
258 {
259 ch = Pos < Len ? UnitString[Pos++] : (char)0;
260 }
261 while (char.IsLetter(ch));
262 }
263 else if (ch == '°')
264 {
265 ch = Pos < Len ? UnitString[Pos++] : (char)0;
266
267 while (char.IsLetter(ch))
268 ch = Pos < Len ? UnitString[Pos++] : (char)0;
269 }
270 else if (ch == '%')
271 {
272 ch = Pos < Len ? UnitString[Pos++] : (char)0;
273
274 if (ch == '0')
275 {
276 ch = Pos < Len ? UnitString[Pos++] : (char)0;
277
278 if (ch == '0')
279 ch = Pos < Len ? UnitString[Pos++] : (char)0;
280 }
281 }
282 else if (ch == '‰' || ch == '‱')
283 ch = Pos < Len ? UnitString[Pos++] : (char)0;
284 else if (ch == '1')
285 {
286 ch = Pos < Len ? UnitString[Pos++] : (char)0;
287 if (char.IsDigit(ch))
288 {
289 Pos -= 2;
290 return false;
291 }
292 }
293 else
294 ch = (char)0;
295
296 if (ch == 0)
297 Name = UnitString.Substring(i, Pos - i);
298 else
299 Name = UnitString.Substring(i, Pos - i - 1);
300
301 if (PermitPrefix)
302 {
303 if (Expression.keywords.ContainsKey(Name2 = UnitString.Substring(Start, i - Start) + Name))
304 return false;
305 else if (HasCompoundFactors = TryGetCompoundUnit(Name2, out CompoundFactors))
306 {
307 Prefix = CompoundFactors.Key;
308 Name = Name2;
309 }
310 else if (ContainsDerivedOrBaseUnit(Name2))
311 {
312 Prefix = Prefix.None;
313 Name = Name2;
314 }
315 else
316 HasCompoundFactors = TryGetCompoundUnit(Name, out CompoundFactors);
317 }
318 else
319 HasCompoundFactors = TryGetCompoundUnit(Name, out CompoundFactors);
320
321 while (ch > 0 && (ch <= ' ' || ch == 160))
322 ch = Pos < Len ? UnitString[Pos++] : (char)0;
323
324 if (ch == '^')
325 {
326 ch = Pos < Len ? UnitString[Pos++] : (char)0;
327 while (ch > 0 && (ch <= ' ' || ch == 160))
328 ch = Pos < Len ? UnitString[Pos++] : (char)0;
329
330 if (ch == '-' || char.IsDigit(ch))
331 {
332 i = Pos - 1;
333
334 if (ch == '-')
335 ch = Pos < Len ? UnitString[Pos++] : (char)0;
336
337 while (char.IsDigit(ch))
338 ch = Pos < Len ? UnitString[Pos++] : (char)0;
339
340 if (ch == 0)
341 s = UnitString.Substring(i, Pos - i);
342 else
343 s = UnitString.Substring(i, Pos - i - 1);
344
345 if (!int.TryParse(s, out Exponent))
346 return false;
347 }
348 else
349 return false;
350 }
351 else if (ch == '²')
352 {
353 Exponent = 2;
354 ch = Pos < Len ? UnitString[Pos++] : (char)0;
355 }
356 else if (ch == '³')
357 {
358 Exponent = 3;
359 ch = Pos < Len ? UnitString[Pos++] : (char)0;
360 }
361 else
362 Exponent = 1;
363
364 if (HasCompoundFactors)
365 {
366 if (LastDivision)
367 {
368 foreach (UnitFactor Segment in CompoundFactors.Value)
369 Factors.Add(new UnitFactor(Segment.Unit, -Segment.Exponent * Exponent));
370 }
371 else
372 {
373 foreach (UnitFactor Segment in CompoundFactors.Value)
374 Factors.Add(new UnitFactor(Segment.Unit, Segment.Exponent * Exponent));
375 }
376 }
377 else
378 {
379 if (LastDivision)
380 Factors.Add(new UnitFactor(Name, -Exponent));
381 else
382 Factors.Add(new UnitFactor(Name, Exponent));
383 }
384 }
385
386 while (ch > 0 && (ch <= ' ' || ch == 160))
387 ch = Pos < Len ? UnitString[Pos++] : (char)0;
388
389 if (ch == '*' || ch == '⋅')
390 LastDivision = false;
391 else if (ch == '/')
392 LastDivision = true;
393 else if (ch == ')')
394 {
395 Pos--;
396 break;
397 }
398 else if (ch == 0)
399 break;
400 else
401 return false;
402
403 ch = Pos < Len ? UnitString[Pos++] : (char)0;
404 while (ch > 0 && (ch <= ' ' || ch == 160))
405 ch = Pos < Len ? UnitString[Pos++] : (char)0;
406
407 i = Pos - 1;
408 PermitPrefix = false;
409 }
410
411 if (!Factors.HasFirstItem)
412 return false;
413
414 Unit = new Unit(Prefix, Factors);
415
416 return true;
417 }
418
422 public Prefix Prefix => this.prefix;
423
427 public ICollection<UnitFactor> Factors => this.factors;
428
432 public bool IsEmpty
433 {
434 get
435 {
436 return this.prefix == Prefix.None && this.factors.Count == 0;
437 }
438 }
439
443 public bool HasFactors
444 {
445 get
446 {
447 return this.factors.Count > 0;
448 }
449 }
450
454 public static readonly Unit Empty = new Unit(Prefix.None, Array.Empty<UnitFactor>());
455
462 public Unit Invert(out int ResidueExponent)
463 {
464 int Exponent = Prefixes.PrefixToExponent(this.prefix);
466
467 foreach (UnitFactor Factor in this.factors)
468 Factors.Add(new UnitFactor(Factor.Unit, -Factor.Exponent));
469
470 return new Unit(Prefixes.ExponentToPrefix(-Exponent, out ResidueExponent), Factors);
471 }
472
474 public override bool Equals(object obj)
475 {
476 if (!(obj is Unit U))
477 return false;
478
479 return this.Equals(U, true);
480 }
481
488 public bool Equals(Unit Unit2, bool CheckPrefix)
489 {
490 if (CheckPrefix && this.prefix != Unit2.prefix)
491 return false;
492
493 if (this.factors.Count != Unit2.factors.Count)
494 return false;
495
496 string Name;
497 bool Found;
498
499 foreach (UnitFactor Factor in this.factors)
500 {
501 Name = Factor.Unit.Name;
502 Found = false;
503
504 foreach (UnitFactor Factor2 in Unit2.factors)
505 {
506 if (Name == Factor2.Unit.Name)
507 {
508 if (Factor.Exponent != Factor2.Exponent)
509 return false;
510
511 Found = true;
512 break;
513 }
514 }
515
516 if (!Found)
517 return false;
518 }
519
520 return true;
521 }
522
524 public override int GetHashCode()
525 {
526 int i = (int)this.prefix;
527
528 foreach (UnitFactor Factor in this.factors)
529 i ^= Factor.Unit.GetHashCode() ^ Factor.Exponent.GetHashCode();
530
531 return i;
532 }
533
535 public override string ToString()
536 {
537 return this.ToString(true);
538 }
539
544 public string ToString(bool IncludePrefix)
545 {
546 StringBuilder Numerator = null;
547 StringBuilder Denominator = null;
548 int NrDenominators = 0;
549
550 foreach (UnitFactor Factor in this.factors)
551 {
552 if (Factor.Exponent > 0)
553 {
554 if (Numerator is null)
555 {
556 if (IncludePrefix)
557 Numerator = new StringBuilder(Prefixes.ToString(this.prefix));
558 else
559 Numerator = new StringBuilder();
560 }
561 else
562 Numerator.Append('⋅');
563
564 Numerator.Append(Factor.Unit.Name);
565
566 if (Factor.Exponent > 1)
567 {
568 if (Factor.Exponent == 2)
569 Numerator.Append('²');
570 else if (Factor.Exponent == 3)
571 Numerator.Append('³');
572 else
573 {
574 Numerator.Append('^');
575 Numerator.Append(Factor.Exponent.ToString());
576 }
577 }
578 }
579 else
580 {
581 if (Denominator is null)
582 Denominator = new StringBuilder();
583 else
584 Denominator.Append('⋅');
585
586 NrDenominators++;
587 Denominator.Append(Factor.Unit.Name);
588
589 if (Factor.Exponent < -1)
590 {
591 if (Factor.Exponent == -2)
592 Denominator.Append('²');
593 else if (Factor.Exponent == -3)
594 Denominator.Append('³');
595 else
596 {
597 Denominator.Append('^');
598 Denominator.Append((-Factor.Exponent).ToString());
599 }
600 }
601 }
602 }
603
604 if (Numerator is null)
605 {
606 if (IncludePrefix)
607 Numerator = new StringBuilder(Prefixes.ToString(this.prefix));
608 else
609 Numerator = new StringBuilder();
610 }
611
612 if (!(Denominator is null))
613 {
614 Numerator.Append('/');
615
616 if (NrDenominators > 1)
617 Numerator.Append('(');
618
619 Numerator.Append(Denominator.ToString());
620
621 if (NrDenominators > 1)
622 Numerator.Append(')');
623 }
624
625 return Numerator.ToString();
626 }
627
635 public static Unit Multiply(Unit Left, Unit Right, out int ResidueExponent)
636 {
638 int LeftExponent = Prefixes.PrefixToExponent(Left.prefix);
639 int RightExponent = Prefixes.PrefixToExponent(Right.prefix);
640 int ResultExponent = LeftExponent + RightExponent;
641 Prefix ResultPrefix = Prefixes.ExponentToPrefix(ResultExponent, out ResidueExponent);
642 string Name;
643
644 Result.AddRange(Left.factors);
645
646 foreach (UnitFactor Factor in Right.factors)
647 {
648 Name = Factor.Unit.Name;
649
650 if (!Result.Update((ref UnitFactor f, out bool Keep) =>
651 {
652 if (f.Unit.Name == Name)
653 {
654 f = new UnitFactor(f.Unit, f.Exponent + Factor.Exponent);
655 Keep = f.Exponent != 0;
656 return false;
657 }
658 else
659 {
660 Keep = true;
661 return true;
662 }
663 }))
664 {
665 continue;
666 }
667
668 Result.Add(Factor);
669 }
670
671 return new Unit(ResultPrefix, Result);
672 }
673
681 public static Unit Divide(Unit Left, Unit Right, out int ResidueExponent)
682 {
684 int LeftExponent = Prefixes.PrefixToExponent(Left.prefix);
685 int RightExponent = Prefixes.PrefixToExponent(Right.prefix);
686 int ResultExponent = LeftExponent - RightExponent;
687 Prefix ResultPrefix = Prefixes.ExponentToPrefix(ResultExponent, out ResidueExponent);
688 string Name;
689
690 Result.AddRange(Left.factors);
691
692 foreach (UnitFactor Factor in Right.factors)
693 {
694 Name = Factor.Unit.Name;
695
696 if (!Result.Update((ref UnitFactor f, out bool Keep) =>
697 {
698 if (f.Unit.Name == Name)
699 {
700 f = new UnitFactor(f.Unit, f.Exponent - Factor.Exponent);
701 Keep = f.Exponent != 0;
702 return false;
703 }
704 else
705 {
706 Keep = true;
707 return true;
708 }
709 }))
710 {
711 continue;
712 }
713
714 Result.Add(new UnitFactor(Factor.Unit, -Factor.Exponent));
715 }
716
717 return new Unit(ResultPrefix, Result);
718 }
719
725 public Unit ToBaseUnits(ref double Magnitude)
726 {
727 if (this.hasBaseUnits)
728 return this;
729
730 lock (synchObject)
731 {
732 if (baseUnits is null)
733 Search();
734
735 bool HasNonBase = false;
736
737 foreach (UnitFactor Factor in this.factors)
738 {
739 if (!baseUnits.ContainsKey(Factor.Unit.Name))
740 {
741 HasNonBase = true;
742 break;
743 }
744 }
745
746 if (HasNonBase)
747 {
749 int Exponent = Prefixes.PrefixToExponent(this.prefix);
750 int FactorExponent;
751 string Name;
752
753 foreach (UnitFactor Factor in this.factors)
754 {
755 FactorExponent = Factor.Exponent;
756
757 if (baseUnits.ContainsKey(Name = Factor.Unit.Name))
758 this.Add(BaseFactors, Factor.Unit, Factor.Exponent);
759 else if (derivedUnits.TryGetValue(Name, out PhysicalQuantity Quantity))
760 {
761 Magnitude *= Math.Pow(Quantity.Magnitude, FactorExponent);
762 Exponent += Prefixes.PrefixToExponent(Quantity.Unit.prefix) * FactorExponent;
763
764 foreach (UnitFactor Segment in Quantity.Unit.factors)
765 this.Add(BaseFactors, Segment.Unit, Segment.Exponent * FactorExponent);
766 }
767 else if (compoundUnits.TryGetValue(Name, out KeyValuePair<Prefix, UnitFactor[]> Units))
768 {
769 Exponent += Prefixes.PrefixToExponent(Units.Key) * FactorExponent;
770
771 foreach (UnitFactor Segment in Units.Value)
772 this.Add(BaseFactors, Segment.Unit, Segment.Exponent * FactorExponent);
773 }
774 else
775 this.Add(BaseFactors, Factor.Unit, Factor.Exponent);
776 }
777
778 Unit Result = new Unit(Prefixes.ExponentToPrefix(Exponent, out FactorExponent), BaseFactors);
779 if (FactorExponent != 0)
780 Magnitude *= Math.Pow(10, FactorExponent);
781
782 Result.hasBaseUnits = true;
783
784 return Result;
785 }
786 else
787 {
788 this.hasBaseUnits = true;
789 return this;
790 }
791 }
792 }
793
794 private void Add(ChunkedList<UnitFactor> Factors, AtomicUnit AtomicUnit, int Exponent)
795 {
796 string Name = AtomicUnit.Name;
797
798 if (Factors.Update((ref UnitFactor f, out bool Keep) =>
799 {
800 if (f.Unit.Name == Name)
801 {
802 f = new UnitFactor(f.Unit, f.Exponent + Exponent);
803 Keep = f.Exponent != 0;
804 return false;
805 }
806 else
807 {
808 Keep = true;
809 return true;
810 }
811 }))
812 {
813 Factors.Add(new UnitFactor(AtomicUnit, Exponent));
814 }
815 }
816
822 public Unit ToReferenceUnits(ref double Magnitude)
823 {
824 double x = 0;
825 return this.ToReferenceUnits(ref Magnitude, ref x);
826 }
827
834 public Unit ToReferenceUnits(ref double Magnitude, ref double NrDecimals)
835 {
836 if (this.hasReferenceUnits)
837 return this;
838
839 lock (synchObject)
840 {
841 if (baseUnits is null)
842 Search();
843
844 bool HasNonReference = false;
845
846 foreach (UnitFactor Factor in this.factors)
847 {
848 if (!referenceUnits.ContainsKey(Factor.Unit.Name))
849 {
850 HasNonReference = true;
851 break;
852 }
853 }
854
855 if (HasNonReference)
856 {
857 ChunkedList<UnitFactor> ReferenceFactors = new ChunkedList<UnitFactor>();
858 int Exponent = Prefixes.PrefixToExponent(this.prefix);
859 int FactorExponent;
860 string Name;
861
862 foreach (UnitFactor Factor in this.factors)
863 {
864 FactorExponent = Factor.Exponent;
865
866 if (referenceUnits.ContainsKey(Name = Factor.Unit.Name))
867 this.Add(ReferenceFactors, Factor.Unit, Factor.Exponent);
868 else if (baseUnits.TryGetValue(Name, out IBaseQuantity BaseQuantity))
869 {
870 if (BaseQuantity.ToReferenceUnit(ref Magnitude, ref NrDecimals, Name, FactorExponent))
871 this.Add(ReferenceFactors, BaseQuantity.ReferenceUnit, FactorExponent);
872 else
873 this.Add(ReferenceFactors, Factor.Unit, Factor.Exponent);
874 }
875 else if (derivedUnits.TryGetValue(Name, out PhysicalQuantity Quantity))
876 {
877 if (FactorExponent != 0)
878 {
879 double k = Math.Pow(Quantity.Magnitude, FactorExponent);
880 Magnitude *= k;
881 NrDecimals -= Math.Log10(k);
882 Exponent += Prefixes.PrefixToExponent(Quantity.Unit.prefix) * FactorExponent;
883 }
884
885 foreach (UnitFactor Segment in Quantity.Unit.factors)
886 {
887 if (referenceUnits.ContainsKey(Name = Segment.Unit.Name))
888 this.Add(ReferenceFactors, Segment.Unit, Segment.Exponent * FactorExponent);
889 else if (baseUnits.TryGetValue(Name, out BaseQuantity))
890 {
891 if (BaseQuantity.ToReferenceUnit(ref Magnitude, ref NrDecimals, Name, Segment.Exponent * FactorExponent))
892 this.Add(ReferenceFactors, BaseQuantity.ReferenceUnit, Segment.Exponent * FactorExponent);
893 else
894 this.Add(ReferenceFactors, Segment.Unit, Segment.Exponent * FactorExponent);
895 }
896 else
897 this.Add(ReferenceFactors, Segment.Unit, Segment.Exponent * FactorExponent);
898 }
899 }
900 else if (compoundUnits.TryGetValue(Name, out KeyValuePair<Prefix, UnitFactor[]> Units))
901 {
902 Exponent += Prefixes.PrefixToExponent(Units.Key) * FactorExponent;
903
904 foreach (UnitFactor Segment in Units.Value)
905 {
906 if (referenceUnits.ContainsKey(Name = Segment.Unit.Name))
907 this.Add(ReferenceFactors, Segment.Unit, Segment.Exponent * FactorExponent);
908 else if (baseUnits.TryGetValue(Name, out BaseQuantity))
909 {
910 if (BaseQuantity.ToReferenceUnit(ref Magnitude, ref NrDecimals, Name, Segment.Exponent * FactorExponent))
911 this.Add(ReferenceFactors, BaseQuantity.ReferenceUnit, Segment.Exponent * FactorExponent);
912 else
913 this.Add(ReferenceFactors, Segment.Unit, Segment.Exponent * FactorExponent);
914 }
915 else
916 this.Add(ReferenceFactors, Segment.Unit, Segment.Exponent * FactorExponent);
917 }
918 }
919 else
920 this.Add(ReferenceFactors, Factor.Unit, Factor.Exponent);
921 }
922
923 Unit Result = new Unit(Prefixes.ExponentToPrefix(Exponent, out FactorExponent), ReferenceFactors);
924 if (FactorExponent != 0)
925 {
926 Magnitude *= Math.Pow(10, FactorExponent);
927 NrDecimals -= FactorExponent;
928 }
929
930 Result.hasBaseUnits = true;
931 Result.hasReferenceUnits = true;
932
933 return Result;
934 }
935 else
936 {
937 this.hasBaseUnits = true;
938 this.hasReferenceUnits = true;
939 return this;
940 }
941 }
942 }
943
949 public Unit FromReferenceUnits(ref double Magnitude)
950 {
951 double x = 0;
952 return this.FromReferenceUnits(ref Magnitude, ref x);
953 }
954
961 public Unit FromReferenceUnits(ref double Magnitude, ref double NrDecimals)
962 {
963 if (this.hasReferenceUnits)
964 return this;
965
966 lock (synchObject)
967 {
968 if (baseUnits is null)
969 Search();
970
971 bool HasNonReference = false;
972
973 foreach (UnitFactor Factor in this.factors)
974 {
975 if (!referenceUnits.ContainsKey(Factor.Unit.Name))
976 {
977 HasNonReference = true;
978 break;
979 }
980 }
981
982 if (HasNonReference)
983 {
984 ChunkedList<UnitFactor> ReferenceFactors = new ChunkedList<UnitFactor>();
985 int Exponent = Prefixes.PrefixToExponent(this.prefix);
986 int FactorExponent;
987 string Name;
988
989 foreach (UnitFactor Factor in this.factors)
990 {
991 FactorExponent = Factor.Exponent;
992
993 if (referenceUnits.ContainsKey(Name = Factor.Unit.Name))
994 this.Add(ReferenceFactors, Factor.Unit, Factor.Exponent);
995 else if (baseUnits.TryGetValue(Name, out IBaseQuantity BaseQuantity))
996 {
997 if (BaseQuantity.FromReferenceUnit(ref Magnitude, ref NrDecimals, Name, FactorExponent))
998 this.Add(ReferenceFactors, BaseQuantity.ReferenceUnit, FactorExponent);
999 else
1000 this.Add(ReferenceFactors, Factor.Unit, Factor.Exponent);
1001 }
1002 else if (derivedUnits.TryGetValue(Name, out PhysicalQuantity Quantity))
1003 {
1004 if (FactorExponent != 0)
1005 {
1006 double k = Math.Pow(Quantity.Magnitude, FactorExponent);
1007 Magnitude /= k;
1008 NrDecimals += Math.Log10(k);
1009 Exponent += Prefixes.PrefixToExponent(Quantity.Unit.prefix) * FactorExponent;
1010 }
1011
1012 foreach (UnitFactor Segment in Quantity.Unit.factors)
1013 {
1014 if (referenceUnits.ContainsKey(Name = Segment.Unit.Name))
1015 this.Add(ReferenceFactors, Segment.Unit, Segment.Exponent * FactorExponent);
1016 else if (baseUnits.TryGetValue(Name, out BaseQuantity))
1017 {
1018 if (BaseQuantity.FromReferenceUnit(ref Magnitude, ref NrDecimals, Name, Segment.Exponent * FactorExponent))
1019 this.Add(ReferenceFactors, BaseQuantity.ReferenceUnit, Segment.Exponent * FactorExponent);
1020 else
1021 this.Add(ReferenceFactors, Segment.Unit, Segment.Exponent * FactorExponent);
1022 }
1023 else
1024 this.Add(ReferenceFactors, Segment.Unit, Segment.Exponent * FactorExponent);
1025 }
1026 }
1027 else if (compoundUnits.TryGetValue(Name, out KeyValuePair<Prefix, UnitFactor[]> Units))
1028 {
1029 Exponent += Prefixes.PrefixToExponent(Units.Key) * FactorExponent;
1030
1031 foreach (UnitFactor Segment in Units.Value)
1032 {
1033 if (referenceUnits.ContainsKey(Name = Segment.Unit.Name))
1034 this.Add(ReferenceFactors, Segment.Unit, Segment.Exponent * FactorExponent);
1035 else if (baseUnits.TryGetValue(Name, out BaseQuantity))
1036 {
1037 if (BaseQuantity.FromReferenceUnit(ref Magnitude, ref NrDecimals, Name, Segment.Exponent * FactorExponent))
1038 this.Add(ReferenceFactors, BaseQuantity.ReferenceUnit, Segment.Exponent * FactorExponent);
1039 else
1040 this.Add(ReferenceFactors, Segment.Unit, Segment.Exponent * FactorExponent);
1041 }
1042 else
1043 this.Add(ReferenceFactors, Segment.Unit, Segment.Exponent * FactorExponent);
1044 }
1045 }
1046 else
1047 this.Add(ReferenceFactors, Factor.Unit, Factor.Exponent);
1048 }
1049
1050 Unit Result = new Unit(Prefixes.ExponentToPrefix(Exponent, out FactorExponent), ReferenceFactors);
1051 if (FactorExponent != 0)
1052 {
1053 Magnitude *= Math.Pow(10, FactorExponent);
1054 NrDecimals -= FactorExponent;
1055 }
1056
1057 Result.hasBaseUnits = true;
1058 Result.hasReferenceUnits = true;
1059
1060 return Result;
1061 }
1062 else
1063 {
1064 this.hasBaseUnits = true;
1065 this.hasReferenceUnits = true;
1066 return this;
1067 }
1068 }
1069 }
1070
1071 private static void Search()
1072 {
1073 Dictionary<string, IBaseQuantity> BaseUnits = new Dictionary<string, IBaseQuantity>();
1074 Dictionary<string, IBaseQuantity> ReferenceUnits = new Dictionary<string, IBaseQuantity>();
1075 Dictionary<string, KeyValuePair<Prefix, UnitFactor[]>> CompoundUnits = new Dictionary<string, KeyValuePair<Prefix, UnitFactor[]>>();
1076 Dictionary<string, PhysicalQuantity> DerivedUnits = new Dictionary<string, PhysicalQuantity>();
1077 IBaseQuantity BaseQuantity;
1078 ICompoundQuantity CompoundQuantity;
1079 IDerivedQuantity DerivedQuantity;
1080
1081 foreach (Type Type in Types.GetTypesImplementingInterface(typeof(IBaseQuantity)))
1082 {
1083 ConstructorInfo CI = Types.GetDefaultConstructor(Type);
1084 if (CI is null)
1085 continue;
1086
1087 try
1088 {
1089 BaseQuantity = (IBaseQuantity)CI.Invoke(Types.NoParameters);
1090 }
1091 catch (Exception ex)
1092 {
1093 Log.Exception(ex);
1094 continue;
1095 }
1096
1097 ReferenceUnits[BaseQuantity.ReferenceUnit.Name] = BaseQuantity;
1098
1099 foreach (string Unit in BaseQuantity.BaseUnits)
1100 BaseUnits[Unit] = BaseQuantity;
1101 }
1102
1103 foreach (Type Type in Types.GetTypesImplementingInterface(typeof(ICompoundQuantity)))
1104 {
1105 ConstructorInfo CI = Types.GetDefaultConstructor(Type);
1106 if (CI is null)
1107 continue;
1108
1109 try
1110 {
1111 CompoundQuantity = (ICompoundQuantity)CI.Invoke(Types.NoParameters);
1112 }
1113 catch (Exception ex)
1114 {
1115 Log.Exception(ex);
1116 continue;
1117 }
1118
1119 foreach (Tuple<string, Prefix, UnitFactor[]> CompoundUnit in CompoundQuantity.CompoundQuantities)
1120 CompoundUnits[CompoundUnit.Item1] = new KeyValuePair<Prefix, UnitFactor[]>(CompoundUnit.Item2, CompoundUnit.Item3);
1121 }
1122
1123 foreach (Type Type in Types.GetTypesImplementingInterface(typeof(IDerivedQuantity)))
1124 {
1125 ConstructorInfo CI = Types.GetDefaultConstructor(Type);
1126 if (CI is null)
1127 continue;
1128
1129 try
1130 {
1131 DerivedQuantity = (IDerivedQuantity)CI.Invoke(Types.NoParameters);
1132 }
1133 catch (Exception ex)
1134 {
1135 Log.Exception(ex);
1136 continue;
1137 }
1138
1139 foreach (KeyValuePair<string, PhysicalQuantity> Derived in DerivedQuantity.DerivedUnits)
1140 DerivedUnits[Derived.Key] = Derived.Value;
1141 }
1142
1143 baseUnits = BaseUnits;
1144 referenceUnits = ReferenceUnits;
1145 compoundUnits = CompoundUnits;
1146 derivedUnits = DerivedUnits;
1147 }
1148
1149 private readonly static Dictionary<string, IUnitCategory> categoryPerUnit = new Dictionary<string, IUnitCategory>();
1150 private static Dictionary<string, IBaseQuantity> baseUnits = null;
1151 private static Dictionary<string, IBaseQuantity> referenceUnits = null;
1152 private static Dictionary<string, KeyValuePair<Prefix, UnitFactor[]>> compoundUnits = null;
1153 private static Dictionary<string, PhysicalQuantity> derivedUnits = null;
1154 private static IUnitCategory[] unitCategories = null;
1155 private static readonly object synchObject = new object();
1156
1157 static Unit()
1158 {
1159 Types.OnInvalidated += Types_OnInvalidated;
1160 }
1161
1162 private static void Types_OnInvalidated(object Sender, EventArgs e)
1163 {
1164 lock (synchObject)
1165 {
1166 baseUnits = null;
1167 referenceUnits = null;
1168 compoundUnits = null;
1169 derivedUnits = null;
1170 unitCategories = null;
1171 categoryPerUnit.Clear();
1172 }
1173 }
1174
1181 internal static bool TryGetCompoundUnit(string Name, out KeyValuePair<Prefix, UnitFactor[]> Factors)
1182 {
1183 lock (synchObject)
1184 {
1185 if (compoundUnits is null)
1186 Search();
1187
1188 return compoundUnits.TryGetValue(Name, out Factors);
1189 }
1190 }
1191
1197 internal static bool ContainsDerivedOrBaseUnit(string Name)
1198 {
1199 lock (synchObject)
1200 {
1201 if (baseUnits is null)
1202 Search();
1203
1204 return baseUnits.ContainsKey(Name) || derivedUnits.ContainsKey(Name);
1205 }
1206 }
1207
1216 public static bool TryConvert(double From, Unit FromUnit, Unit ToUnit, out double To)
1217 {
1218 return TryConvert(From, FromUnit, 0, ToUnit, out To, out _);
1219 }
1220
1231 public static bool TryConvert(double From, Unit FromUnit, byte FromNrDec, Unit ToUnit, out double To, out byte ToNrDec)
1232 {
1233 int ToNrDec2;
1234
1235 if (FromUnit.Equals(ToUnit, false))
1236 {
1237 To = From;
1238 ToNrDec = FromNrDec;
1239
1240 if (FromUnit.prefix != ToUnit.prefix)
1241 {
1242 int ExponentDiff = Prefixes.PrefixToExponent(FromUnit.prefix);
1243 ExponentDiff -= Prefixes.PrefixToExponent(ToUnit.prefix);
1244
1245 if (ExponentDiff != 0)
1246 {
1247 To *= Math.Pow(10, ExponentDiff);
1248 ToNrDec2 = ToNrDec - ExponentDiff;
1249 if (ToNrDec2 < 0)
1250 ToNrDec = 0;
1251 else if (ToNrDec2 > 255)
1252 ToNrDec = 255;
1253 else
1254 ToNrDec = (byte)ToNrDec2;
1255 }
1256 }
1257
1258 return true;
1259 }
1260
1261 double NrDec = FromNrDec;
1262
1263 FromUnit = FromUnit.ToReferenceUnits(ref From, ref NrDec);
1264 To = From;
1265 ToUnit = ToUnit.FromReferenceUnits(ref To, ref NrDec);
1266 ToNrDec2 = (int)Math.Round(NrDec);
1267
1268 Unit Div = Divide(FromUnit, ToUnit, out int Exponent);
1269 Exponent += Prefixes.PrefixToExponent(Div.prefix);
1270 if (Exponent != 0)
1271 {
1272 To *= Math.Pow(10, Exponent);
1273 ToNrDec2 -= Exponent;
1274 }
1275
1276 if (ToNrDec2 < 0)
1277 ToNrDec = 0;
1278 else if (ToNrDec2 > 255)
1279 ToNrDec = 255;
1280 else
1281 ToNrDec = (byte)ToNrDec2;
1282
1283 return !Div.HasFactors;
1284 }
1285
1292 public static bool TryGetCategory(Unit Unit, out IUnitCategory Category)
1293 {
1294 string s = Unit.ToString();
1295
1296 lock (synchObject)
1297 {
1298 if (categoryPerUnit.TryGetValue(s, out Category))
1299 return !(Category is null);
1300
1301 if (unitCategories is null)
1302 {
1304
1305 foreach (Type Type in Types.GetTypesImplementingInterface(typeof(IUnitCategory)))
1306 {
1307 ConstructorInfo CI = Types.GetDefaultConstructor(Type);
1308 if (CI is null)
1309 continue;
1310
1311 try
1312 {
1313 Category = (IUnitCategory)CI.Invoke(Types.NoParameters);
1314 }
1315 catch (Exception ex)
1316 {
1317 Log.Exception(ex);
1318 continue;
1319 }
1320
1321 Categories.Add(Category);
1322 }
1323
1324 unitCategories = Categories.ToArray();
1325 }
1326 }
1327
1328 Category = null;
1329
1330 foreach (IUnitCategory Category2 in unitCategories)
1331 {
1332 if (TryConvert(1, Unit, Category2.Reference, out double _))
1333 {
1334 Category = Category2;
1335 break;
1336 }
1337 }
1338
1339 lock (synchObject)
1340 {
1341 categoryPerUnit[s] = Category;
1342 }
1343
1344 return !(Category is null);
1345 }
1346
1347 }
1348}
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:14
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Definition: Log.cs:1657
A chunked list is a linked list of chunks of objects of type T .
Definition: ChunkedList.cs:54
void AddRange(IEnumerable< T > Collection)
Adds a range of elements (last) to the list.
bool Update(UpdateCallback< T > Callback)
Iterates through all elements in the collection, and calls the callback method in Callback for each ...
Definition: ChunkedList.cs:640
void Add(T Item)
Adds an item to the collection.
Definition: ChunkedList.cs:272
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:15
static object[] NoParameters
Contains an empty array of parameter values.
Definition: Types.cs:572
static Type[] GetTypesImplementingInterface(string InterfaceFullName)
Gets all types implementing a given interface.
Definition: Types.cs:85
static ConstructorInfo GetDefaultConstructor(Type Type)
Gets the default constructor of a type, if one exists.
Definition: Types.cs:1742
Class managing a script expression.
Definition: Expression.cs:41
Represents an atomic unit.
Definition: AtomicUnit.cs:7
override int GetHashCode()
Definition: AtomicUnit.cs:34
Static class managing units.
Definition: Prefixes.cs:122
static Prefix ExponentToPrefix(int Exponent, out int ResidueExponent)
Converts an exponent to a prefix.
Definition: Prefixes.cs:150
static int PrefixToExponent(Prefix Prefix)
Conerts a prefix to an exponent.
Definition: Prefixes.cs:138
static string ToString(Prefix Prefix)
Converts a prefix to its string representation.
Definition: Prefixes.cs:411
A unit factor, used to form compound units.
Definition: UnitFactor.cs:7
int Exponent
Exponent of the unit factor.
Definition: UnitFactor.cs:58
AtomicUnit Unit
Unit factor, without its exponent.
Definition: UnitFactor.cs:53
Represents a unit.
Definition: Unit.cs:16
static bool TryConvert(double From, Unit FromUnit, byte FromNrDec, Unit ToUnit, out double To, out byte ToNrDec)
Tries to convert a magnitude in one unit to a magnitude in another.
Definition: Unit.cs:1231
Unit(Prefix Prefix, params AtomicUnit[] AtomicUnits)
Represents a unit.
Definition: Unit.cs:58
ICollection< UnitFactor > Factors
Sequence of atomic unit factors, and their corresponding exponents.
Definition: Unit.cs:427
static bool TryConvert(double From, Unit FromUnit, Unit ToUnit, out double To)
Tries to convert a magnitude in one unit to a magnitude in another.
Definition: Unit.cs:1216
Unit FromReferenceUnits(ref double Magnitude, ref double NrDecimals)
Converts the unit to a series of reference unit factors. (Unrecognized units will be assumed to be re...
Definition: Unit.cs:961
static Unit Divide(Unit Left, Unit Right, out int ResidueExponent)
Divides the right unit from the left unit: Left /Right .
Definition: Unit.cs:681
Unit ToReferenceUnits(ref double Magnitude, ref double NrDecimals)
Converts the unit to a series of reference unit factors. (Unrecognized units will be assumed to be re...
Definition: Unit.cs:834
override string ToString()
Definition: Unit.cs:535
static Unit Parse(string UnitString)
Parses a unit string.
Definition: Unit.cs:106
Unit(params string[] AtomicUnits)
Represents a unit.
Definition: Unit.cs:78
bool IsEmpty
If the unit is empty. (A unit of only a prefix, but no factors, is not empty.)
Definition: Unit.cs:433
Unit Invert(out int ResidueExponent)
Inverts the unit.
Definition: Unit.cs:462
Unit(Prefix Prefix, params string[] AtomicUnits)
Represents a unit.
Definition: Unit.cs:88
override bool Equals(object obj)
Definition: Unit.cs:474
bool HasFactors
If the unit has any factors.
Definition: Unit.cs:444
Unit(Prefix Prefix, params UnitFactor[] Factors)
Represents a unit.
Definition: Unit.cs:38
Unit(Prefix Prefix, ICollection< UnitFactor > Factors)
Represents a unit.
Definition: Unit.cs:27
bool Equals(Unit Unit2, bool CheckPrefix)
Checks if the unit is equal to another
Definition: Unit.cs:488
static readonly Unit Empty
Empty unit.
Definition: Unit.cs:454
Unit FromReferenceUnits(ref double Magnitude)
Converts the unit to a series of reference unit factors. (Unrecognized units will be assumed to be re...
Definition: Unit.cs:949
string ToString(bool IncludePrefix)
Converts the unit to a string.
Definition: Unit.cs:544
static Unit Multiply(Unit Left, Unit Right, out int ResidueExponent)
Multiplies two units with each other.
Definition: Unit.cs:635
override int GetHashCode()
Definition: Unit.cs:524
Unit ToReferenceUnits(ref double Magnitude)
Converts the unit to a series of reference unit factors. (Unrecognized units will be assumed to be re...
Definition: Unit.cs:822
static bool TryParse(string UnitString, out Unit Unit)
Tries to parse a string into a unit.
Definition: Unit.cs:120
static bool TryGetCategory(Unit Unit, out IUnitCategory Category)
Tries to get the unit category of a unit.
Definition: Unit.cs:1292
Unit(params AtomicUnit[] AtomicUnits)
Represents a unit.
Definition: Unit.cs:48
Unit ToBaseUnits(ref double Magnitude)
Converts the unit to a series of base unit factors. (Unrecognized units will be assumed to be base un...
Definition: Unit.cs:725
Prefix Prefix
Associated prefix.
Definition: Unit.cs:422
Interface for physical base quantities
Definition: IBaseQuantity.cs:7
string[] BaseUnits
Base Units supported.
AtomicUnit ReferenceUnit
Reference unit of category.
Interface for physical compound quantities
Tuple< string, Prefix, UnitFactor[]>[] CompoundQuantities
Compound quantities. Must only use base quantity units.
Interface for derived quantities
KeyValuePair< string, PhysicalQuantity >[] DerivedUnits
Derived Units supported.
Interface for a category of units.
Definition: IUnitCategory.cs:7
Unit Reference
Reference unit for category.
Definition: ImplTypes.g.cs:58
Prefix
SI prefixes. http://physics.nist.gov/cuu/Units/prefixes.html
Definition: Prefixes.cs:11