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