PageRenderTime 58ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 2ms

/PhysMeasure/PhysicalMeasure/PhysicalMeasure.2.Classes.cs

#
C# | 7916 lines | 6243 code | 1331 blank | 342 comment | 1447 complexity | 6d0760784ae4e865085f2ea494146e65 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. /* http://physicalmeasure.codeplex.com */
  2. /* http://en.wikipedia.org/wiki/International_System_of_Units */
  3. /* http://en.wikipedia.org/wiki/Physical_quantity */
  4. /* http://en.wikipedia.org/wiki/Physical_constant */
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Linq;
  8. using System.Diagnostics;
  9. using System.Globalization;
  10. using System.Runtime.Serialization;
  11. using static PhysicalMeasure.DimensionExponentsExtension;
  12. namespace PhysicalMeasure
  13. {
  14. #region Physical Measure Classes
  15. #region Physical Measure Exceptions
  16. [Serializable]
  17. public class PhysicalUnitFormatException : FormatException
  18. {
  19. public PhysicalUnitFormatException(String message, Exception innerException)
  20. : base(message, innerException)
  21. {
  22. }
  23. protected PhysicalUnitFormatException(SerializationInfo info, StreamingContext context)
  24. : base(info, context)
  25. {
  26. }
  27. public PhysicalUnitFormatException()
  28. : this("The string argument is not in a valid physical unit format.")
  29. {
  30. }
  31. public PhysicalUnitFormatException(String message)
  32. : base(message)
  33. {
  34. }
  35. }
  36. [Serializable]
  37. public class PhysicalUnitMathException : Exception
  38. {
  39. public PhysicalUnitMathException(String message, Exception innerException)
  40. : base(message, innerException)
  41. {
  42. }
  43. protected PhysicalUnitMathException(SerializationInfo info, StreamingContext context)
  44. : base(info, context)
  45. {
  46. }
  47. public PhysicalUnitMathException()
  48. : this("The result of the math operation on the Unit argument can't be represented by this implementation of PhysicalMeasure.")
  49. {
  50. }
  51. public PhysicalUnitMathException(String message)
  52. : base(message)
  53. {
  54. }
  55. }
  56. #endregion Physical Measure Exceptions
  57. #region Dimension Exponents Classes
  58. public class DimensionExponents : IEquatable<DimensionExponents>
  59. {
  60. private SByte[] exponents;
  61. public DimensionExponents(SByte[] exponents)
  62. {
  63. this.exponents = exponents;
  64. }
  65. public override int GetHashCode()
  66. {
  67. if (exponents == null)
  68. {
  69. return base.GetHashCode();
  70. }
  71. return exponents.GetHashCode();
  72. }
  73. public override Boolean Equals(Object obj)
  74. {
  75. if (obj == null)
  76. return false;
  77. DimensionExponents DimensionExponentsObj = obj as DimensionExponents;
  78. if (DimensionExponentsObj == null)
  79. return false;
  80. else
  81. return Equals(DimensionExponentsObj);
  82. }
  83. public Boolean Equals(DimensionExponents other)
  84. {
  85. if (other == null)
  86. return false;
  87. return Equals(this.exponents, other.exponents);
  88. }
  89. }
  90. public static class DimensionExponentsExtension
  91. {
  92. public static Boolean DimensionEquals(this SByte[] exponents1, SByte[] exponents2)
  93. {
  94. Debug.Assert(exponents1 != null, "Parameter must be specified");
  95. Debug.Assert(exponents2 != null, "Parameter must be specified");
  96. if (ReferenceEquals(exponents1, exponents2))
  97. {
  98. return true;
  99. }
  100. SByte MinNoOfBaseUnits = (SByte)Math.Min(exponents1.Length, exponents2.Length);
  101. SByte MaxNoOfBaseUnits = (SByte)Math.Max(exponents1.Length, exponents2.Length);
  102. Debug.Assert(MaxNoOfBaseUnits <= Physics.NoOfBaseQuanties + 1, "Too many base units:" + MaxNoOfBaseUnits.ToString() + ". No more than " + (Physics.NoOfBaseQuanties + 1) + " expected.");
  103. Boolean equal = true;
  104. SByte i = 0;
  105. do
  106. { // Compare exponents where defined in both arrays
  107. equal = exponents1[i] == exponents2[i];
  108. i++;
  109. }
  110. while (equal && i < MinNoOfBaseUnits);
  111. // Check tail of longest array to contain only zeros
  112. while (equal && i < MaxNoOfBaseUnits)
  113. {
  114. if (exponents1.Length > exponents2.Length)
  115. {
  116. equal = exponents1[i] == 0;
  117. }
  118. else
  119. {
  120. equal = exponents2[i] == 0;
  121. }
  122. i++;
  123. }
  124. return equal;
  125. }
  126. public static Boolean IsDimensionless(this SByte[] exponents)
  127. {
  128. Debug.Assert(exponents != null, "Parameter needed");
  129. SByte NoOfBaseUnits = (SByte)exponents.Length;
  130. Debug.Assert(NoOfBaseUnits <= Physics.NoOfBaseQuanties + 1, "Too many base units:" + NoOfBaseUnits.ToString() + ". No more than " + (Physics.NoOfBaseQuanties + 1) + " expected.");
  131. Boolean isDimensionless = true;
  132. SByte i = 0;
  133. do
  134. {
  135. isDimensionless = exponents[i] == 0;
  136. i++;
  137. }
  138. while (i < NoOfBaseUnits && isDimensionless);
  139. return isDimensionless;
  140. }
  141. public static SByte NoOfDimensions(this SByte[] exponents)
  142. {
  143. Debug.Assert(exponents != null, "Parameter needed");
  144. SByte NoOfBaseUnits = (SByte)exponents.Length;
  145. Debug.Assert(NoOfBaseUnits <= Physics.NoOfBaseQuanties + 1, "Too many base units:" + NoOfBaseUnits.ToString() + ". No more than " + (Physics.NoOfBaseQuanties + 1) + " expected.");
  146. SByte noOfDimensions = 0;
  147. SByte i = 0;
  148. do
  149. {
  150. if (exponents[i] != 0)
  151. {
  152. noOfDimensions++;
  153. }
  154. i++;
  155. }
  156. while (i < NoOfBaseUnits);
  157. return noOfDimensions;
  158. }
  159. public delegate SByte CombineExponentsFunc(SByte e1, SByte e2);
  160. public static SByte SByte_Mult(SByte e1, SByte e2) => (SByte)(e1 * e2);
  161. public static SByte SByte_Div(SByte e1, SByte e2) => (SByte)(e1 / e2);
  162. public static SByte SByte_Add(SByte e1, SByte e2) => (SByte)(e1 + e2);
  163. public static SByte SByte_Sub(SByte e1, SByte e2) => (SByte)(e1 - e2);
  164. public static SByte[] CombineExponentArrays(this SByte[] exponents1, SByte[] exponents2, CombineExponentsFunc cef)
  165. {
  166. Debug.Assert(exponents1 != null, "Parameter exponents1 needed");
  167. Debug.Assert(exponents2 != null, "Parameter exponents2 needed");
  168. SByte NoOfBaseUnits1 = (SByte)exponents1.Length;
  169. SByte NoOfBaseUnits2 = (SByte)exponents2.Length;
  170. SByte MaxNoOfBaseUnits = (SByte)Math.Max(NoOfBaseUnits1, NoOfBaseUnits2);
  171. SByte MinNoOfBaseUnits = (SByte)Math.Min(NoOfBaseUnits1, NoOfBaseUnits2);
  172. Debug.Assert(NoOfBaseUnits1 <= Physics.NoOfBaseQuanties + 1, "exponents1 has too many base units:" + NoOfBaseUnits1.ToString() + ". No more than " + (Physics.NoOfBaseQuanties + 1) + " expected.");
  173. Debug.Assert(NoOfBaseUnits2 <= Physics.NoOfBaseQuanties + 1, "exponents2 has too many base units:" + NoOfBaseUnits2.ToString() + ". No more than " + (Physics.NoOfBaseQuanties + 1) + " expected.");
  174. SByte[] NewExponents = new SByte[MaxNoOfBaseUnits];
  175. SByte i = 0;
  176. do
  177. {
  178. NewExponents[i] = cef(exponents1[i], exponents2[i]);
  179. i++;
  180. }
  181. while (i < MinNoOfBaseUnits);
  182. while (i < MaxNoOfBaseUnits)
  183. {
  184. if (NoOfBaseUnits1 > NoOfBaseUnits2)
  185. {
  186. NewExponents[i] = exponents1[i];
  187. }
  188. else
  189. {
  190. NewExponents[i] = cef(0, exponents2[i]);
  191. }
  192. i++;
  193. }
  194. return NewExponents;
  195. }
  196. public static SByte[] Multiply(this SByte[] exponents1, SByte[] exponents2)
  197. {
  198. SByte[] NewExponents = CombineExponentArrays(exponents1, exponents2, SByte_Add);
  199. return NewExponents;
  200. }
  201. public static SByte[] Divide(this SByte[] exponents1, SByte[] exponents2)
  202. {
  203. SByte[] NewExponents = CombineExponentArrays(exponents1, exponents2, SByte_Sub);
  204. return NewExponents;
  205. }
  206. public static SByte[] Power(this SByte[] exponents, SByte exponent)
  207. {
  208. Debug.Assert(exponents != null, "Parameter needed");
  209. Debug.Assert(exponent != 0, "Parameter needed");
  210. SByte NoOfBaseUnits = (SByte)exponents.Length;
  211. Debug.Assert(NoOfBaseUnits <= Physics.NoOfBaseQuanties + 1, "Too many base units:" + NoOfBaseUnits.ToString() + ". No more than " + (Physics.NoOfBaseQuanties + 1) + " expected.");
  212. SByte[] NewExponents = new SByte[NoOfBaseUnits];
  213. SByte i = 0;
  214. do
  215. {
  216. NewExponents[i] = (SByte)(exponents[i] * exponent);
  217. i++;
  218. }
  219. while (i < NoOfBaseUnits);
  220. return NewExponents;
  221. }
  222. public static SByte[] Root(this SByte[] exponents, SByte exponent)
  223. {
  224. Debug.Assert(exponents != null, "Parameter needed");
  225. Debug.Assert(exponent != 0, "Parameter needed");
  226. SByte NoOfBaseUnits = (SByte)exponents.Length;
  227. Debug.Assert(NoOfBaseUnits <= Physics.NoOfBaseQuanties + 1, "Too many base units:" + NoOfBaseUnits.ToString() + ". No more than " + (Physics.NoOfBaseQuanties + 1) + " expected.");
  228. SByte[] NewExponents = new SByte[NoOfBaseUnits];
  229. SByte i = 0;
  230. Boolean OK = true;
  231. do
  232. {
  233. int Remainder;
  234. int NewExponent = Math.DivRem(exponents[i], exponent, out Remainder);
  235. OK = Remainder == 0;
  236. NewExponents[i] = (SByte)NewExponent;
  237. i++;
  238. }
  239. while (i < NoOfBaseUnits && OK);
  240. if (!OK)
  241. {
  242. Debug.Assert(OK, "Verify to not happening");
  243. //if (ThrowExceptionOnUnitMathError) {
  244. throw new PhysicalUnitMathException("The result of the math operation on the Unit argument can't be represented by this implementation of PhysicalMeasure: (" + exponents.ToString() + ").Root(" + exponent.ToString() + ")");
  245. //}
  246. //NewExponents = null;
  247. }
  248. return NewExponents;
  249. }
  250. public static SByte[] AllExponents(this SByte[] Exponents, SByte length)
  251. {
  252. SByte[] resExponents;
  253. if (Exponents.Length < length)
  254. {
  255. resExponents = new SByte[length];
  256. foreach (int i in Enumerable.Range(0, length))
  257. {
  258. resExponents[i] = Exponents[i];
  259. }
  260. }
  261. else
  262. {
  263. Debug.Assert(Exponents.Length == length);
  264. resExponents = Exponents;
  265. }
  266. return resExponents;
  267. }
  268. public static String ArrayToString(this SByte[] exponents)
  269. {
  270. String str = "[";
  271. foreach (int i in Enumerable.Range(0, exponents.Length))
  272. {
  273. if (i > 0)
  274. {
  275. str = str + ", ";
  276. }
  277. str = str + exponents[i].ToString();
  278. }
  279. str = str + "]";
  280. return str;
  281. }
  282. }
  283. #endregion Dimension Exponents Classes
  284. public class NamedObject : INamed
  285. {
  286. private readonly String name;
  287. public String Name => name;
  288. public NamedObject(String someName)
  289. {
  290. this.name = someName;
  291. }
  292. public override String ToString() => Name;
  293. }
  294. #region Physical Unit prefix Classes
  295. public class UnitPrefixExponent : IUnitPrefixExponent
  296. {
  297. private SByte exponent;
  298. public SByte Exponent => exponent;
  299. public Double Value => Math.Pow(10, exponent);
  300. public UnitPrefixExponent(SByte somePrefixExponent)
  301. {
  302. this.exponent = somePrefixExponent;
  303. }
  304. #region IUnitPrefixExponentMath implementation
  305. public IUnitPrefixExponent Multiply(IUnitPrefixExponent prefix) => new UnitPrefixExponent((SByte)(this.exponent + prefix.Exponent));
  306. public IUnitPrefixExponent Divide(IUnitPrefixExponent prefix) => new UnitPrefixExponent((SByte)(this.exponent - prefix.Exponent));
  307. public IUnitPrefixExponent Power(SByte someExponent) => new UnitPrefixExponent((SByte)(this.exponent * someExponent));
  308. public IUnitPrefixExponent Root(SByte someExponent)
  309. {
  310. SByte result_exponent = (SByte)(this.exponent / someExponent);
  311. Debug.Assert(result_exponent * someExponent == this.exponent, " Root result exponent must be an integer");
  312. return new UnitPrefixExponent(result_exponent);
  313. }
  314. #endregion IUnitPrefixExponentMath implementation
  315. public override String ToString() => Exponent.ToString();
  316. }
  317. public class UnitPrefix : NamedObject, IUnitPrefix
  318. {
  319. private IUnitPrefixTable unitPrefixTable;
  320. private Char prefixChar;
  321. IUnitPrefixExponent prefixExponent;
  322. #region IUnitPrefix implementation
  323. public Char PrefixChar => prefixChar;
  324. public SByte Exponent => prefixExponent.Exponent;
  325. public Double Value => prefixExponent.Value;
  326. #endregion IUnitPrefix implementation
  327. public UnitPrefix(IUnitPrefixTable someUnitPrefixTable, String someName, Char somePrefixChar, IUnitPrefixExponent somePrefixExponent)
  328. : base(someName)
  329. {
  330. this.unitPrefixTable = someUnitPrefixTable;
  331. this.prefixChar = somePrefixChar;
  332. this.prefixExponent = somePrefixExponent;
  333. }
  334. public UnitPrefix(IUnitPrefixTable someUnitPrefixTable, String someName, Char somePrefixChar, SByte somePrefixExponent)
  335. : this(someUnitPrefixTable, someName, somePrefixChar, new UnitPrefixExponent(somePrefixExponent))
  336. {
  337. }
  338. public IUnitPrefix Multiply(IUnitPrefix prefix)
  339. {
  340. IUnitPrefix unitPrefix = null;
  341. IUnitPrefixExponent resultExponent = this.prefixExponent.Multiply(prefix);
  342. if (!unitPrefixTable.GetUnitPrefixFromExponent(resultExponent, out unitPrefix))
  343. {
  344. unitPrefix = new UnitPrefix(null, null, '\0', resultExponent);
  345. }
  346. return unitPrefix;
  347. }
  348. public IUnitPrefix Divide(IUnitPrefix prefix)
  349. {
  350. IUnitPrefix unitPrefix = null;
  351. IUnitPrefixExponent resultExponent = this.prefixExponent.Divide(prefix);
  352. if (!unitPrefixTable.GetUnitPrefixFromExponent(resultExponent, out unitPrefix))
  353. {
  354. unitPrefix = new UnitPrefix(null, null, '\0', resultExponent);
  355. }
  356. return unitPrefix;
  357. }
  358. #region IUnitPrefixExponentMath implementation
  359. public IUnitPrefixExponent Multiply(IUnitPrefixExponent prefix)
  360. {
  361. IUnitPrefix unitPrefix = null;
  362. IUnitPrefixExponent resultExponent = this.prefixExponent.Multiply(prefix);
  363. if (!unitPrefixTable.GetUnitPrefixFromExponent(resultExponent, out unitPrefix))
  364. {
  365. return resultExponent;
  366. }
  367. return unitPrefix;
  368. }
  369. public IUnitPrefixExponent Divide(IUnitPrefixExponent prefix)
  370. {
  371. IUnitPrefix unitPrefix = null;
  372. IUnitPrefixExponent resultExponent = this.prefixExponent.Divide(prefix);
  373. if (!unitPrefixTable.GetUnitPrefixFromExponent(resultExponent, out unitPrefix))
  374. {
  375. return resultExponent;
  376. }
  377. return unitPrefix;
  378. }
  379. public IUnitPrefixExponent Power(SByte someExponent)
  380. {
  381. IUnitPrefix unitPrefix = null;
  382. IUnitPrefixExponent resultExponent = this.prefixExponent.Power(someExponent);
  383. if (!unitPrefixTable.GetUnitPrefixFromExponent(resultExponent, out unitPrefix))
  384. {
  385. return resultExponent;
  386. }
  387. return unitPrefix;
  388. }
  389. public IUnitPrefixExponent Root(SByte someExponent)
  390. {
  391. IUnitPrefix unitPrefix = null;
  392. IUnitPrefixExponent resultExponent = this.prefixExponent.Root(someExponent);
  393. if (!unitPrefixTable.GetUnitPrefixFromExponent(resultExponent, out unitPrefix))
  394. {
  395. return resultExponent;
  396. }
  397. return unitPrefix;
  398. }
  399. public IPrefixedUnit Multiply(INamedSymbolUnit symbolUnit) => new PrefixedUnit(this, symbolUnit);
  400. #endregion IUnitPrefixExponentMath implementation
  401. public override String ToString() => PrefixChar.ToString();
  402. }
  403. public class UnitPrefixTable : IUnitPrefixTable
  404. {
  405. private readonly UnitPrefix[] unitPrefixes;
  406. public UnitPrefix[] UnitPrefixes => unitPrefixes;
  407. public UnitPrefixTable(UnitPrefix[] someUnitPrefix)
  408. {
  409. this.unitPrefixes = someUnitPrefix;
  410. }
  411. public Boolean GetUnitPrefixFromExponent(IUnitPrefixExponent someExponent, out IUnitPrefix unitPrefix)
  412. {
  413. Debug.Assert(someExponent.Exponent != 0);
  414. IUnitPrefix TempUnitPrefix;
  415. SByte ScaleFactorExponent;
  416. GetFloorUnitPrefixAndScaleFactorFromExponent(someExponent.Exponent, out TempUnitPrefix, out ScaleFactorExponent);
  417. if (ScaleFactorExponent == 0)
  418. {
  419. unitPrefix = TempUnitPrefix;
  420. return true;
  421. }
  422. else
  423. {
  424. unitPrefix = null;
  425. return false;
  426. }
  427. }
  428. public void GetFloorUnitPrefixAndScaleFactorFromExponent(SByte someExponent, out IUnitPrefix unitPrefix, out SByte ScaleFactorExponent)
  429. {
  430. Debug.Assert(someExponent != 0);
  431. int UnitPrefix = 11; // 10^1
  432. while (UnitPrefix - 1 >= 0 && UnitPrefixes[UnitPrefix - 1].Exponent <= someExponent)
  433. {
  434. UnitPrefix--;
  435. }
  436. while (UnitPrefix + 1 < UnitPrefixes.Length && UnitPrefixes[UnitPrefix + 1].Exponent >= someExponent)
  437. {
  438. UnitPrefix++;
  439. }
  440. unitPrefix = UnitPrefixes[UnitPrefix];
  441. ScaleFactorExponent = (SByte)(someExponent - unitPrefix.Exponent);
  442. }
  443. public Boolean GetPrefixCharFromExponent(IUnitPrefixExponent someExponent, out Char prefixChar)
  444. {
  445. prefixChar = '\0';
  446. foreach (UnitPrefix us in UnitPrefixes)
  447. {
  448. if (us.Exponent == someExponent.Exponent)
  449. {
  450. prefixChar = us.PrefixChar;
  451. return true;
  452. }
  453. }
  454. return false;
  455. }
  456. public Boolean GetUnitPrefixFromPrefixChar(Char somePrefixChar, out IUnitPrefix unitPrefix)
  457. {
  458. switch (somePrefixChar)
  459. {
  460. case '\x03BC':
  461. // 'μ' // '\0x03BC' (char)956
  462. // 'µ' // '\0x00B5' (char)181
  463. somePrefixChar = 'µ'; // 'µ' MICRO SIGN '\0x00B5' (char)181
  464. break;
  465. case 'k':
  466. somePrefixChar = 'K'; // Kilo
  467. break;
  468. case 'h':
  469. somePrefixChar = 'H'; // Hecto
  470. break;
  471. }
  472. foreach (UnitPrefix up in UnitPrefixes)
  473. {
  474. if (up.PrefixChar == somePrefixChar)
  475. {
  476. unitPrefix = up;
  477. return true;
  478. }
  479. }
  480. unitPrefix = null;
  481. return false;
  482. }
  483. public Boolean GetExponentFromPrefixChar(Char somePrefixChar, out IUnitPrefixExponent exponent)
  484. {
  485. switch (somePrefixChar)
  486. {
  487. case '\x03BC':
  488. // 'μ' // '\0x03BC' (Char)956
  489. // 'µ' // '\0x00B5' (Char)181
  490. somePrefixChar = 'µ'; // 'µ' MICRO SIGN '\0x00B5' (Char)181
  491. break;
  492. case 'k':
  493. somePrefixChar = 'K'; // Kilo
  494. break;
  495. case 'h':
  496. somePrefixChar = 'H'; // Hecto
  497. break;
  498. }
  499. foreach (UnitPrefix up in UnitPrefixes)
  500. {
  501. if (up.PrefixChar == somePrefixChar)
  502. {
  503. exponent = up;
  504. return true;
  505. }
  506. }
  507. exponent = null;
  508. return false;
  509. }
  510. public IUnitPrefix UnitPrefixFromPrefixChar(char somePrefixChar)
  511. {
  512. IUnitPrefix unitPrefix = null;
  513. GetUnitPrefixFromPrefixChar(somePrefixChar, out unitPrefix);
  514. return unitPrefix;
  515. }
  516. public IUnitPrefixExponent ExponentFromPrefixChar(char somePrefixChar)
  517. {
  518. IUnitPrefixExponent exponent = null;
  519. GetExponentFromPrefixChar(somePrefixChar, out exponent);
  520. return exponent;
  521. }
  522. public IUnitPrefix this[char somePrefixChar] => UnitPrefixFromPrefixChar(somePrefixChar);
  523. }
  524. #endregion Physical Unit prefix Classes
  525. #region Value Conversion Classes
  526. public abstract class ValueConversion : IValueConversion
  527. {
  528. // Specific/absolute quantity unit conversion (e.g. specific temperature)
  529. // Conversion value is specified. Must assume Specific conversion e.g. specific temperature.
  530. public Double Convert(Double value, Boolean backwards = false)
  531. {
  532. if (backwards)
  533. {
  534. return ConvertToPrimaryUnit(value);
  535. }
  536. else
  537. {
  538. return ConvertFromPrimaryUnit(value);
  539. }
  540. }
  541. public abstract Double ConvertFromPrimaryUnit(Double value);
  542. public abstract Double ConvertToPrimaryUnit(Double value);
  543. // No Conversion value is specified. Must assume relative conversion e.g. temperature interval.
  544. public Double Convert(Boolean backwards = false)
  545. {
  546. if (backwards)
  547. {
  548. return ConvertToPrimaryUnit();
  549. }
  550. else
  551. {
  552. return ConvertFromPrimaryUnit();
  553. }
  554. }
  555. public abstract Double ConvertFromPrimaryUnit();
  556. public abstract Double ConvertToPrimaryUnit();
  557. public abstract Double LinearOffset { get; }
  558. public abstract Double LinearScale { get; }
  559. }
  560. public class LinearValueConversion : ValueConversion
  561. {
  562. private Double offset;
  563. private Double scale;
  564. public Double Offset
  565. {
  566. get { return offset; }
  567. set { offset = value; }
  568. }
  569. public Double Scale
  570. {
  571. get { return scale; }
  572. set { scale = value; }
  573. }
  574. public override Double LinearOffset => offset;
  575. public override Double LinearScale => scale;
  576. public LinearValueConversion(Double someOffset, Double someScale)
  577. {
  578. this.Offset = someOffset;
  579. this.Scale = someScale;
  580. }
  581. // Specific/absolute quantity unit conversion (e.g. specific temperature)
  582. // Conversion value is specified. Must assume Specific conversion e.g. specific temperature.
  583. public override Double ConvertFromPrimaryUnit(Double value) => (value * this.Scale) + this.Offset;
  584. public override Double ConvertToPrimaryUnit(Double value)
  585. {
  586. Double convertedValue = (value - this.Offset) / this.Scale;
  587. return convertedValue;
  588. }
  589. // Unspecific/relative non-quantity unit conversion (e.g. temperature interval)
  590. // No Conversion value is specified. Must assume relative conversion e.g. temperature interval.
  591. public override Double ConvertFromPrimaryUnit() => 1.0d * this.Scale;
  592. public override Double ConvertToPrimaryUnit() => 1.0d / this.Scale;
  593. }
  594. public class ScaledValueConversion : LinearValueConversion
  595. {
  596. public ScaledValueConversion(Double someScale)
  597. : base(0, someScale)
  598. {
  599. Debug.Assert(someScale != 0, "Parameter needed");
  600. Debug.Assert(!Double.IsInfinity(someScale), "Finite scale value needed");
  601. if (someScale == 0)
  602. {
  603. throw new ArgumentException("0 is not a valid scale", nameof(someScale));
  604. }
  605. if (Double.IsInfinity(someScale))
  606. {
  607. throw new ArgumentException("Infinity is not a valid scale", nameof(someScale));
  608. }
  609. }
  610. }
  611. public class IdentityValueConversion : ScaledValueConversion
  612. {
  613. public IdentityValueConversion()
  614. : base(1)
  615. {
  616. }
  617. }
  618. public class CombinedValueConversion : ValueConversion
  619. {
  620. private IValueConversion firstValueConversion;
  621. private IValueConversion secondValueConversion;
  622. private Boolean firstValueConversionDirectionInverted;
  623. private Boolean secondValueConversionDirectionInverted;
  624. public override Double LinearOffset => firstValueConversion.LinearOffset + secondValueConversion.LinearOffset;
  625. public override Double LinearScale => firstValueConversion.LinearScale * secondValueConversion.LinearScale;
  626. public CombinedValueConversion(IValueConversion firstValueConversion, Boolean firstValueConversionDirectionInverted, IValueConversion secondValueConversion, Boolean secondValueConversionDirectionInverted)
  627. {
  628. this.firstValueConversion = firstValueConversion;
  629. this.firstValueConversionDirectionInverted = firstValueConversionDirectionInverted;
  630. this.secondValueConversion = secondValueConversion;
  631. this.secondValueConversionDirectionInverted = secondValueConversionDirectionInverted;
  632. }
  633. // Specific/absolute quantity unit conversion (e.g. specific temperature)
  634. // Conversion value is specified. Must assume Specific conversion e.g. specific temperature.
  635. public override Double ConvertFromPrimaryUnit(Double value) => this.secondValueConversion.Convert(this.firstValueConversion.Convert(value, this.firstValueConversionDirectionInverted), this.secondValueConversionDirectionInverted);
  636. public override Double ConvertToPrimaryUnit(Double value) => this.firstValueConversion.Convert(this.secondValueConversion.Convert(value, !this.secondValueConversionDirectionInverted), !this.firstValueConversionDirectionInverted);
  637. // Unspecific/relative non-quantity unit conversion (e.g. temperature interval)
  638. // No Conversion value is specified. Must assume relative conversion e.g. temperature interval.
  639. public override Double ConvertFromPrimaryUnit() => this.secondValueConversion.Convert(this.firstValueConversion.Convert(this.firstValueConversionDirectionInverted), this.secondValueConversionDirectionInverted);
  640. public override Double ConvertToPrimaryUnit() => this.firstValueConversion.Convert(this.secondValueConversion.Convert(!this.secondValueConversionDirectionInverted), !this.firstValueConversionDirectionInverted);
  641. }
  642. #endregion Value Conversion Classes
  643. #region Physical Unit Classes
  644. public class NamedSymbol : NamedObject, INamedSymbol
  645. {
  646. private String symbol;
  647. public String Symbol { get { return symbol; } set { symbol = value; } }
  648. public NamedSymbol(String someName, String someSymbol)
  649. : base(someName)
  650. {
  651. this.Symbol = someSymbol;
  652. }
  653. }
  654. public abstract class Unit : ISystemItem, IUnit /* <BaseUnit | DerivedUnit | ConvertibleUnit | CombinedUnit> */
  655. {
  656. protected Unit()
  657. {
  658. }
  659. public static IUnit MakePhysicalUnit(SByte[] exponents, Double ConversionFactor = 1, Double ConversionOffset = 0)
  660. {
  661. return MakePhysicalUnit(Physics.SI_Units, exponents, ConversionFactor, ConversionOffset);
  662. }
  663. public static Unit MakePhysicalUnit(IUnitSystem system, SByte[] exponents, Double ConversionFactor = 1, Double ConversionOffset = 0)
  664. {
  665. Unit res_unit = null;
  666. int nod = exponents.NoOfDimensions();
  667. if (nod == 0)
  668. {
  669. res_unit = Physics.dimensionless;
  670. }
  671. else
  672. {
  673. res_unit = system.UnitFromExponents(exponents);
  674. }
  675. if (ConversionFactor != 1 || ConversionOffset != 0)
  676. {
  677. if (ConversionOffset == 0)
  678. {
  679. res_unit = new ConvertibleUnit(null, res_unit, new ScaledValueConversion(ConversionFactor));
  680. }
  681. else
  682. {
  683. res_unit = new ConvertibleUnit(null, res_unit, new LinearValueConversion(ConversionOffset, ConversionFactor));
  684. }
  685. }
  686. Debug.Assert(res_unit != null, "res_unit must be found");
  687. return res_unit;
  688. }
  689. public PrefixedUnitExponentList AsPrefixedUnitExponentList()
  690. {
  691. IUnitSystem us = this.ExponentsSystem;
  692. PrefixedUnitExponentList res = new PrefixedUnitExponentList(this.Exponents.Select((exp, i) =>
  693. {
  694. if (exp != 0)
  695. {
  696. return new PrefixedUnitExponent(us.BaseUnits[i], exp);
  697. }
  698. else
  699. {
  700. return null;
  701. }
  702. }));
  703. return res;
  704. }
  705. #if DEBUG
  706. // [Conditional("DEBUG")]
  707. public void TestPropertiesPrint()
  708. {
  709. Boolean test = true;
  710. if (test)
  711. {
  712. string KindStr = this.Kind.ToString();
  713. string SimpleSystemStr = SimpleSystem?.ToString();
  714. string ExponentsSystemStr = ExponentsSystem?.ToString();
  715. string ExponentsStr = Exponents?.ArrayToString();
  716. string DimensionlessStr = Dimensionless?.ToPrintString();
  717. string IsDimensionlessStr = IsDimensionless.ToString();
  718. string ThisPrintStr = this.ToPrintString();
  719. string ThisStr = this.ToString();
  720. }
  721. }
  722. #endif // DEBUG
  723. public abstract IUnitSystem SimpleSystem { get; set; }
  724. public abstract IUnitSystem ExponentsSystem { get; }
  725. public abstract UnitKind Kind { get; }
  726. public abstract SByte[] Exponents { get; }
  727. public virtual Unit Dimensionless => Physics.dimensionless;
  728. public virtual Boolean IsDimensionless
  729. {
  730. get
  731. {
  732. SByte[] exponents = Exponents;
  733. if (Exponents == null)
  734. {
  735. Debug.Assert(Exponents != null, "Exponents must be found");
  736. return false; // Maybe combined unit with Assume
  737. }
  738. return Exponents.IsDimensionless();
  739. }
  740. }
  741. public override int GetHashCode() => this.ExponentsSystem.GetHashCode() + this.Exponents.GetHashCode();
  742. #region Unit Expression parser methods
  743. /**
  744. U = U "*" F | U F | U "/" F | F .
  745. F = SUX | "(" U ")" .
  746. SUX = U | S U | U X | S U X .
  747. U = F Uopt .
  748. //Uopt = "*" F Uopt | "/" F Uopt | UX | e .
  749. Uopt = "*" F Uopt | "/" F Uopt| U | e .
  750. F = SUX | "(" U ")" .
  751. SUX = SU Xopt .
  752. SU = Sopt u .
  753. Sopt = s | e .
  754. Xopt = x | e .
  755. * s : scale prefix char
  756. * u : unit symbol
  757. * x : exponent number
  758. **/
  759. #region IPhysicalUnit unit expression parser methods
  760. /// <summary>
  761. /// Parses the physical quantity from a string in form
  762. /// [prefix] [unitSymbol]
  763. /// </summary>
  764. public static Unit Parse(String unitString)
  765. {
  766. Unit pu = null;
  767. String resultLine = null;
  768. pu = ParseUnit(ref unitString, ref resultLine, throwExceptionOnInvalidInput: true);
  769. return pu;
  770. }
  771. public static Unit ParseUnit(ref String unitString, ref String resultLine, Boolean throwExceptionOnInvalidInput = true)
  772. {
  773. Unit pu = null;
  774. Char timeSeparator = ':';
  775. Char[] separators = { timeSeparator };
  776. Char fractionUnitSeparator = '\0';
  777. String fractionUnitSeparatorStr = null;
  778. int unitStrCount = 0;
  779. int unitStrStartCharIndex = 0;
  780. int nextUnitStrStartCharIndex = 0;
  781. Boolean validFractionalUnit = true;
  782. int lastUnitFieldRemainingLen = 0;
  783. Stack<Tuple<string, Unit>> FractionalUnits = new Stack<Tuple<string, Unit>>();
  784. while (validFractionalUnit && (unitStrStartCharIndex >= 0) && (unitStrStartCharIndex < unitString.Length))
  785. {
  786. int unitStrLen;
  787. int unitStrSeparatorCharIndex = unitString.IndexOfAny(separators, unitStrStartCharIndex);
  788. if (unitStrSeparatorCharIndex == -1)
  789. {
  790. unitStrLen = unitString.Length - unitStrStartCharIndex;
  791. nextUnitStrStartCharIndex = unitString.Length;
  792. }
  793. else
  794. {
  795. unitStrLen = unitStrSeparatorCharIndex - unitStrStartCharIndex;
  796. nextUnitStrStartCharIndex = unitStrSeparatorCharIndex + 1;
  797. }
  798. if (unitStrLen > 0)
  799. {
  800. unitStrCount++;
  801. string unitFieldString = unitString.Substring(unitStrStartCharIndex, unitStrLen).Trim();
  802. Unit tempPU = ParseUnit(null, ref unitFieldString);
  803. if (tempPU == null)
  804. {
  805. validFractionalUnit = false;
  806. resultLine = "'" + unitFieldString + "' is not a valid unit.";
  807. if (throwExceptionOnInvalidInput)
  808. {
  809. throw new PhysicalUnitFormatException("The string argument unitString is not in a valid physical unit format. " + resultLine);
  810. }
  811. }
  812. else
  813. {
  814. fractionUnitSeparatorStr = fractionUnitSeparator.ToString();
  815. FractionalUnits.Push(new Tuple<string, Unit>(fractionUnitSeparatorStr, tempPU));
  816. lastUnitFieldRemainingLen = unitFieldString.Length;
  817. if (lastUnitFieldRemainingLen != 0)
  818. { // Unparsed chars in (last?) field
  819. unitStrLen -= lastUnitFieldRemainingLen;
  820. }
  821. }
  822. }
  823. // Shift to next field
  824. if (unitStrSeparatorCharIndex >= 0)
  825. {
  826. fractionUnitSeparator = unitString[unitStrSeparatorCharIndex];
  827. }
  828. unitStrStartCharIndex = nextUnitStrStartCharIndex;
  829. }
  830. unitString = unitString.Substring(nextUnitStrStartCharIndex - lastUnitFieldRemainingLen);
  831. foreach (Tuple<string, Unit> tempFU in FractionalUnits)
  832. {
  833. Unit tempPU = tempFU.Item2;
  834. String tempFractionUnitSeparator = tempFU.Item1;
  835. if (pu == null)
  836. {
  837. pu = tempPU;
  838. fractionUnitSeparatorStr = tempFractionUnitSeparator;
  839. }
  840. else
  841. {
  842. if (new Quantity(tempPU).ConvertTo(pu) != null)
  843. {
  844. Debug.Assert(fractionUnitSeparatorStr != null, "Unit separator needed");
  845. pu = new MixedUnit(tempPU, fractionUnitSeparatorStr, pu);
  846. fractionUnitSeparatorStr = tempFractionUnitSeparator;
  847. }
  848. else
  849. {
  850. Debug.Assert(resultLine == null, "No resultLine expected");
  851. resultLine = tempPU.ToPrintString() + " is not a valid fractional unit for " + pu.ToPrintString() + ".";
  852. if (throwExceptionOnInvalidInput)
  853. {
  854. throw new PhysicalUnitFormatException("The string argument is not in a valid physical unit format. " + resultLine);
  855. }
  856. }
  857. }
  858. }
  859. return pu;
  860. }
  861. // Token kind enum values
  862. public enum TokenKind
  863. {
  864. None = 0,
  865. Unit = 1,
  866. Exponent = 2,
  867. Operator = 3
  868. }
  869. // Operator kind enum values
  870. // Precedence for a group of operators is same as first (lowest) enum in the group
  871. public enum OperatorKind
  872. {
  873. None = 0,
  874. // Precedence == 2
  875. Mult = 2,
  876. Div = 3,
  877. //Precedence == 4
  878. Pow = 4,
  879. Root = 5
  880. }
  881. private static OperatorKind OperatorPrecedence(OperatorKind operatoren)
  882. {
  883. OperatorKind precedence = (OperatorKind)((int)operatoren & 0XE);
  884. return precedence;
  885. }
  886. private class Token
  887. {
  888. public readonly TokenKind TokenKind;
  889. public readonly Unit PhysicalUnit;
  890. public readonly SByte Exponent;
  891. public readonly OperatorKind Operator;
  892. public Token(Unit physicalUni)
  893. {
  894. this.TokenKind = TokenKind.Unit;
  895. this.PhysicalUnit = physicalUni;
  896. }
  897. public Token(SByte exponent)
  898. {
  899. this.TokenKind = TokenKind.Exponent;
  900. this.Exponent = exponent;
  901. }
  902. public Token(OperatorKind Operator)
  903. {
  904. this.TokenKind = TokenKind.Operator;
  905. this.Operator = Operator;
  906. }
  907. }
  908. private class ExpressionTokenizer
  909. {
  910. private String inputString;
  911. private int pos = 0;
  912. private int afterLastOperandPos = 0;
  913. private int lastValidPos = 0;
  914. private Boolean inputRecognized = true;
  915. private IUnit dimensionless = Physics.dimensionless;
  916. private Boolean throwExceptionOnInvalidInput = false;
  917. private Stack<OperatorKind> operators = new Stack<OperatorKind>();
  918. private List<Token> tokens = new List<Token>();
  919. private TokenKind lastReadToken = TokenKind.None;
  920. public ExpressionTokenizer(String inputStr)
  921. {
  922. this.inputString = inputStr;
  923. }
  924. public ExpressionTokenizer(IUnit someDimensionless, String someInputStr)
  925. {
  926. this.dimensionless = someDimensionless;
  927. this.inputString = someInputStr;
  928. }
  929. public ExpressionTokenizer(IUnit someDimensionless, Boolean someThrowExceptionOnInvalidInput, String someInputStr)
  930. {
  931. this.dimensionless = someDimensionless;
  932. this.throwExceptionOnInvalidInput = someThrowExceptionOnInvalidInput;
  933. this.inputString = someInputStr;
  934. }
  935. public string GetRemainingInput() => inputString.Substring(pos);
  936. public string GetRemainingInputForLastValidPos() => inputString.Substring(lastValidPos);
  937. public void SetValidPos()
  938. {
  939. if (operators.Count <= 1 && tokens.Count == 0)
  940. {
  941. lastValidPos = afterLastOperandPos;
  942. }
  943. }
  944. private Boolean PushNewOperator(OperatorKind newOperator)
  945. {
  946. if (lastReadToken != TokenKind.Operator)
  947. {
  948. if (operators.Count > 0)
  949. {
  950. // Pop operators with precedence higher than new operator
  951. OperatorKind precedence = OperatorPrecedence(newOperator);
  952. while ((operators.Count > 0) && (operators.Peek() >= precedence))
  953. {
  954. tokens.Add(new Token(operators.Pop()));
  955. }
  956. }
  957. operators.Push(newOperator);
  958. lastReadToken = TokenKind.Operator;
  959. return true;
  960. }
  961. else
  962. {
  963. if (throwExceptionOnInvalidInput)
  964. {
  965. throw new PhysicalUnitFormatException("The string argument is not in a valid physical unit format. Invalid or missing unit at position " + pos.ToString());
  966. }
  967. return false;
  968. }
  969. }
  970. private void HandleNewOperator(OperatorKind newOperator)
  971. { // Push newOperator and shift Pos or mark as failed
  972. if (PushNewOperator(newOperator))
  973. {
  974. pos++;
  975. }
  976. else
  977. {
  978. inputRecognized = false;
  979. }
  980. }
  981. private Token RemoveFirstToken()
  982. { // return first operator from post fix operators
  983. Token token = tokens[0];
  984. tokens.RemoveAt(0);
  985. return token;
  986. }
  987. public Token GetToken()
  988. {
  989. Debug.Assert(inputString != null, "Source needed");
  990. if (tokens.Count > 0)
  991. { // return first operator from post fix operators
  992. return RemoveFirstToken();
  993. }
  994. int OperatorsCountForRecognizedTokens = operators.Count;
  995. while ((inputString.Length > pos) && inputRecognized)
  996. {
  997. Char c = inputString[pos];
  998. if (Char.IsWhiteSpace(c))
  999. {
  1000. // Ignore spaces, tabs, etc.
  1001. pos++;
  1002. }
  1003. else if (c == '*'
  1004. || c == '·') // center dot '\0x0B7' (char)183 U+00B7
  1005. {
  1006. HandleNewOperator(OperatorKind.Mult);
  1007. }
  1008. else if (c == '/')
  1009. {
  1010. HandleNewOperator(OperatorKind.Div);
  1011. }
  1012. else if (c == '^')
  1013. {
  1014. HandleNewOperator(OperatorKind.Pow);
  1015. }
  1016. else if (c == '-'
  1017. || c == '+'
  1018. || Char.IsDigit(c))
  1019. {
  1020. // An exponent
  1021. if ((lastReadToken != TokenKind.Unit) // Exponent can follow unit directly
  1022. && ((lastReadToken != TokenKind.Operator) // or follow Pow operator
  1023. || (operators.Peek() != OperatorKind.Pow)))
  1024. {
  1025. if (throwExceptionOnInvalidInput)
  1026. {
  1027. throw new PhysicalUnitFormatException("The string argument is not in a valid physical unit format. An exponent must follow a unit or Pow operator. Invalid exponent at '" + c + "' at position " + pos.ToString());
  1028. }
  1029. else
  1030. {
  1031. // return null;
  1032. inputRecognized = false;
  1033. }
  1034. }
  1035. else
  1036. {
  1037. //// Try to read an exponent from input
  1038. Int16 numLen = 1;
  1039. int maxLen = Math.Min(inputString.Length - pos, 1 + 3); // Max length of sign and digits to look for
  1040. while (numLen < maxLen && Char.IsDigit(inputString[pos + numLen]))
  1041. {
  1042. numLen++;
  1043. }
  1044. SByte exponent;
  1045. if (numLen > 0 && SByte.TryParse(inputString.Substring(pos, numLen), out exponent))
  1046. {
  1047. if ((lastReadToken == TokenKind.Operator)
  1048. && (operators.Peek() == OperatorKind.Pow))
  1049. {
  1050. // Exponent follow Pow operator;
  1051. // Remove Pow operator from operator stack since it is handled as implicit in parser.
  1052. operators.Pop();
  1053. }
  1054. pos += numLen;
  1055. afterLastOperandPos = pos;
  1056. lastReadToken = TokenKind.Exponent;
  1057. return new Token(exponent);
  1058. }
  1059. else
  1060. {
  1061. if (throwExceptionOnInvalidInput)
  1062. {
  1063. throw new PhysicalUnitFormatException("The string argument is not in a valid physical unit format. Invalid or missing exponent after '" + c + "' at position " + pos.ToString());
  1064. }
  1065. else
  1066. {
  1067. // return null;
  1068. inputRecognized = false;
  1069. }
  1070. }
  1071. }
  1072. }
  1073. else
  1074. {
  1075. if ((lastReadToken == TokenKind.Operator) // Unit follow Pow operator;
  1076. && (operators.Peek() == OperatorKind.Pow))
  1077. {
  1078. if (throwExceptionOnInvalidInput)
  1079. {
  1080. throw new PhysicalUnitFormatException("The string argument is not in a valid physical unit format. An unit must not follow an pow operator. Missing exponent at '" + c + "' at position " + pos.ToString());
  1081. }
  1082. else
  1083. {
  1084. inputRecognized = false;
  1085. }
  1086. }
  1087. else
  1088. {
  1089. // Try to read a unit from input
  1090. int maxLen = Math.Min(1 + 3, inputString.Length - pos); // Max length of scale and symbols to look for
  1091. String tempStr = inputString.Substring(pos, maxLen);
  1092. maxLen = tempStr.IndexOfAny(new Char[] { ' ', '*', '·', '/', '^', '+', '-', '(', ')' }); // '·' center dot '\0x0B7' (Char)183 U+00B7
  1093. if (maxLen < 0)
  1094. {
  1095. maxLen = tempStr.Length;
  1096. }
  1097. for (int unitLen = maxLen; unitLen > 0; unitLen--)
  1098. {
  1099. String unitStr = tempStr.Substring(0, unitLen);
  1100. Unit su = Physics.UnitSystems.ScaledUnitFromSymbol(unitStr);
  1101. if (su != null)
  1102. {
  1103. if (lastReadToken == TokenKind.Unit)
  1104. { // Assume implicit Mult operator
  1105. PushNewOp

Large files files are truncated, but you can click here to view the full file