diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..8d73938c4e866f32011a08856aaf8d092761d085 --- /dev/null +++ b/.gitignore @@ -0,0 +1,261 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +project.fragment.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +#*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc \ No newline at end of file diff --git a/VectoSI/VectoCommon/DoubleExtensionMethods.cs b/VectoSI/VectoCommon/DoubleExtensionMethods.cs new file mode 100644 index 0000000000000000000000000000000000000000..0824de24cba81d4068a86d2eaf7f8f49974531e9 --- /dev/null +++ b/VectoSI/VectoCommon/DoubleExtensionMethods.cs @@ -0,0 +1,165 @@ +/* +* This file is part of VECTO. +* +* Copyright © 2012-2016 European Union +* +* Developed by Graz University of Technology, +* Institute of Internal Combustion Engines and Thermodynamics, +* Institute of Technical Informatics +* +* VECTO is licensed under the EUPL, Version 1.1 or - as soon they will be approved +* by the European Commission - subsequent versions of the EUPL (the "Licence"); +* You may not use VECTO except in compliance with the Licence. +* You may obtain a copy of the Licence at: +* +* https://joinup.ec.europa.eu/community/eupl/og_page/eupl +* +* Unless required by applicable law or agreed to in writing, VECTO +* distributed under the Licence is distributed on an "AS IS" basis, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the Licence for the specific language governing permissions and +* limitations under the Licence. +* +* Authors: +* Stefan Hausberger, hausberger@ivt.tugraz.at, IVT, Graz University of Technology +* Christian Kreiner, christian.kreiner@tugraz.at, ITI, Graz University of Technology +* Michael Krisper, michael.krisper@tugraz.at, ITI, Graz University of Technology +* Raphael Luz, luz@ivt.tugraz.at, IVT, Graz University of Technology +* Markus Quaritsch, markus.quaritsch@tugraz.at, IVT, Graz University of Technology +* Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology +*/ + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace TUGraz.VectoCommon.Utils +{ + /// <summary> + /// Extension methods for double. + /// Simplified Version for VECTO SI Performance. + /// </summary> + public static class DoubleExtensionMethods + { + /// <summary> + /// The tolerance. + /// </summary> + public const double Tolerance = 1e-6; + + /// <summary> + /// The tolerancefactor for relative comparisons. + /// </summary> + public const double ToleranceFactor = 1e-6; + + /// <summary> + /// Determines whether the specified other is equal within tolerance. + /// </summary> + /// <param name="self">The self.</param> + /// <param name="other">The other.</param> + /// <param name="tolerance">The tolerance.</param> + /// <returns></returns> + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsEqual(this double self, double other, double tolerance = Tolerance) + { + return Math.Abs(self - other) < tolerance; + } + + /// <summary> + /// Determines whether the specified other is smaller within tolerance. + /// </summary> + /// <param name="self">The self.</param> + /// <param name="other">The other.</param> + /// <param name="tolerance">The tolerance.</param> + /// <returns></returns> + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsSmaller(this double self, double other, double tolerance = Tolerance) + { + return self < other - tolerance; + } + + /// <summary> + /// Determines whether the specified other is smaller or equal within tolerance. + /// </summary> + /// <param name="self">The self.</param> + /// <param name="other">The other.</param> + /// <param name="tolerance">The tolerance.</param> + /// <returns></returns> + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsSmallerOrEqual(this double self, double other, double tolerance = Tolerance) + { + return self <= other + tolerance; + } + + /// <summary> + /// Determines whether the specified other is greater within tolerance. + /// </summary> + /// <param name="self">The self.</param> + /// <param name="other">The other.</param> + /// <param name="tolerance">The tolerance.</param> + /// <returns></returns> + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsGreater(this double self, double other, double tolerance = Tolerance) + { + return self > other + tolerance; + } + + /// <summary> + /// Determines whether the specified other is greater or equal within tolerance. + /// </summary> + /// <param name="self">The self.</param> + /// <param name="other">The other.</param> + /// <param name="tolerance">The tolerance.</param> + /// <returns></returns> + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsGreaterOrEqual(this double self, double other, double tolerance = Tolerance) + { + return self >= other - tolerance; + } + + /// <summary> + /// Converts the double-value from RPM (rounds per minute) to the SI Unit PerSecond. + /// </summary> + /// <param name="self"></param> + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PerSecond RPMtoRad(this double self) + { + return SI<PerSecond>(self * 2 * Math.PI / 60.0); + } + + /// <summary> + /// Converts the value from rounds per minute to the SI Unit PerSecond + /// </summary> + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PerSecond RPMtoRad(this int self) + { + return SI<PerSecond>(self * 2.0 * Math.PI / 60.0); + } + + /// <summary> + /// Creates an SI object for the number (unit-less: [-]). + /// </summary> + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static SI SI(this double value) + { + return new SI(value); + } + + /// <summary> + /// Creates an templated SI object for the number. + /// </summary> + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T SI<T>(this double value) where T : SIBase<T> + { + return SIBase<T>.Create(value); + } + } +} \ No newline at end of file diff --git a/VectoSI/VectoCommon/EnumerableExtensionMethods.cs b/VectoSI/VectoCommon/EnumerableExtensionMethods.cs new file mode 100644 index 0000000000000000000000000000000000000000..a424945a3f000d5fafe8c2cab2771d608a1310fb --- /dev/null +++ b/VectoSI/VectoCommon/EnumerableExtensionMethods.cs @@ -0,0 +1,63 @@ +/* +* This file is part of VECTO. +* +* Copyright © 2012-2016 European Union +* +* Developed by Graz University of Technology, +* Institute of Internal Combustion Engines and Thermodynamics, +* Institute of Technical Informatics +* +* VECTO is licensed under the EUPL, Version 1.1 or - as soon they will be approved +* by the European Commission - subsequent versions of the EUPL (the "Licence"); +* You may not use VECTO except in compliance with the Licence. +* You may obtain a copy of the Licence at: +* +* https://joinup.ec.europa.eu/community/eupl/og_page/eupl +* +* Unless required by applicable law or agreed to in writing, VECTO +* distributed under the Licence is distributed on an "AS IS" basis, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the Licence for the specific language governing permissions and +* limitations under the Licence. +* +* Authors: +* Stefan Hausberger, hausberger@ivt.tugraz.at, IVT, Graz University of Technology +* Christian Kreiner, christian.kreiner@tugraz.at, ITI, Graz University of Technology +* Michael Krisper, michael.krisper@tugraz.at, ITI, Graz University of Technology +* Raphael Luz, luz@ivt.tugraz.at, IVT, Graz University of Technology +* Markus Quaritsch, markus.quaritsch@tugraz.at, IVT, Graz University of Technology +* Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology +*/ + +using System; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace TUGraz.VectoCommon.Utils +{ + /// <summary> + /// Enumerable Helper Methods. + /// Simplified Version for VECTO SI Performance. + /// </summary> + public static class EnumerableExtensionMethods + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool SequenceEqualFast<T>(this T[] self, T[] other) where T : IComparable + { + if (self.Equals(other)) { + return true; + } + + if (self.Length != other.Length) { + return false; + } + + for (var i = 0; i < self.Length; i++) { + if (self[i].CompareTo(other[i]) != 0) { + return self.OrderBy(x => x).SequenceEqual(other.OrderBy(x => x)); + } + } + return true; + } + } +} \ No newline at end of file diff --git a/VectoSI/VectoCommon/IntExtensionMethods.cs b/VectoSI/VectoCommon/IntExtensionMethods.cs new file mode 100644 index 0000000000000000000000000000000000000000..7e40fbc43822de8055ea33f60565bfbfaf9deca5 --- /dev/null +++ b/VectoSI/VectoCommon/IntExtensionMethods.cs @@ -0,0 +1,63 @@ +/* +* This file is part of VECTO. +* +* Copyright © 2012-2016 European Union +* +* Developed by Graz University of Technology, +* Institute of Internal Combustion Engines and Thermodynamics, +* Institute of Technical Informatics +* +* VECTO is licensed under the EUPL, Version 1.1 or - as soon they will be approved +* by the European Commission - subsequent versions of the EUPL (the "Licence"); +* You may not use VECTO except in compliance with the Licence. +* You may obtain a copy of the Licence at: +* +* https://joinup.ec.europa.eu/community/eupl/og_page/eupl +* +* Unless required by applicable law or agreed to in writing, VECTO +* distributed under the Licence is distributed on an "AS IS" basis, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the Licence for the specific language governing permissions and +* limitations under the Licence. +* +* Authors: +* Stefan Hausberger, hausberger@ivt.tugraz.at, IVT, Graz University of Technology +* Christian Kreiner, christian.kreiner@tugraz.at, ITI, Graz University of Technology +* Michael Krisper, michael.krisper@tugraz.at, ITI, Graz University of Technology +* Raphael Luz, luz@ivt.tugraz.at, IVT, Graz University of Technology +* Markus Quaritsch, markus.quaritsch@tugraz.at, IVT, Graz University of Technology +* Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology +*/ + +using System; +using System.Diagnostics; + +namespace TUGraz.VectoCommon.Utils +{ + /// <summary> + /// Int Helper Methods. + /// Simplified Version for VECTO SI Performance. + /// </summary> + public static class IntExtensionMethods + { + /// <summary> + /// Gets the unit-less SI representation of the number. + /// </summary> + [DebuggerHidden] + public static SI SI(this int value) + { + return new SI(value); + } + + /// <summary> + /// Gets the special SI class of the number. + /// </summary> + /// <param name="d"></param> + /// <returns></returns> + [DebuggerHidden] + public static T SI<T>(this int d) where T : SIBase<T> + { + return SIBase<T>.Create(d); + } + } +} \ No newline at end of file diff --git a/VectoSI/VectoCommon/Properties/AssemblyInfo.cs b/VectoSI/VectoCommon/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000000000000000000000000000000000..f9e8c394ae8ed5c88f865b564659e5b0202c24e3 --- /dev/null +++ b/VectoSI/VectoCommon/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("VectoCommon")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("VectoCommon")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("228258f5-d390-44cf-b326-9b0057833722")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/VectoSI/VectoCommon/SI.cs b/VectoSI/VectoCommon/SI.cs new file mode 100644 index 0000000000000000000000000000000000000000..96a8c15ed7c87767caff741bcad9638237c10520 --- /dev/null +++ b/VectoSI/VectoCommon/SI.cs @@ -0,0 +1,2278 @@ +/* +* This file is part of VECTO. +* +* Copyright © 2012-2016 European Union +* +* Developed by Graz University of Technology, +* Institute of Internal Combustion Engines and Thermodynamics, +* Institute of Technical Informatics +* +* VECTO is licensed under the EUPL, Version 1.1 or - as soon they will be approved +* by the European Commission - subsequent versions of the EUPL (the "Licence"); +* You may not use VECTO except in compliance with the Licence. +* You may obtain a copy of the Licence at: +* +* https://joinup.ec.europa.eu/community/eupl/og_page/eupl +* +* Unless required by applicable law or agreed to in writing, VECTO +* distributed under the Licence is distributed on an "AS IS" basis, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the Licence for the specific language governing permissions and +* limitations under the Licence. +* +* Authors: +* Stefan Hausberger, hausberger@ivt.tugraz.at, IVT, Graz University of Technology +* Christian Kreiner, christian.kreiner@tugraz.at, ITI, Graz University of Technology +* Michael Krisper, michael.krisper@tugraz.at, ITI, Graz University of Technology +* Raphael Luz, luz@ivt.tugraz.at, IVT, Graz University of Technology +* Markus Quaritsch, markus.quaritsch@tugraz.at, IVT, Graz University of Technology +* Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology +*/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.CompilerServices; +using TUGraz.VectoCommon.Exceptions; + +// ReSharper disable ClassNeverInstantiated.Global + +namespace TUGraz.VectoCommon.Utils +{ + /// <summary> + /// SI Class for Scalar Values. Converts implicitely to double and is only castable if the SI value has no units. + /// </summary> + public class Scalar : SIBase<Scalar> + { + [DebuggerHidden] + private Scalar(double val) : base(val) {} + + public static implicit operator double(Scalar self) + { + return self.Val; + } + + /// <summary> + /// Implements the operator +. + /// </summary> + [DebuggerHidden] + public static Scalar operator +(Scalar si1, Scalar si2) + { + return Create(si1.Val + si2.Val); + } + + /// <summary> + /// Implements the operator +. + /// </summary> + [DebuggerHidden] + public static Scalar operator +(Scalar si1, double si2) + { + return Create(si1.Val + si2); + } + + /// <summary> + /// Implements the operator +. + /// </summary> + [DebuggerHidden] + public static Scalar operator +(double si1, Scalar si2) + { + return Create(si1 + si2.Val); + } + + /// <summary> + /// Implements the operator -. + /// </summary> + [DebuggerHidden] + public static Scalar operator -(Scalar si1, Scalar si2) + { + return Create(si1.Val - si2.Val); + } + + /// <summary> + /// Implements the operator -. + /// </summary> + [DebuggerHidden] + public static Scalar operator -(Scalar si1, double si2) + { + return Create(si1.Val - si2); + } + + /// <summary> + /// Implements the operator -. + /// </summary> + [DebuggerHidden] + public static Scalar operator -(double si1, Scalar si2) + { + return Create(si1 - si2.Val); + } + } + + /// <summary> + /// SI Class for Newton [N]. + /// </summary> + public class Newton : SIBase<Newton> + { + private static readonly Unit[] NumeratorDefault = { Unit.N }; + + [DebuggerHidden] + private Newton(double val) : base(val, NumeratorDefault) {} + + /// <summary> + /// Implements the operator *. + /// </summary> + /// <param name="newton">The newton.</param> + /// <param name="meter">The meter.</param> + /// <returns> + /// The result of the operator. + /// </returns> + [DebuggerHidden] + public static NewtonMeter operator *(Newton newton, Meter meter) + { + return SIBase<NewtonMeter>.Create(newton.Val * meter.Value()); + } + + [DebuggerHidden] + public static Watt operator *(Newton newton, MeterPerSecond meterPerSecond) + { + return SIBase<Watt>.Create(newton.Val * meterPerSecond.Value()); + } + + [DebuggerHidden] + public static Watt operator *(MeterPerSecond meterPerSecond, Newton newton) + { + return SIBase<Watt>.Create(newton.Val * meterPerSecond.Value()); + } + } + + /// <summary> + /// SI Class for Radian [] (rad). + /// </summary> + public class Radian : SIBase<Radian> + { + [DebuggerHidden] + private Radian(double val) : base(val) {} + } + + /// <summary> + /// SI Class for PerSquareSecond [1/s^2]. + /// </summary> + public class PerSquareSecond : SIBase<PerSquareSecond> + { + private static readonly Unit[] DenominatorDefault = { Unit.s, Unit.s }; + + [DebuggerHidden] + private PerSquareSecond(double val) : base(val, new Unit[0], denominator: DenominatorDefault) {} + + [DebuggerHidden] + public static PerSecond operator *(PerSquareSecond perSquareSecond, Second second) + { + return SIBase<PerSecond>.Create(perSquareSecond.Val * second.Value()); + } + } + + /// <summary> + /// SI Class for Meter per square second [m/s^2]. + /// </summary> + public class MeterPerSquareSecond : SIBase<MeterPerSquareSecond> + { + private static readonly Unit[] NumeratorDefault = { Unit.m }; + private static readonly Unit[] DenominatorDefault = { Unit.s, Unit.s }; + + [DebuggerHidden] + private MeterPerSquareSecond(double val) : base(val, NumeratorDefault, DenominatorDefault) {} + + /// <summary> + /// Implements the operator *. + /// </summary> + [DebuggerHidden] + public static MeterPerSecond operator *(MeterPerSquareSecond meterPerSecond, Second second) + { + return SIBase<MeterPerSecond>.Create(meterPerSecond.Val * second.Value()); + } + } + + /// <summary> + /// SI Class for Second [s]. + /// </summary> + public class Second : SIBase<Second> + { + private static readonly Unit[] NumeratorDefault = { Unit.s }; + + [DebuggerHidden] + private Second(double val) : base(val, NumeratorDefault) {} + } + + /// <summary> + /// SI Class for Meter [m]. + /// </summary> + public class Meter : SIBase<Meter> + { + private static readonly Unit[] NumeratorDefault = { Unit.m }; + + [DebuggerHidden] + private Meter(double val) : base(val, NumeratorDefault) {} + + [DebuggerHidden] + public static MeterPerSecond operator /(Meter meter, Second second) + { + return SIBase<MeterPerSecond>.Create(meter.Val / second.Value()); + } + + [DebuggerHidden] + public static MeterPerSecond operator *(Meter meter, PerSecond perSecond) + { + return SIBase<MeterPerSecond>.Create(meter.Val * perSecond.Value()); + } + + /// <summary> + /// Implements the operator /. + /// </summary> + [DebuggerHidden] + public static Second operator /(Meter second, MeterPerSecond meterPerSecond) + { + return SIBase<Second>.Create(second.Val / meterPerSecond.Value()); + } + } + + /// <summary> + /// SI Class for KilogramPerMeter [kg/m]. + /// </summary> + public class KilogramPerMeter : SIBase<KilogramPerMeter> + { + private static readonly Unit[] NumeratorDefault = { Unit.k, Unit.g }; + private static readonly Unit[] DenominatorDefault = { Unit.m }; + + [DebuggerHidden] + private KilogramPerMeter(double val) : base(val, NumeratorDefault, DenominatorDefault) {} + } + + ///// <summary> + ///// SI Class for Gram + ///// </summary> + //public class Gram : SIBase<Gram> + //{ + // private static readonly Unit[] NumeratorDefault = { Unit.g }; + + // [DebuggerHidden] + // private Gram(double val) : base(val, NumeratorDefault) {} + //} + + //public class GramPerSecond : SIBase<GramPerSecond> + //{ + // private static readonly Unit[] NumeratorDefault = { Unit.g }; + // private static readonly Unit[] DenominatorDefault = { Unit.s }; + + // private GramPerSecond(double val) : base(val, NumeratorDefault, DenominatorDefault) { } + + // public static Gram operator *(GramPerSecond gps, Second s) + // { + // return SIBase<Gram>.Create(gps.Val * s.Value()); + // } + //} + + //public class GramPerLiter : SIBase<GramPerLiter> + //{ + // private static readonly Unit[] NumeratorDefault = { Unit.g }; + // private static readonly Unit[] DenominatorDefault = { Unit.liter }; + + // private GramPerLiter(double val) : base(val, NumeratorDefault, DenominatorDefault) {} + //} + + public class LiterPerSecond : SIBase<LiterPerSecond> + { + private static readonly Unit[] NumeratorDefault = { Unit.liter }; + private static readonly Unit[] DenominatorDefault = { Unit.s }; + + private LiterPerSecond(double val) : base(val, NumeratorDefault, DenominatorDefault) {} + } + + /// <summary> + /// SI Class for Kilogram [kg]. + /// </summary> + public class Kilogram : SIBase<Kilogram> + { + private static readonly Unit[] NumeratorDefault = { Unit.k, Unit.g }; + + [DebuggerHidden] + private Kilogram(double val) : base(val, NumeratorDefault) {} + + [DebuggerHidden] + public static KilogramPerSecond operator /(Kilogram kg, Second second) + { + return SIBase<KilogramPerSecond>.Create(kg.Val / second.Value()); + } + + [DebuggerHidden] + public static SI operator /(Kilogram kg, Joule j) + { + return (kg as SI) / j; + } + + [DebuggerHidden] + public static Scalar operator /(Kilogram kg, Kilogram kg2) + { + return SIBase<Scalar>.Create(kg.Val / kg2.Val); + } + + [DebuggerHidden] + public static KilogramPerMeter operator /(Kilogram kg, Meter m) + { + return SIBase<KilogramPerMeter>.Create(kg.Val / m.Value()); + } + + [DebuggerHidden] + public static Newton operator *(Kilogram kg, MeterPerSquareSecond m) + { + return SIBase<Newton>.Create(kg.Val * m.Value()); + } + + [DebuggerHidden] + public static Kilogram operator *(Kilogram kg, double d) + { + return new Kilogram(kg.Val * d); + } + + [DebuggerHidden] + public static Kilogram operator *(double d, Kilogram kg) + { + return new Kilogram(d * kg.Val); + } + + public static Liter operator /(Kilogram kilogram, KilogramPerCubicMeter kilogramPerCubicMeter) + { + return SIBase<Liter>.Create(kilogram.Value() / kilogramPerCubicMeter.Value() * 1000); + } + } + + public class Liter : SIBase<Liter> + { + private static readonly Unit[] NumeratorDefault = { Unit.liter }; + + [DebuggerHidden] + private Liter(double val) : base(val, NumeratorDefault) {} + + public static Kilogram operator *(Liter liter, KilogramPerCubicMeter kilogramPerCubicMeter) + { + return SIBase<Kilogram>.Create(liter.Val / 1000 * kilogramPerCubicMeter.Value()); + } + } + + /// <summary> + /// + /// </summary> + public class NormLiter : SIBase<NormLiter> + { + private static readonly Unit[] NumeratorDefault = { Unit.NI }; + + [DebuggerHidden] + private NormLiter(double val) : base(val, NumeratorDefault) {} + + public static NormLiterPerSecond operator /(NormLiter nl, Second s) + { + return SIBase<NormLiterPerSecond>.Create(nl.Val / s.Value()); + } + } + + /// <summary> + /// + /// </summary> + public class NormLiterPerSecond : SIBase<NormLiterPerSecond> + { + private static readonly Unit[] NumeratorDefault = { Unit.NI }; + private static readonly Unit[] DenominatorDefault = { Unit.s }; + + [DebuggerHidden] + private NormLiterPerSecond(double val) : base(val, NumeratorDefault, DenominatorDefault) {} + + public static NormLiter operator *(NormLiterPerSecond nips, Second s) + { + return SIBase<NormLiter>.Create(nips.Val * s.Value()); + } + + public static NormLiterPerSecond operator *(NormLiterPerSecond nps, double val) + { + return Create(nps.Val * val); + } + } + + /// <summary> + /// SI Class for Kilogram per Second [kg]. + /// </summary> + public class KilogramPerSecond : SIBase<KilogramPerSecond> + { + private static readonly Unit[] NumeratorDefault = { Unit.k, Unit.g }; + private static readonly Unit[] DenominatorDefault = { Unit.s }; + + [DebuggerHidden] + private KilogramPerSecond(double value) : base(value, NumeratorDefault, DenominatorDefault) {} + + [DebuggerHidden] + public static Kilogram operator *(KilogramPerSecond kilogramPerSecond, Second second) + { + return SIBase<Kilogram>.Create(kilogramPerSecond.Val * second.Value()); + } + } + + /// <summary> + /// SI Class for Square meter [m^2]. + /// </summary> + public class SquareMeter : SIBase<SquareMeter> + { + private static readonly Unit[] NumeratorDefault = { Unit.m, Unit.m }; + + [DebuggerHidden] + private SquareMeter(double value) : base(value, NumeratorDefault) {} + } + + /// <summary> + /// SI Class for cubic meter [m^3]. + /// </summary> + public class CubicMeter : SIBase<CubicMeter> + { + private static readonly Unit[] NumeratorDefault = { Unit.m, Unit.m, Unit.m }; + + [DebuggerHidden] + private CubicMeter(double value) + : base(value, NumeratorDefault) {} + } + + /// <summary> + /// SI Class for Kilogram Square Meter [kgm^2]. + /// </summary> + public class KilogramSquareMeter : SIBase<KilogramSquareMeter> + { + private static readonly Unit[] NumeratorDefault = { Unit.k, Unit.g, Unit.m, Unit.m }; + + [DebuggerHidden] + private KilogramSquareMeter(double value) : base(value, NumeratorDefault) {} + + [DebuggerHidden] + public static NewtonMeter operator *(KilogramSquareMeter kilogramSquareMeter, PerSquareSecond perSquareSecond) + { + return SIBase<NewtonMeter>.Create(kilogramSquareMeter.Val * perSquareSecond.Value()); + } + } + + /// <summary> + /// SI Class for Kilogram Square Meter [kgm^2]. + /// </summary> + public class KilogramPerCubicMeter : SIBase<KilogramPerCubicMeter> + { + private static readonly Unit[] NumeratorDefault = { Unit.k, Unit.g }; + private static readonly Unit[] DenominatorDefault = { Unit.m, Unit.m, Unit.m }; + + [DebuggerHidden] + private KilogramPerCubicMeter(double value) : base(value, NumeratorDefault, DenominatorDefault) {} + + [DebuggerHidden] + public static Kilogram operator *(KilogramPerCubicMeter kilogramPerCubicMeter, CubicMeter cubicMeter) + { + return SIBase<Kilogram>.Create(kilogramPerCubicMeter.Val * cubicMeter.Value()); + } + + public static Kilogram operator *(KilogramPerCubicMeter kilogramPerCubicMeter, Liter liter) + { + return SIBase<Kilogram>.Create(kilogramPerCubicMeter.Val * liter.Value() / 1000); + } + + //public static CubicMeter operator /(Kilogram kg, KilogramPerCubicMeter kgm3) + //{ + // return SIBase<CubicMeter>.Create(kg.Value() / kgm3.Val); + //} + } + + /// <summary> + /// SI Class for Kilogramm per watt second [kg/Ws]. + /// </summary> + public class KilogramPerWattSecond : SIBase<KilogramPerWattSecond> + { + private static readonly Unit[] NumeratorDefault = { Unit.k, Unit.g }; + private static readonly Unit[] DenominatorDefault = { Unit.W, Unit.s }; + + [DebuggerHidden] + private KilogramPerWattSecond(double val) : base(val, NumeratorDefault, DenominatorDefault) {} + } + + /// <summary> + /// SI Class for watt second [Ws]. + /// </summary> + public class WattSecond : SIBase<WattSecond> + { + private static readonly Unit[] NumeratorDefault = { Unit.W, Unit.s }; + + [DebuggerHidden] + private WattSecond(double val) : base(val, NumeratorDefault) {} + + [DebuggerHidden] + public static Watt operator /(WattSecond wattSecond, Second second) + { + return SIBase<Watt>.Create(wattSecond.Val / second.Value()); + } + } + + /// <summary> + /// SI Class for Watt [W]. + /// </summary> + public class Watt : SIBase<Watt> + { + private static readonly Unit[] NumeratorDefault = { Unit.W }; + + [DebuggerHidden] + private Watt(double val) : base(val, NumeratorDefault) {} + + /// <summary> + /// Implements the operator /. + /// </summary> + /// <param name="watt">The watt.</param> + /// <param name="newtonMeter">The newton meter.</param> + /// <returns> + /// The result of the operator. + /// </returns> + [DebuggerHidden] + public static PerSecond operator /(Watt watt, NewtonMeter newtonMeter) + { + return SIBase<PerSecond>.Create(watt.Val / newtonMeter.Value()); + } + + [DebuggerHidden] + public static Newton operator /(Watt watt, MeterPerSecond meterPerSecond) + { + return SIBase<Newton>.Create(watt.Val / meterPerSecond.Value()); + } + + /// <summary> + /// Implements the operator /. + /// </summary> + /// <param name="watt">The watt.</param> + /// <param name="perSecond">The per second.</param> + /// <returns> + /// The result of the operator. + /// </returns> + [DebuggerHidden] + public static NewtonMeter operator /(Watt watt, PerSecond perSecond) + { + return SIBase<NewtonMeter>.Create(watt.Val / perSecond.Value()); + } + + [DebuggerHidden] + public static WattSecond operator *(Watt watt, Second second) + { + return SIBase<WattSecond>.Create(watt.Val * second.Value()); + } + + [DebuggerHidden] + public static Watt operator *(Watt watt, double val) + { + return Create(watt.Val * val); + } + } + + public class Joule : SIBase<Joule> + { + private static readonly Unit[] NumeratorDefault = { Unit.W, Unit.s }; + + [DebuggerHidden] + private Joule(double val) : base(val, NumeratorDefault) {} + + public static implicit operator Joule(WattSecond self) + { + return Create(self.Value()); + } + + public static Joule operator +(Joule joule, WattSecond ws) + { + return Create(joule.Val + ws.Value()); + } + + public static Watt operator /(Joule joule, Second s) + { + return SIBase<Watt>.Create(joule.Val / s.Value()); + } + } + + public class JoulePerKilogramm : SIBase<JoulePerKilogramm> + { + private static readonly Unit[] NumeratorDefault = { Unit.J }; + private static readonly Unit[] DenominatorDefault = { Unit.k, Unit.g }; + + private JoulePerKilogramm(double val) : base(val, NumeratorDefault, DenominatorDefault) {} + + public static Joule operator *(Kilogram kg, JoulePerKilogramm jpg) + { + return SIBase<Joule>.Create(kg.Value() * jpg.Val); + } + } + + /// <summary> + /// SI Class for one per second [1/s]. + /// </summary> + [DebuggerDisplay("rad/s: {Val} | rpm: {AsRPM}")] + public class PerSecond : SIBase<PerSecond> + { + private static readonly Unit[] DenominatorDefault = { Unit.s }; + + [DebuggerHidden] + private PerSecond(double val) : base(val, new Unit[0], DenominatorDefault) {} + + [DebuggerHidden] + public static PerSquareSecond operator /(PerSecond perSecond, Second second) + { + return SIBase<PerSquareSecond>.Create(perSecond.Val / second.Value()); + } + + public double AsRPM + { + get { return Val * 60 / (2 * Math.PI); } + } + } + + /// <summary> + /// SI Class for Meter per second [m/s]. + /// </summary> + [DebuggerDisplay("{Val} | {AsKmph}")] + public class MeterPerSecond : SIBase<MeterPerSecond> + { + private static readonly Unit[] NumeratorDefault = { Unit.m }; + private static readonly Unit[] DenominatorDefault = { Unit.s }; + + [DebuggerHidden] + private MeterPerSecond(double val) : base(val, NumeratorDefault, DenominatorDefault) {} + + public double AsKmph + { + get { return Val * 3.6; } + } + + /// <summary> + /// Implements the operator /. + /// </summary> + [DebuggerHidden] + public static PerSecond operator /(MeterPerSecond meterPerSecond, Meter meter) + { + return SIBase<PerSecond>.Create(meterPerSecond.Val / meter.Value()); + } + + /// <summary> + /// Implements the operator /. + /// </summary> + [DebuggerHidden] + public static Second operator /(MeterPerSecond meterPerSecond, MeterPerSquareSecond meterPerSquareSecond) + { + return SIBase<Second>.Create(meterPerSecond.Val / meterPerSquareSecond.Value()); + } + + /// <summary> + /// Implements the operator /. + /// </summary> + [DebuggerHidden] + public static MeterPerSquareSecond operator /(MeterPerSecond meterPerSecond, Second second) + { + return SIBase<MeterPerSquareSecond>.Create(meterPerSecond.Val / second.Value()); + } + + /// <summary> + /// Implements the operator *. + /// </summary> + [DebuggerHidden] + public static Meter operator *(MeterPerSecond meterPerSecond, Second second) + { + return SIBase<Meter>.Create(meterPerSecond.Val * second.Value()); + } + + /// <summary> + /// Implements the operator *. + /// </summary> + [DebuggerHidden] + public static MeterPerSquareSecond operator *(MeterPerSecond meterPerSecond, PerSecond perSecond) + { + return SIBase<MeterPerSquareSecond>.Create(meterPerSecond.Val * perSecond.Value()); + } + + /// <summary> + /// Implements the operator *. + /// </summary> + [DebuggerHidden] + public static Meter operator *(Second second, MeterPerSecond meterPerSecond) + { + return SIBase<Meter>.Create(second.Value() * meterPerSecond.Val); + } + } + + /// <summary> + /// SI Class for NewtonMeter [Nm]. + /// </summary> + public class NewtonMeter : SIBase<NewtonMeter> + { + private static readonly Unit[] NumeratorDefault = { Unit.N, Unit.m }; + + [DebuggerHidden] + private NewtonMeter(double val) : base(val, NumeratorDefault) {} + + [DebuggerHidden] + public static Watt operator *(NewtonMeter newtonMeter, PerSecond perSecond) + { + return SIBase<Watt>.Create(newtonMeter.Val * perSecond.Value()); + } + + [DebuggerHidden] + public static Watt operator *(PerSecond perSecond, NewtonMeter newtonMeter) + { + return SIBase<Watt>.Create(perSecond.Value() * newtonMeter.Val); + } + + [DebuggerHidden] + public static Second operator /(NewtonMeter newtonMeter, Watt watt) + { + return SIBase<Second>.Create(newtonMeter.Val / watt.Value()); + } + + [DebuggerHidden] + public static PerSquareSecond operator /(NewtonMeter newtonMeter, KilogramSquareMeter kgKilogramSquareMeter) + { + return SIBase<PerSquareSecond>.Create(newtonMeter.Val / kgKilogramSquareMeter.Value()); + } + + [DebuggerHidden] + public static PerSecond operator /(NewtonMeter newtonMeter, NewtonMeterSecond newtonMeterSecond) + { + return SIBase<PerSecond>.Create(newtonMeter.Val / newtonMeterSecond.Value()); + } + + [DebuggerHidden] + public static Newton operator /(NewtonMeter newtonMeter, Meter meter) + { + return SIBase<Newton>.Create(newtonMeter.Val / meter.Value()); + } + + [DebuggerHidden] + public static NewtonMeterSecond operator /(NewtonMeter newtonMeter, PerSecond perSecond) + { + return SIBase<NewtonMeterSecond>.Create(newtonMeter.Val / perSecond.Value()); + } + } + + public class NewtonMeterSecond : SIBase<NewtonMeterSecond> + { + private static readonly Unit[] NumeratorDefault = { Unit.N, Unit.m, Unit.s }; + private NewtonMeterSecond(double val) : base(val, NumeratorDefault) {} + } + + public class Ampere : SIBase<Ampere> + { + private static readonly Unit[] NumeratorDefault = { Unit.Ampere }; + private Ampere(double val) : base(val, NumeratorDefault) {} + + public static Watt operator *(Ampere ampere, Volt volt) + { + return SIBase<Watt>.Create(volt.Value() * ampere.Val); + } + + public static Ampere operator *(Ampere ampere, double val) + { + return Create(ampere.Val * val); + } + + public static Volt operator /(Watt watt, Ampere ampere) + { + return SIBase<Volt>.Create(watt.Value() / ampere.Value()); + } + + public static Watt operator /(Volt volt, Ampere ampere) + { + return SIBase<Watt>.Create(volt.Value() * ampere.Value()); + } + } + + public class Volt : SIBase<Volt> + { + private static readonly Unit[] NumeratorDefault = { Unit.Volt }; + private Volt(double val) : base(val, NumeratorDefault) {} + + public static Watt operator *(Volt volt, Ampere ampere) + { + return SIBase<Watt>.Create(volt.Val * ampere.Value()); + } + + public static Ampere operator /(Watt watt, Volt volt) + { + return SIBase<Ampere>.Create(watt.Value() / volt.Value()); + } + } + + /// <summary> + /// Base Class for all special SI Classes. Not intended to be used directly. + /// Implements templated operators for type safety and convenience. + /// </summary> + /// <typeparam name="T"></typeparam> + public abstract class SIBase<T> : SI where T : SIBase<T> + { + private static readonly T ZeroPrototype; + + static SIBase() + { + const BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Instance; + var constructorInfo = typeof(T).GetConstructor(bindingFlags, null, new[] { typeof(double) }, null); + var parameter = Expression.Parameter(typeof(double)); + var lambda = Expression.Lambda<Func<double, T>>(Expression.New(constructorInfo, parameter), parameter); + Constructor = lambda.Compile(); + ZeroPrototype = Constructor(0); + } + + /// <summary> + /// The constructor for the generic type T. + /// </summary> + private static readonly Func<double, T> Constructor; + + /// <summary> + /// Creates the specified special SI object. + /// </summary> + /// <param name="val">The value of the SI object.</param> + [DebuggerStepThrough] + public static T Create(double val) + { + if (val == 0) { + return ZeroPrototype; + } + + return Constructor(val); + } + + [DebuggerStepThrough] + protected SIBase(double value) : base(value) {} + + [DebuggerStepThrough] + protected SIBase(double value, Unit[] numerator) : base(value) + { + Numerator = numerator; + } + + [DebuggerStepThrough] + protected SIBase(double value, Unit[] numerator, Unit[] denominator) : base(value) + { + Numerator = numerator; + Denominator = denominator; + } + + [DebuggerStepThrough] + public new T Abs() + { + return Create(Math.Abs(Val)); + } + + #region Operators + + /// <summary> + /// Implements the operator + for two specialized SI Classes. + /// </summary> + /// <param name="si1">The si1.</param> + /// <param name="si2">The si2.</param> + /// <returns> + /// The result of the operator. + /// </returns> + [DebuggerHidden] + public static T operator +(SIBase<T> si1, SIBase<T> si2) + { + return Create(si1.Val + si2.Val); + } + + /// <summary> + /// Implements the operator + for a specialized SI Class and a generic SI Class. + /// </summary> + /// <param name="si1">The si1.</param> + /// <param name="si2">The si2.</param> + /// <returns> + /// The result of the operator. + /// </returns> + [DebuggerHidden] + public static T operator +(SIBase<T> si1, SI si2) + { + return ((si1 as SI) + si2).Cast<T>(); + } + + /// <summary> + /// Implements the operator + for a generic SI Class and a specialized SI Class. + /// </summary> + /// <param name="si1">The si1.</param> + /// <param name="si2">The si2.</param> + /// <returns> + /// The result of the operator. + /// </returns> + [DebuggerHidden] + public static T operator +(SI si1, SIBase<T> si2) + { + return (si1 + (si2 as SI)).Cast<T>(); + } + + /// <summary> + /// Implements the unary operator -. + /// </summary> + /// <param name="si1">The si1.</param> + /// <returns> + /// The result of the operator. + /// </returns> + [DebuggerHidden] + public static T operator -(SIBase<T> si1) + { + return Create(-si1.Val); + } + + /// <summary> + /// Implements the operator - for two specialized SI classes. + /// </summary> + /// <param name="si1">The si1.</param> + /// <param name="si2">The si2.</param> + /// <returns> + /// The result of the operator. + /// </returns> + [DebuggerHidden] + public static T operator -(SIBase<T> si1, SIBase<T> si2) + { + return Create(si1.Val - si2.Val); + } + + /// <summary> + /// Implements the operator - for a specialized SI class and a generic SI class. + /// </summary> + /// <param name="si1">The si1.</param> + /// <param name="si2">The si2.</param> + /// <returns> + /// The result of the operator. + /// </returns> + [DebuggerHidden] + public static T operator -(SIBase<T> si1, SI si2) + { + return ((si1 as SI) - si2).Cast<T>(); + } + + /// <summary> + /// Implements the operator - for a generic SI class and a specialized SI class. + /// </summary> + /// <param name="si1">The si1.</param> + /// <param name="si2">The si2.</param> + /// <returns> + /// The result of the operator. + /// </returns> + [DebuggerHidden] + public static T operator -(SI si1, SIBase<T> si2) + { + return (si1 - (si2 as SI)).Cast<T>(); + } + + /// <summary> + /// Implements the operator * for a double and a specialized SI class. + /// </summary> + /// <param name="d">The double value.</param> + /// <param name="si">The si.</param> + /// <returns> + /// The result of the operator. + /// </returns> + [DebuggerHidden] + public static T operator *(double d, SIBase<T> si) + { + return Create(d * si.Val); + } + + /// <summary> + /// Implements the operator * for a specialized SI class and a double. + /// </summary> + /// <param name="si">The si.</param> + /// <param name="d">The double.</param> + /// <returns> + /// The result of the operator. + /// </returns> + [DebuggerHidden] + public static T operator *(SIBase<T> si, double d) + { + return Create(si.Val * d); + } + + /// <summary> + /// Implements the operator / for a specialized SI class and a double. + /// </summary> + /// <param name="si">The si.</param> + /// <param name="d">The double.</param> + /// <returns> + /// The result of the operator. + /// </returns> + [DebuggerHidden] + public static T operator /(SIBase<T> si, double d) + { + return Create(si.Val / d); + } + + [DebuggerHidden] + public static Scalar operator /(SIBase<T> si, SIBase<T> si2) + { + return SIBase<Scalar>.Create(si.Val / si2.Val); + } + + #endregion + } + + /// <summary> + /// Class for representing generic SI Units. + /// </summary> + /// <remarks> + /// Usage: new SI(1.0).Newton.Meter, new SI(2.3).Rounds.Per.Minute + /// </remarks> + [DebuggerDisplay("{Val}")] + public class SI : IComparable + { + /// <summary> + /// The basic scalar value of the SI. + /// </summary> + protected readonly double Val; + + /// <summary> + /// The denominator of the SI. + /// </summary> + protected Unit[] Denominator; + + /// <summary> + /// The numerator of the SI. + /// </summary> + protected Unit[] Numerator; + + /// <summary> + /// The current exponent for conversion operations (Square, Cubic, Linear, e.g. new SI(3).Square.Meter). + /// Can be reseted with Reset, Per, Cast. + /// </summary> + private readonly int _exponent; + + /// <summary> + /// A flag indicating if the current SI is in reciprocal mode (used in the <see cref="Per"/> method for reciprocal units: e.g. new SI(2).Meter.Per.Second) ==> [m/s] + /// Can be reseted with Reset, Per, Cast. + /// </summary> + private readonly bool _reciproc; + + /// <summary> + /// A flag indicating if the current SI is in reverse mode (used for conversions: e.g. new SI(2).Rounds.Per.Minute.ConverTo.Radian.Per.Second ==> [rpm/min] => [rad/s]). + /// </summary> + private readonly bool _reverse; + + /// <summary> + /// Enum for defining the Units. + /// </summary> + [SuppressMessage("ReSharper", "InconsistentNaming")] + protected enum Unit + { + k, + s, + m, + g, + W, + N, + min, + c, + d, + h, + milli, + t, + J, + Ampere, + NI, // norm liter + liter, + Volt + } + + /// <summary> + /// Initializes a new instance of the <see cref="SI"/> class without any units (dimensionless, scalar) [-]. + /// </summary> + /// <param name="val">The value.</param> + [DebuggerHidden] + public SI(double val = 0.0) + { + Val = val; + _reciproc = false; + _reverse = false; + Numerator = new Unit[0]; + Denominator = new Unit[0]; + _exponent = 1; + + if (double.IsNaN(val)) { + throw new VectoException("NaN [{0}] is not allowed for SI-Values in Vecto.", GetUnitString()); + } + + if (double.IsInfinity(Val)) { + throw new VectoException("Infinity [{0}] is not allowed for SI-Values in Vecto.", GetUnitString()); + } + } + + /// <summary> + /// Initializes a new instance of the <see cref="SI"/> class which allows to construct a new SI with all parameters. + /// </summary> + /// <param name="val">The value.</param> + /// <param name="numerator">The numerator.</param> + /// <param name="denominator">The denominator.</param> + /// <param name="reciproc">if set to <c>true</c> then the object is in reciproc mode (1/...)</param> + /// <param name="reverse">if set to <c>true</c> then the object is in reverse convertion mode (e.g. rpm/min => rad/s).</param> + /// <param name="exponent">The exponent for further conversions (e.g. Square.Meter).</param> + protected SI(double val, Unit[] numerator, Unit[] denominator, bool reciproc = false, + bool reverse = false, int exponent = 1) + { + Val = val; + _reciproc = reciproc; + _reverse = reverse; + _exponent = exponent; + + var tmpDenominator = denominator.ToList(); + Numerator = numerator.Where(n => !tmpDenominator.Remove(n)).ToArray(); + Denominator = tmpDenominator.ToArray(); + + if (double.IsNaN(Val)) { + throw new VectoException("NaN [{0}] is not allowed for SI-Values in Vecto.", GetUnitString()); + } + + if (double.IsInfinity(Val)) { + throw new VectoException("Infinity [{0}] is not allowed for SI-Values in Vecto.", GetUnitString()); + } + } + + /// <summary> + /// Initializes a new instance of the <see cref="SI"/> class which copies the units from an already existing SI. + /// </summary> + /// <param name="val">The value.</param> + /// <param name="unit">The unit.</param> + [DebuggerHidden] + private SI(double val, SI unit) : this(val, unit.Numerator, unit.Denominator) {} + + [DebuggerHidden] + protected SI(SI si, double? factor = null, Unit? fromUnit = null, Unit? toUnit = null, + bool? reciproc = null, bool? reverse = null, int? exponent = null) + { + var numerator = si.Denominator.ToList(); + var denominator = si.Numerator.ToList(); + + Val = si.Val; + _reciproc = reciproc ?? si._reciproc; + _reverse = reverse ?? si._reverse; + _exponent = exponent ?? si._exponent; + + // if reverse mode then swap fromUnit and toUnit and invert factor. + if (_reverse) { + var tmp = fromUnit; + fromUnit = toUnit; + toUnit = tmp; + factor = 1 / factor; + } + + // add the unit as often as is defined by the exponent. + for (var i = 0; i < _exponent; i++) { + if (!_reciproc) { + UpdateUnit(fromUnit, toUnit, denominator); + if (factor.HasValue) { + Val *= factor.Value; + } + } else { + UpdateUnit(fromUnit, toUnit, numerator); + if (factor.HasValue) { + Val /= factor.Value; + } + } + } + + Numerator = denominator.ToArray(); + Denominator = numerator.ToArray(); + + if (double.IsNaN(Val)) { + throw new VectoException("NaN [{0}] is not allowed for SI-Values in Vecto.", GetUnitString()); + } + + if (double.IsInfinity(Val)) { + throw new VectoException("Infinity [{0}] is not allowed for SI-Values in Vecto.", GetUnitString()); + } + } + + /// <summary> + /// Adds the new toUnit to the units collection and removes the fromUnit. + /// </summary> + /// <param name="fromUnit">From unit.</param> + /// <param name="toUnit">To unit.</param> + /// + /// <param name="units">The units.</param> + /// <exception cref="VectoException"></exception> + [DebuggerHidden] + private void UpdateUnit(Unit? fromUnit, Unit? toUnit, ICollection<Unit> units) + { + if (_reverse && fromUnit.HasValue) { + if (units.Contains(fromUnit.Value)) { + units.Remove(fromUnit.Value); + } else { + throw new VectoException("Unit missing. Conversion not possible. [{0}] does not contain a [{1}].", + string.Join(", ", units), + fromUnit); + } + } + + if (toUnit.HasValue) { + units.Add(toUnit.Value); + } + } + + /// <summary> + /// Converts the SI unit to another SI unit, defined by term(s) following after the ConvertTo(). + /// The Conversion Mode is active until an arithmetic operator is used (+,-,*,/), + /// or the .Value-Method, or the .Cast-Method were called. + /// ATTENTION: Before returning an SI Unit, ensure to cancel Conversion Mode (with or Cast). + /// </summary> + /// <returns></returns> + [DebuggerHidden] + public SI ConvertTo() + { + return new SI(Linear, reciproc: false, reverse: true); + } + + /// <summary> + /// Casts the SI Unit to the concrete unit type (if the units allow such an cast). + /// </summary> + /// <typeparam name="T">the specialized SI unit. e.g. Watt, NewtonMeter, Second</typeparam> + //[DebuggerHidden] + public T Cast<T>() where T : SIBase<T> + { + var si = ToBasicUnits(); + var t = SIBase<T>.Create(si.Val); + if (!si.HasEqualUnit(t)) { + throw new VectoException("SI Unit Conversion failed: From {0} to {1}", si, t); + } + return t; + } + + /// <summary> + /// Converts the derived SI units to the basic units and returns this as a new SI object. + /// </summary> + public SI ToBasicUnits() + { + var numerator = new List<Unit>(10); + var denominator = new List<Unit>(10); + + var numeratorFactor = 1.0; + for (var i = 0; i < Numerator.Length; i++) { + numeratorFactor *= ConvertToBasicUnits(Numerator[i], numerator, denominator); + } + var denominatorFactor = 1.0; + for (var i = 0; i < Denominator.Length; i++) { + denominatorFactor *= ConvertToBasicUnits(Denominator[i], denominator, numerator); + } + + return new SI(Val * numeratorFactor / denominatorFactor, numerator.ToArray(), denominator.ToArray()); + } + + /// <summary> + /// Converts to basic units. e.g [W] => [kgm²/s³] + /// </summary> + /// <param name="unit">The unit.</param> + /// <param name="numerator">The numerator.</param> + /// <param name="denominator">The denominator.</param> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static double ConvertToBasicUnits(Unit unit, List<Unit> numerator, List<Unit> denominator) + { + switch (unit) { + case Unit.W: + numerator.AddRange(new[] { Unit.k, Unit.g, Unit.m, Unit.m }); + denominator.AddRange(new[] { Unit.s, Unit.s, Unit.s }); + break; + case Unit.N: + numerator.AddRange(new[] { Unit.k, Unit.g, Unit.m }); + denominator.AddRange(new[] { Unit.s, Unit.s }); + break; + case Unit.t: + numerator.AddRange(new[] { Unit.k, Unit.g }); + return 1000; + case Unit.J: + numerator.AddRange(new[] { Unit.k, Unit.g, Unit.m, Unit.m }); + denominator.AddRange(new[] { Unit.s, Unit.s }); + break; + case Unit.min: + numerator.Add(Unit.s); + return 60; + default: + numerator.Add(unit); + break; + } + return 1; + } + + /// <summary> + /// Gets the underlying scalar double value. + /// </summary> + [DebuggerHidden] + public double Value() + { + return Val; + } + + /// <summary> + /// Clones this instance. + /// </summary> + public SI Clone() + { + return new SI(Val, Numerator, Denominator); + } + + /// <summary> + /// Returns the absolute value. + /// </summary> + public virtual SI Abs() + { + return new SI(Math.Abs(Val), this); + } + + /// <summary> + /// Returns the numerical sign of the SI. + /// </summary> + /// <returns>-1 if si < 0. 0 if si==0, 1 if si > 0.</returns> + [DebuggerHidden] + public int Sign() + { + return Math.Sign(Val); + } + + #region Unit Definitions + + /// <summary> + /// Defines the denominator by the terms following after the Per. + /// </summary> + [DebuggerHidden] + public SI Per + { + [DebuggerHidden] get { return new SI(Linear, reciproc: !_reciproc); } + } + + /// <summary> + /// Takes all following terms as cubic terms (=to the power of 3). + /// </summary> + [DebuggerHidden] + public SI Cubic + { + [DebuggerHidden] get { return new SI(this, exponent: 3); } + } + + /// <summary> + /// Takes all following terms as quadratic terms (=to the power of 2). + /// </summary> + [DebuggerHidden] + public SI Square + { + [DebuggerHidden] get { return new SI(this, exponent: 2); } + } + + /// <summary> + /// Takes all following terms as linear terms (=to the power of 1). + /// </summary> + [DebuggerHidden] + public SI Linear + { + [DebuggerHidden] get { return new SI(this, exponent: 1); } + } + + /// <summary> + /// [g] (to basic unit: [kg]) + /// </summary> + [DebuggerHidden] + public SI Gramm + { + [DebuggerHidden] get { return new SI(new SI(this, toUnit: Unit.k), 0.001, Unit.g, Unit.g); } + } + + [DebuggerHidden] + public SI Liter + { + [DebuggerHidden] get { return new SI(this, fromUnit: Unit.liter, toUnit: Unit.liter); } + } + + [DebuggerHidden] + public SI Joule + { + [DebuggerHidden] get { return new SI(this, fromUnit: Unit.J, toUnit: Unit.J); } + } + + /// <summary> + /// [t] (to basic unit: [kg]) + /// </summary> + [DebuggerHidden] + public SI Ton + { + [DebuggerHidden] get { return new SI(new SI(this, toUnit: Unit.k), 1000, Unit.t, Unit.g); } + } + + /// <summary> + /// [N] + /// </summary> + [DebuggerHidden] + public SI Newton + { + [DebuggerHidden] get { return new SI(this, fromUnit: Unit.N, toUnit: Unit.N); } + } + + /// <summary> + /// [W] + /// </summary> + [DebuggerHidden] + public SI Watt + { + [DebuggerHidden] get { return new SI(this, fromUnit: Unit.W, toUnit: Unit.W); } + } + + /// <summary> + /// [m] + /// </summary> + [DebuggerHidden] + public SI Meter + { + [DebuggerHidden] get { return new SI(this, fromUnit: Unit.m, toUnit: Unit.m); } + } + + /// <summary> + /// [s] + /// </summary> + [DebuggerHidden] + public SI Second + { + [DebuggerHidden] get { return new SI(this, fromUnit: Unit.s, toUnit: Unit.s); } + } + + /// <summary> + /// [-]. Defines radian. Only virtual. Has no real SI unit. + /// </summary> + [DebuggerHidden] + public SI Radian + { + [DebuggerHidden] get { return new SI(this); } + } + + /// <summary> + /// [-]. Converts to/from Radiant. Internally everything is stored in radian. + /// </summary> + [DebuggerHidden] + public SI Rounds + { + [DebuggerHidden] get { return new SI(this, 2 * Math.PI); } + } + + /// <summary> + /// [s] Converts to/from Second. Internally everything is stored in seconds. + /// </summary> + [DebuggerHidden] + public SI Hour + { + [DebuggerHidden] get { return new SI(this, 3600.0, Unit.h, Unit.s); } + } + + /// <summary> + /// [s] Converts to/from Second. Internally everything is stored in seconds. + /// </summary> + [DebuggerHidden] + public SI Minute + { + [DebuggerHidden] get { return new SI(this, 60.0, Unit.min, Unit.s); } + } + + /// <summary> + /// Quantifier for milli (1/1000). + /// </summary> + [DebuggerHidden] + public SI Milli + { + [DebuggerHidden] get { return new SI(this, 0.001, Unit.milli); } + } + + /// <summary> + /// Quantifier for Kilo (1000). + /// </summary> + [DebuggerHidden] + public SI Kilo + { + [DebuggerHidden] get { return new SI(this, 1000.0, Unit.k); } + } + + public SI Ampere + { + [DebuggerHidden] get { return new SI(this, 1.0, Unit.Ampere); } + } + + /// <summary> + /// Quantifier for Dezi (1/10) + /// </summary> + [DebuggerHidden] + public SI Dezi + { + [DebuggerHidden] get { return new SI(this, 0.1, Unit.d); } + } + + /// <summary> + /// Quantifier for Centi (1/100) + /// </summary> + [DebuggerHidden] + public SI Centi + { + [DebuggerHidden] get { return new SI(this, 0.01, Unit.c); } + } + + #endregion + + #region Operators + + /// <summary> + /// Implements the operator +. + /// </summary> + /// <param name="si1">The si1.</param> + /// <param name="si2">The si2.</param> + /// <returns> + /// The result of the operator. + /// </returns> + /// <exception cref="VectoException"></exception> + [DebuggerHidden] + public static SI operator +(SI si1, SI si2) + { + if (!si1.HasEqualUnit(si2)) { + throw new VectoException("Operator '+' can only operate on SI Objects with the same unit. Got: {0} + {1}", si1, si2); + } + + return new SI(si1.Val + si2.Val) { + Numerator = si1.Numerator, + Denominator = si1.Denominator + }; + } + + /// <summary> + /// Implements the operator -. + /// </summary> + /// <param name="si1">The si1.</param> + /// <param name="si2">The si2.</param> + /// <returns> + /// The result of the operator. + /// </returns> + /// <exception cref="VectoException"></exception> + [DebuggerHidden] + public static SI operator -(SI si1, SI si2) + { + if (!si1.HasEqualUnit(si2)) { + throw new VectoException("Operator '-' can only operate on SI Objects with the same unit. Got: {0} - {1}", si1, si2); + } + return new SI(si1.Val - si2.Val) { + Numerator = si1.Numerator, + Denominator = si1.Denominator + }; + } + + /// <summary> + /// Implements the operator -. + /// </summary> + /// <param name="si1">The si1.</param> + /// <returns> + /// The result of the operator. + /// </returns> + [DebuggerHidden] + public static SI operator -(SI si1) + { + return new SI(-si1.Val) { Numerator = si1.Numerator, Denominator = si1.Denominator }; + } + + /// <summary> + /// Implements the operator *. + /// </summary> + /// <param name="si1">The si1.</param> + /// <param name="si2">The si2.</param> + /// <returns> + /// The result of the operator. + /// </returns> + public static SI operator *(SI si1, SI si2) + { + var numerator = new Unit[si1.Numerator.Length + si2.Numerator.Length]; + Array.Copy(si1.Numerator, numerator, si1.Numerator.Length); + Array.Copy(si2.Numerator, 0, numerator, si1.Numerator.Length, si2.Numerator.Length); + + var denominator = new Unit[si1.Denominator.Length + si2.Denominator.Length]; + Array.Copy(si1.Denominator, denominator, si1.Denominator.Length); + Array.Copy(si2.Denominator, 0, denominator, si1.Denominator.Length, si2.Denominator.Length); + + return new SI(si1.Val * si2.Val, numerator, denominator); + } + + /// <summary> + /// Implements the operator *. + /// </summary> + /// <param name="si1">The si1.</param> + /// <param name="d">The d.</param> + /// <returns> + /// The result of the operator. + /// </returns> + [DebuggerHidden] + public static SI operator *(SI si1, double d) + { + return new SI(si1.Val * d) { Numerator = si1.Numerator, Denominator = si1.Denominator }; + } + + /// <summary> + /// Implements the operator *. + /// </summary> + /// <param name="d">The d.</param> + /// <param name="si1">The si1.</param> + /// <returns> + /// The result of the operator. + /// </returns> + [DebuggerHidden] + public static SI operator *(double d, SI si1) + { + return new SI(d * si1.Val) { Numerator = si1.Numerator, Denominator = si1.Denominator }; + } + + /// <summary> + /// Implements the operator /. + /// </summary> + /// <param name="si1">The si1.</param> + /// <param name="si2">The si2.</param> + /// <returns> + /// The result of the operator. + /// </returns> + public static SI operator /(SI si1, SI si2) + { + double result; + try { + result = si1.Val / si2.Val; + + // bad cases: Infinity = x / 0.0 (for x != 0), NaN = 0.0 / 0.0 + if (double.IsInfinity(result) || double.IsNaN(result)) { + throw new DivideByZeroException(); + } + } catch (DivideByZeroException ex) { + throw new VectoException( + string.Format("Can not compute division by zero ([{0}] / 0[{1}])", si1.GetUnitString(), si2.GetUnitString()), ex); + } + + var numerator = new Unit[si1.Numerator.Length + si2.Denominator.Length]; + Array.Copy(si1.Numerator, numerator, si1.Numerator.Length); + Array.Copy(si2.Denominator, 0, numerator, si1.Numerator.Length, si2.Denominator.Length); + + var denominator = new Unit[si1.Denominator.Length + si2.Numerator.Length]; + Array.Copy(si1.Denominator, denominator, si1.Denominator.Length); + Array.Copy(si2.Numerator, 0, denominator, si1.Denominator.Length, si2.Numerator.Length); + + return new SI(result, numerator, denominator); + } + + /// <summary> + /// Implements the operator /. + /// </summary> + /// <param name="si1">The si1.</param> + /// <param name="d">The d.</param> + /// <returns> + /// The result of the operator. + /// </returns> + [DebuggerHidden] + public static SI operator /(SI si1, double d) + { + if (d.IsEqual(0)) { + throw new VectoException(string.Format("Can not compute division by zero ([{0}] / 0)", si1.GetUnitString()), + new DivideByZeroException()); + } + + return new SI(si1.Val / d) { Numerator = si1.Numerator, Denominator = si1.Denominator }; + } + + /// <summary> + /// Implements the operator /. + /// </summary> + /// <param name="d">The d.</param> + /// <param name="si1">The si1.</param> + /// <returns> + /// The result of the operator. + /// </returns> + [DebuggerHidden] + public static SI operator /(double d, SI si1) + { + if (si1.IsEqual(0)) { + throw new VectoException(string.Format("Can not compute division by zero (x / 0[{0}])", si1.GetUnitString()), + new DivideByZeroException()); + } + + return new SI(d / si1.Val) { Numerator = si1.Denominator, Denominator = si1.Numerator }; + } + + /// <summary> + /// Implements the operator <. + /// </summary> + /// <param name="si1">The si1.</param> + /// <param name="si2">The si2.</param> + /// <returns> + /// The result of the operator. + /// </returns> + /// <exception cref="VectoException"></exception> + [DebuggerHidden] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator <(SI si1, SI si2) + { + if (!si1.HasEqualUnit(si2)) { + throw new VectoException("Operator '<' can only operate on SI Objects with the same unit. Got: {0} < {1}", si1, si2); + } + return si1.Val < si2.Val; + } + + /// <summary> + /// Implements the operator <. + /// </summary> + /// <param name="si1">The si1.</param> + /// <param name="d">The d.</param> + /// <returns> + /// The result of the operator. + /// </returns> + [DebuggerHidden] + public static bool operator <(SI si1, double d) + { + return si1 != null && si1.Val < d; + } + + /// <summary> + /// Implements the operator >. + /// </summary> + /// <param name="si1">The si1.</param> + /// <param name="si2">The si2.</param> + /// <returns> + /// The result of the operator. + /// </returns> + /// <exception cref="VectoException"></exception> + [DebuggerHidden] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator >(SI si1, SI si2) + { + if (!si1.HasEqualUnit(si2)) { + throw new VectoException("Operator '>' can only operate on SI Objects with the same unit. Got: {0} > {1}", si1, si2); + } + return si1.Val > si2.Val; + } + + /// <summary> + /// Implements the operator >. + /// </summary> + /// <param name="si1">The si1.</param> + /// <param name="d">The d.</param> + /// <returns> + /// The result of the operator. + /// </returns> + [DebuggerHidden] + public static bool operator >(SI si1, double d) + { + return si1 != null && si1.Val > d; + } + + /// <summary> + /// Implements the operator >. + /// </summary> + /// <param name="d">The d.</param> + /// <param name="si1">The si1.</param> + /// <returns> + /// The result of the operator. + /// </returns> + [DebuggerHidden] + public static bool operator >(double d, SI si1) + { + return si1 != null && d > si1.Val; + } + + /// <summary> + /// Implements the operator <. + /// </summary> + /// <param name="d">The d.</param> + /// <param name="si1">The si1.</param> + /// <returns> + /// The result of the operator. + /// </returns> + [DebuggerHidden] + public static bool operator <(double d, SI si1) + { + return si1 != null && d < si1.Val; + } + + /// <summary> + /// Implements the operator <=. + /// </summary> + /// <param name="si1">The si1.</param> + /// <param name="si2">The si2.</param> + /// <returns> + /// The result of the operator. + /// </returns> + /// <exception cref="VectoException"></exception> + [DebuggerHidden] + public static bool operator <=(SI si1, SI si2) + { + if (!si1.HasEqualUnit(si2)) { + throw new VectoException("Operator '<=' can only operate on SI Objects with the same unit. Got: {0} <= {1}", si1, + si2); + } + return si1.Val <= si2.Val; + } + + /// <summary> + /// Implements the operator <=. + /// </summary> + /// <param name="si1">The si1.</param> + /// <param name="d">The d.</param> + /// <returns> + /// The result of the operator. + /// </returns> + [DebuggerHidden] + public static bool operator <=(SI si1, double d) + { + return si1 != null && si1.Val <= d; + } + + /// <summary> + /// Implements the operator >=. + /// </summary> + /// <param name="si1">The si1.</param> + /// <param name="si2">The si2.</param> + /// <returns> + /// The result of the operator. + /// </returns> + /// <exception cref="VectoException"></exception> + [DebuggerHidden] + public static bool operator >=(SI si1, SI si2) + { + if (!si1.HasEqualUnit(si2)) { + throw new VectoException("Operator '>=' can only operate on SI Objects with the same unit. Got: {0} >= {1}", si1, + si2); + } + return si1.Val >= si2.Val; + } + + /// <summary> + /// Implements the operator >=. + /// </summary> + /// <param name="si1">The si1.</param> + /// <param name="d">The d.</param> + /// <returns> + /// The result of the operator. + /// </returns> + [DebuggerHidden] + public static bool operator >=(SI si1, double d) + { + return si1 != null && si1.Val >= d; + } + + /// <summary> + /// Implements the operator >=. + /// </summary> + /// <param name="d">The d.</param> + /// <param name="si1">The lower.</param> + /// <returns> + /// The result of the operator. + /// </returns> + [DebuggerHidden] + public static bool operator >=(double d, SI si1) + { + return si1 != null && d >= si1.Val; + } + + /// <summary> + /// Implements the operator <=. + /// </summary> + /// <param name="d">The d.</param> + /// <param name="si1">The lower.</param> + /// <returns> + /// The result of the operator. + /// </returns> + public static bool operator <=(double d, SI si1) + { + return si1 != null && d <= si1.Val; + } + + /// <summary> + /// Determines whether the SI is between lower and uppper bound. + /// </summary> + /// <param name="lower">The lower bound.</param> + /// <param name="upper">The upper bound.</param> + /// <returns></returns> + public bool IsBetween(SI lower, SI upper) + { + return lower <= Val && Val <= upper; + } + + /// <summary> + /// Determines whether the SI is between lower and uppper bound. + /// </summary> + /// <param name="lower">The lower bound.</param> + /// <param name="upper">The upper bound.</param> + /// <returns></returns> + public bool IsBetween(double lower, double upper) + { + return lower <= Val && Val <= upper; + } + + #endregion + + #region ToString + + /// <summary> + /// Returns the Unit Part of the SI Unit Expression. + /// </summary> + public string GetUnitString() + { + if (Denominator.Any()) { + if (Numerator.Any()) { + return string.Concat( + Numerator.GroupBy(x => x) + .Select(x => x.Count() == 1 ? x.Key.ToString() : string.Format("{0}^{1}", x.Key, x.Count()))) + + "/" + + string.Concat( + Denominator.GroupBy(x => x) + .Select(x => x.Count() == 1 ? x.Key.ToString() : string.Format("{0}^{1}", x.Key, x.Count()))); + } + return "1/" + string.Concat( + Denominator.GroupBy(x => x) + .Select(x => x.Count() == 1 ? x.Key.ToString() : string.Format("{0}^{1}", x.Key, x.Count()))); + } + + if (Numerator.Any()) { + return string.Concat( + Numerator.GroupBy(x => x) + .Select(x => x.Count() == 1 ? x.Key.ToString() : string.Format("{0}^{1}", x.Key, x.Count()))); + } + + return "-"; + } + + /// <summary> + /// Returns the String representation. + /// </summary> + public override string ToString() + { + return ToString(null); + } + + /// <summary> + /// Returns a <see cref="System.String" /> that represents this instance. + /// </summary> + /// <param name="format">The format.</param> + /// <returns> + /// A <see cref="System.String" /> that represents this instance. + /// </returns> + private string ToString(string format) + { + if (string.IsNullOrEmpty(format)) { + format = "F4"; + } + + return string.Format(CultureInfo.InvariantCulture, "{0:" + format + "} [{2}]", Val, format, GetUnitString()); + } + + #endregion + + #region Equality members + + /// <summary> + /// Compares the Unit-Parts of two SI Units. + /// </summary> + /// <param name="si">The si.</param> + /// <returns></returns> + [DebuggerHidden] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool HasEqualUnit(SI si) + { + if (Numerator.SequenceEqualFast(si.Numerator) && Denominator.SequenceEqualFast(si.Denominator)) { + return true; + } + + var self = ToBasicUnits(); + var other = si.ToBasicUnits(); + return self.Denominator.SequenceEqualFast(other.Denominator) && self.Numerator.SequenceEqualFast(other.Numerator); + } + + /// <summary> + /// Determines whether the specified <see cref="System.Object" />, is equal to this instance. + /// </summary> + /// <param name="obj">The <see cref="System.Object" /> to compare with this instance.</param> + /// <returns> + /// <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>. + /// </returns> + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) { + return false; + } + if (ReferenceEquals(this, obj)) { + return true; + } + var other = obj as SI; + return other != null && Val.Equals(other.Val) && HasEqualUnit(other); + } + + /// <summary> + /// Determines whether the specified si is equal. + /// </summary> + /// <param name="si">The si.</param> + /// <param name="tolerance">The tolerance.</param> + /// <returns></returns> + public bool IsEqual(SI si, SI tolerance = null) + { + return (tolerance == null || HasEqualUnit(tolerance)) && HasEqualUnit(si) && + Val.IsEqual(si.Val, tolerance == null ? DoubleExtensionMethods.Tolerance : tolerance.Value()); + } + + /// <summary> + /// Determines whether the specified value is equal. + /// </summary> + /// <param name="val">The value.</param> + /// <param name="tolerance">The tolerance.</param> + /// <returns></returns> + [DebuggerHidden] + public bool IsEqual(double val, double tolerance = DoubleExtensionMethods.Tolerance) + { + return Val.IsEqual(val, tolerance); + } + + /// <summary> + /// Determines whether the specified si is smaller. + /// </summary> + /// <param name="si">The si.</param> + /// <param name="tolerance">The tolerance.</param> + /// <returns></returns> + public bool IsSmaller(SI si, SI tolerance = null) + { + if (!HasEqualUnit(si)) { + throw new VectoException("compared value has to be the same unit. Got: {0} <=> {1}", this, si); + } + if (tolerance != null && !HasEqualUnit(tolerance)) { + throw new VectoException("tolerance has to be the same unit. Got: {0} <=> {1}", this, tolerance); + } + + return Val.IsSmaller(si.Val, tolerance == null ? DoubleExtensionMethods.Tolerance : tolerance.Value()); + } + + /// <summary> + /// Determines whether the specified si is smaller. + /// </summary> + /// <param name="si">The si.</param> + /// <param name="tolerance">The tolerance.</param> + /// <returns></returns> + public bool IsSmaller(SI si, double tolerance) + { + if (!HasEqualUnit(si)) { + throw new VectoException("compared value has to be the same unit. Got: {0} <=> {1}", this, si); + } + + return Val.IsSmaller(si.Val, tolerance); + } + + /// <summary> + /// Determines whether [is smaller or equal] [the specified si]. + /// </summary> + /// <param name="si">The si.</param> + /// <param name="tolerance">The tolerance.</param> + /// <returns></returns> + public bool IsSmallerOrEqual(SI si, SI tolerance = null) + { + if (!HasEqualUnit(si)) { + throw new VectoException("compared value has to be the same unit. Got: {0} <=> {1}", this, si); + } + if (tolerance != null && !HasEqualUnit(tolerance)) { + throw new VectoException("tolerance has to be the same unit. Got: {0} <=> {1}", this, tolerance); + } + + return Val.IsSmallerOrEqual(si.Val, tolerance == null ? DoubleExtensionMethods.Tolerance : tolerance.Value()); + } + + /// <summary> + /// Determines whether the specified si is greater. + /// </summary> + /// <param name="si">The si.</param> + /// <param name="tolerance">The tolerance.</param> + /// <returns></returns> + public bool IsGreater(SI si, SI tolerance = null) + { + if (!HasEqualUnit(si)) { + throw new VectoException("compared value has to be the same unit. Got: {0} <=> {1}", this, si); + } + if (tolerance != null && !HasEqualUnit(tolerance)) { + throw new VectoException("tolerance has to be the same unit. Got: {0} <=> {1}", this, tolerance); + } + + return Val.IsGreater(si.Val, tolerance == null ? DoubleExtensionMethods.Tolerance : tolerance.Value()); + } + + /// <summary> + /// Determines whether the specified si is greater. + /// </summary> + /// <param name="si">The si.</param> + /// <param name="tolerance">The tolerance.</param> + /// <returns></returns> + [DebuggerStepThrough] + public bool IsGreater(SI si, double tolerance) + { + if (!HasEqualUnit(si)) { + throw new VectoException("compared value has to be the same unit. Got: {0} <=> {1}", this, si); + } + + return Val.IsGreater(si.Val, tolerance); + } + + /// <summary> + /// Determines whether [is greater or equal] [the specified si]. + /// </summary> + /// <param name="si">The si.</param> + /// <param name="tolerance">The tolerance.</param> + /// <returns></returns> + [DebuggerStepThrough] + public bool IsGreaterOrEqual(SI si, SI tolerance = null) + { + if (!HasEqualUnit(si)) { + throw new VectoException("compared value has to be the same unit. Got: {0} <=> {1}", this, si); + } + if (tolerance != null && !HasEqualUnit(tolerance)) { + throw new VectoException("tolerance has to be the same unit. Got: {0} <=> {1}", this, tolerance); + } + + return Val.IsGreaterOrEqual(si.Val, tolerance == null ? DoubleExtensionMethods.Tolerance : tolerance.Value()); + } + + /// <summary> + /// Determines whether the specified value is smaller. + /// </summary> + /// <param name="val">The value.</param> + /// <param name="tolerance">The tolerance.</param> + /// <returns></returns> + [DebuggerStepThrough] + public bool IsSmaller(double val, double tolerance = DoubleExtensionMethods.Tolerance) + { + return Val.IsSmaller(val, tolerance); + } + + /// <summary> + /// Determines whether [is smaller or equal] [the specified value]. + /// </summary> + /// <param name="val">The value.</param> + /// <param name="tolerance">The tolerance.</param> + /// <returns></returns> + [DebuggerStepThrough] + public bool IsSmallerOrEqual(double val, double tolerance = DoubleExtensionMethods.Tolerance) + { + return Val.IsSmallerOrEqual(val, tolerance); + } + + /// <summary> + /// Determines whether the specified value is greater. + /// </summary> + /// <param name="val">The value.</param> + /// <param name="tolerance">The tolerance.</param> + /// <returns></returns> + [DebuggerStepThrough] + public bool IsGreater(double val, double tolerance = DoubleExtensionMethods.Tolerance) + { + return Val.IsGreater(val, tolerance); + } + + /// <summary> + /// Determines whether [is greater or equal] [the specified value]. + /// </summary> + /// <param name="val">The value.</param> + /// <param name="tolerance">The tolerance.</param> + /// <returns></returns> + [DebuggerStepThrough] + public bool IsGreaterOrEqual(double val, double tolerance = DoubleExtensionMethods.Tolerance) + { + return Val.IsGreaterOrEqual(val, tolerance); + } + + /// <summary> + /// Returns a hash code for this instance. + /// </summary> + /// <returns> + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// </returns> + public override int GetHashCode() + { + unchecked { + // ReSharper disable once NonReadonlyMemberInGetHashCode + var hashCode = Val.GetHashCode(); + hashCode = (hashCode * 397) ^ (Numerator != null ? Numerator.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (Denominator != null ? Denominator.GetHashCode() : 0); + return hashCode; + } + } + + /// <summary> + /// Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object. + /// </summary> + /// <param name="obj">An object to compare with this instance.</param> + /// <returns> + /// A value that indicates the relative order of the objects being compared. The return value has these meanings: Value Meaning Less than zero This instance precedes <paramref name="obj" /> in the sort order. Zero This instance occurs in the same position in the sort order as <paramref name="obj" />. Greater than zero This instance follows <paramref name="obj" /> in the sort order. + /// </returns> + public int CompareTo(object obj) + { + var si = obj as SI; + if (si == null) { + return 1; + } + + if (!HasEqualUnit(si)) { + if (si.Numerator.Length + si.Denominator.Length >= Numerator.Length + Denominator.Length) { + return -1; + } + return 1; + } + + if (this > si) { + return 1; + } + return this < si ? -1 : 0; + } + + /// <summary> + /// Implements the operator ==. + /// </summary> + /// <param name="left">The left.</param> + /// <param name="right">The right.</param> + /// <returns> + /// The result of the operator. + /// </returns> + public static bool operator ==(SI left, SI right) + { + return Equals(left, right); + } + + /// <summary> + /// Implements the operator !=. + /// </summary> + /// <param name="left">The left.</param> + /// <param name="right">The right.</param> + /// <returns> + /// The result of the operator. + /// </returns> + public static bool operator !=(SI left, SI right) + { + return !Equals(left, right); + } + + #endregion + + /// <summary> + /// Convert the SI to a string in the wished output format. + /// </summary> + /// <param name="decimals">The decimals.</param> + /// <param name="outputFactor">The output factor.</param> + /// <param name="showUnit">The show unit.</param> + /// <returns></returns> + public string ToOutputFormat(uint? decimals = null, double? outputFactor = null, bool? showUnit = null) + { + decimals = decimals ?? 4; + outputFactor = outputFactor ?? 1.0; + showUnit = showUnit ?? false; + + if (showUnit.Value) { + return (Val * outputFactor.Value).ToString("F" + decimals.Value, CultureInfo.InvariantCulture) + " [" + + GetUnitString() + "]"; + } + + return (Val * outputFactor.Value).ToString("F" + decimals.Value, CultureInfo.InvariantCulture); + } + + public string ToGUIFormat() + { + return Val.ToString(CultureInfo.InvariantCulture); + } + } +} \ No newline at end of file diff --git a/VectoSI/VectoCommon/VectoCommon.csproj b/VectoSI/VectoCommon/VectoCommon.csproj new file mode 100644 index 0000000000000000000000000000000000000000..12af2d49d765dfdb9adfb943c0992ba470a047e2 --- /dev/null +++ b/VectoSI/VectoCommon/VectoCommon.csproj @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{228258F5-D390-44CF-B326-9B0057833722}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>VectoCommon</RootNamespace> + <AssemblyName>VectoCommon</AssemblyName> + <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <ItemGroup> + <Reference Include="System" /> + <Reference Include="System.Core" /> + <Reference Include="System.Xml.Linq" /> + <Reference Include="System.Data.DataSetExtensions" /> + <Reference Include="Microsoft.CSharp" /> + <Reference Include="System.Data" /> + <Reference Include="System.Net.Http" /> + <Reference Include="System.Xml" /> + </ItemGroup> + <ItemGroup> + <Compile Include="DoubleExtensionMethods.cs" /> + <Compile Include="EnumerableExtensionMethods.cs" /> + <Compile Include="IntExtensionMethods.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="SI.cs" /> + <Compile Include="VectoExceptions.cs" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> +</Project> \ No newline at end of file diff --git a/VectoSI/VectoCommon/VectoExceptions.cs b/VectoSI/VectoCommon/VectoExceptions.cs new file mode 100644 index 0000000000000000000000000000000000000000..1e0d7c05a9e264f508dd54aec0df9bab1352fffd --- /dev/null +++ b/VectoSI/VectoCommon/VectoExceptions.cs @@ -0,0 +1,56 @@ +/* +* This file is part of VECTO. +* +* Copyright © 2012-2016 European Union +* +* Developed by Graz University of Technology, +* Institute of Internal Combustion Engines and Thermodynamics, +* Institute of Technical Informatics +* +* VECTO is licensed under the EUPL, Version 1.1 or - as soon they will be approved +* by the European Commission - subsequent versions of the EUPL (the "Licence"); +* You may not use VECTO except in compliance with the Licence. +* You may obtain a copy of the Licence at: +* +* https://joinup.ec.europa.eu/community/eupl/og_page/eupl +* +* Unless required by applicable law or agreed to in writing, VECTO +* distributed under the Licence is distributed on an "AS IS" basis, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the Licence for the specific language governing permissions and +* limitations under the Licence. +* +* Authors: +* Stefan Hausberger, hausberger@ivt.tugraz.at, IVT, Graz University of Technology +* Christian Kreiner, christian.kreiner@tugraz.at, ITI, Graz University of Technology +* Michael Krisper, michael.krisper@tugraz.at, ITI, Graz University of Technology +* Raphael Luz, luz@ivt.tugraz.at, IVT, Graz University of Technology +* Markus Quaritsch, markus.quaritsch@tugraz.at, IVT, Graz University of Technology +* Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology +*/ + +using System; +using System.Runtime.Serialization; + +namespace TUGraz.VectoCommon.Exceptions +{ + /// <summary> + /// Base Exception for all Exception in VECTO. + /// Simplified for Vecto SI Performance Project. + /// </summary> + [Serializable] + public class VectoException : Exception + { + protected VectoException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + + public VectoException(string message, Exception innerException) : base(message, innerException) + { + } + + public VectoException(string message, params object[] args) : base(string.Format(message, args)) + { + } + } +} \ No newline at end of file diff --git a/VectoSI/VectoSI.sln b/VectoSI/VectoSI.sln index baaa9edc0a09a701c76e5d6931ddeb5dcf5f69c4..26d487b5d9a246f8e3bd2bdebd939536d10d2f75 100644 --- a/VectoSI/VectoSI.sln +++ b/VectoSI/VectoSI.sln @@ -3,7 +3,25 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26228.10 MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VectoCommon", "VectoCommon\VectoCommon.csproj", "{228258F5-D390-44CF-B326-9B0057833722}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VectoTest", "VectoTest\VectoTest.csproj", "{9873B975-9C17-4CC9-AAD1-FFB2BDBBA0C2}" +EndProject Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {228258F5-D390-44CF-B326-9B0057833722}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {228258F5-D390-44CF-B326-9B0057833722}.Debug|Any CPU.Build.0 = Debug|Any CPU + {228258F5-D390-44CF-B326-9B0057833722}.Release|Any CPU.ActiveCfg = Release|Any CPU + {228258F5-D390-44CF-B326-9B0057833722}.Release|Any CPU.Build.0 = Release|Any CPU + {9873B975-9C17-4CC9-AAD1-FFB2BDBBA0C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9873B975-9C17-4CC9-AAD1-FFB2BDBBA0C2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9873B975-9C17-4CC9-AAD1-FFB2BDBBA0C2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9873B975-9C17-4CC9-AAD1-FFB2BDBBA0C2}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection diff --git a/VectoSI/VectoTest/AssertHelper.cs b/VectoSI/VectoTest/AssertHelper.cs new file mode 100644 index 0000000000000000000000000000000000000000..ed7cbd9b2321cb54d63aa6bd382a70339544cbd2 --- /dev/null +++ b/VectoSI/VectoTest/AssertHelper.cs @@ -0,0 +1,110 @@ +/* +* This file is part of VECTO. +* +* Copyright © 2012-2016 European Union +* +* Developed by Graz University of Technology, +* Institute of Internal Combustion Engines and Thermodynamics, +* Institute of Technical Informatics +* +* VECTO is licensed under the EUPL, Version 1.1 or - as soon they will be approved +* by the European Commission - subsequent versions of the EUPL (the "Licence"); +* You may not use VECTO except in compliance with the Licence. +* You may obtain a copy of the Licence at: +* +* https://joinup.ec.europa.eu/community/eupl/og_page/eupl +* +* Unless required by applicable law or agreed to in writing, VECTO +* distributed under the Licence is distributed on an "AS IS" basis, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the Licence for the specific language governing permissions and +* limitations under the Licence. +* +* Authors: +* Stefan Hausberger, hausberger@ivt.tugraz.at, IVT, Graz University of Technology +* Christian Kreiner, christian.kreiner@tugraz.at, ITI, Graz University of Technology +* Michael Krisper, michael.krisper@tugraz.at, ITI, Graz University of Technology +* Raphael Luz, luz@ivt.tugraz.at, IVT, Graz University of Technology +* Markus Quaritsch, markus.quaritsch@tugraz.at, IVT, Graz University of Technology +* Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology +*/ + +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Diagnostics; +using System.Globalization; +using TUGraz.VectoCommon.Utils; + +namespace TUGraz.VectoCore.Tests.Utils +{ + public static class AssertHelper + { + /// <summary> + /// Assert an expected Exception. + /// </summary> + [DebuggerHidden] + public static void Exception<T>(this Action func, string message = null) where T : Exception + { + try { + func(); + Assert.Fail("Expected Exception {0}, but no exception occured.", typeof(T)); + } catch (T ex) { + if (message != null) { + Assert.AreEqual(message, ex.Message); + } + } + } + + [DebuggerHidden] + public static void AreRelativeEqual(SI expected, SI actual, string message = null, + double toleranceFactor = DoubleExtensionMethods.ToleranceFactor) + { + Assert.IsTrue(actual.HasEqualUnit(expected), + string.Format("Wrong SI Units: expected: {0}, actual: {1}", expected.ToBasicUnits(), actual.ToBasicUnits())); + AreRelativeEqual(expected.Value(), actual.Value(), toleranceFactor: toleranceFactor, message: message); + } + + [DebuggerHidden] + public static void AreRelativeEqual(Scalar expected, Scalar actual, + double toleranceFactor = DoubleExtensionMethods.ToleranceFactor) + { + Assert.IsTrue(expected.HasEqualUnit(new SI()) && actual.HasEqualUnit(new SI()), "Units of Scalars must be empty."); + AreRelativeEqual(expected.Value(), actual.Value(), toleranceFactor: toleranceFactor); + } + + [DebuggerHidden] + public static void AreRelativeEqual(double? expected, SI actual, + double toleranceFactor = DoubleExtensionMethods.ToleranceFactor) + { + if (expected.HasValue) { + AreRelativeEqual(expected.Value, actual.Value(), toleranceFactor: toleranceFactor); + } else { + Assert.IsNull(actual, "Both Values have to be null or not null."); + } + } + + [DebuggerHidden] + public static void AreRelativeEqual(double? expected, double? actual, string message = null, + double toleranceFactor = DoubleExtensionMethods.ToleranceFactor) + { + if (!string.IsNullOrWhiteSpace(message)) { + message = "\n" + message; + } else { + message = ""; + } + + Assert.IsFalse(expected.HasValue ^ actual.HasValue, "Both Values have to be null or not null."); + + if (double.IsNaN(expected.Value)) { + Assert.IsTrue(double.IsNaN(actual.Value), + string.Format("Actual value is not NaN. Expected: {0}, Actual: {1}{2}", expected, actual, message)); + return; + } + + var ratio = expected == 0 ? Math.Abs(actual.Value) : Math.Abs(actual.Value / expected.Value - 1); + Assert.IsTrue(ratio < toleranceFactor, string.Format(CultureInfo.InvariantCulture, + "Given values are not equal. Expected: {0}, Actual: {1}, Difference: {3} (Tolerance Factor: {2}){4}", + expected, actual, toleranceFactor, expected - actual, message)); + } + } +} \ No newline at end of file diff --git a/VectoSI/VectoTest/Properties/AssemblyInfo.cs b/VectoSI/VectoTest/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000000000000000000000000000000000..a91d0382aa6a6c5ea06e943e7a59ce8519f2e3c6 --- /dev/null +++ b/VectoSI/VectoTest/Properties/AssemblyInfo.cs @@ -0,0 +1,20 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("VectoTest")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("VectoTest")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: Guid("9873b975-9c17-4cc9-aad1-ffb2bdbba0c2")] + +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/VectoSI/VectoTest/SITest.cs b/VectoSI/VectoTest/SITest.cs new file mode 100644 index 0000000000000000000000000000000000000000..311dab6b955259a61409608ec1523091b6d96bf1 --- /dev/null +++ b/VectoSI/VectoTest/SITest.cs @@ -0,0 +1,518 @@ +/* +* This file is part of VECTO. +* +* Copyright © 2012-2016 European Union +* +* Developed by Graz University of Technology, +* Institute of Internal Combustion Engines and Thermodynamics, +* Institute of Technical Informatics +* +* VECTO is licensed under the EUPL, Version 1.1 or - as soon they will be approved +* by the European Commission - subsequent versions of the EUPL (the "Licence"); +* You may not use VECTO except in compliance with the Licence. +* You may obtain a copy of the Licence at: +* +* https://joinup.ec.europa.eu/community/eupl/og_page/eupl +* +* Unless required by applicable law or agreed to in writing, VECTO +* distributed under the Licence is distributed on an "AS IS" basis, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the Licence for the specific language governing permissions and +* limitations under the Licence. +* +* Authors: +* Stefan Hausberger, hausberger@ivt.tugraz.at, IVT, Graz University of Technology +* Christian Kreiner, christian.kreiner@tugraz.at, ITI, Graz University of Technology +* Michael Krisper, michael.krisper@tugraz.at, ITI, Graz University of Technology +* Raphael Luz, luz@ivt.tugraz.at, IVT, Graz University of Technology +* Markus Quaritsch, markus.quaritsch@tugraz.at, IVT, Graz University of Technology +* Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology +*/ + +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using TUGraz.VectoCommon.Exceptions; +using TUGraz.VectoCommon.Utils; + +namespace TUGraz.VectoCore.Tests.Utils +{ + [TestClass] + public class SITest + { + [TestMethod] + public void SI_TypicalUsage() + { + //mult + var angularVelocity = 600.RPMtoRad(); + var torque = 1500.SI<NewtonMeter>(); + var power = angularVelocity * torque; + Assert.IsInstanceOfType(power, typeof(Watt)); + Assert.AreEqual(600.0 / 60 * 2 * Math.PI * 1500, power.Value()); + + var siStandardMult = power * torque; + Assert.IsInstanceOfType(siStandardMult, typeof(SI)); + Assert.AreEqual(600.0 / 60 * 2 * Math.PI * 1500 * 1500, siStandardMult.Value()); + Assert.IsTrue(siStandardMult.HasEqualUnit(new SI().Watt.Newton.Meter)); + + //div + var torque2 = power / angularVelocity; + Assert.IsInstanceOfType(torque2, typeof(NewtonMeter)); + Assert.AreEqual(1500, torque2.Value()); + + var siStandardDiv = power / power; + Assert.IsInstanceOfType(siStandardMult, typeof(SI)); + Assert.IsTrue(siStandardDiv.HasEqualUnit(new SI())); + Assert.AreEqual(600.0 / 60 * 2 * Math.PI * 1500 * 1500, siStandardMult.Value()); + + var force = torque / 100.SI<Meter>(); + Assert.IsInstanceOfType(force, typeof(Newton)); + Assert.AreEqual(15, force.Value()); + + var test = 2.0.SI<PerSecond>(); + var reziprok = 1.0 / test; + Assert.AreEqual(0.5, reziprok.Value()); + Assert.IsTrue(1.SI<Second>().HasEqualUnit(reziprok)); + + //add + PerSecond angVeloSum = 600.RPMtoRad() + 400.SI<PerSecond>(); + AssertHelper.AreRelativeEqual(600 * 2 * Math.PI / 60 + 400, angVeloSum); + AssertHelper.Exception<VectoException>(() => { var x = 500.SI().Watt + 300.SI().Newton; }); + + //subtract + PerSecond angVeloDiff = 600.RPMtoRad() - 400.SI<PerSecond>(); + AssertHelper.AreRelativeEqual(600 * 2 * Math.PI / 60 - 400, angVeloDiff); + + //general si unit + var generalSIUnit = 3600000000.0.SI().Gramm.Per.Kilo.Watt.Hour.ConvertTo().Kilo.Gramm.Per.Watt.Second; + Assert.IsInstanceOfType(generalSIUnit, typeof(SI)); + Assert.AreEqual(1, generalSIUnit.Value()); + + //type conversion + var engineSpeed = 600.0; + PerSecond angularVelocity3 = engineSpeed.RPMtoRad(); + + // convert between units measures + var angularVelocity4 = engineSpeed.SI().Rounds.Per.Minute.ConvertTo().Radian.Per.Second; + Assert.IsInstanceOfType(angularVelocity4, typeof(SI)); + + // cast SI to specialized unit classes. + PerSecond angularVelocity5 = angularVelocity4.Cast<PerSecond>(); + Assert.AreEqual(angularVelocity3, angularVelocity5); + Assert.AreEqual(angularVelocity3.Value(), angularVelocity4.Value()); + + // ConvertTo only allows conversion if the units are correct. + AssertHelper.Exception<VectoException>(() => { var x = 40.SI<Newton>().ConvertTo().Watt; }); + var res1 = 40.SI<Newton>().ConvertTo().Newton; + + // Cast only allows the cast if the units are correct. + AssertHelper.Exception<VectoException>(() => { var x = 40.SI().Newton.Cast<Watt>(); }); + var res2 = 40.SI().Newton.Cast<Newton>(); + } + + [TestMethod] + public void SI_ConstructorPerformance() + { + for (var i = 0; i < 5e5; i++) + { + var si = i.SI(); + var meter = i.SI<Meter>(); + var watt = i.SI<Watt>(); + var perSecond = i.SI<PerSecond>(); + var meterPerSecond = i.SI<MeterPerSecond>(); + var second = i.SI<Second>(); + var newton = i.SI<Newton>(); + var kilogram = i.SI<Kilogram>(); + var squareMeter = i.SI<SquareMeter>(); + var scalar = i.SI<Scalar>(); + var compound = i.SI().Kilo.Gramm.Square.Meter.Per.Cubic.Second.Cast<Watt>(); + } + } + + [TestMethod] + public void SI_SpecialArithmeticPerformance() + { + var transmissionCoefficient = 2.8; + for (var i = 0; i < 2e5; i++) + { + var angularVelocity = 1.5.SI<PerSecond>(); + var torque = 50.SI<Newton>(); + var power = torque * angularVelocity; + + var outAngularVelocity = angularVelocity / transmissionCoefficient; + var outTorque = torque * transmissionCoefficient; + + var outPower = outTorque * outAngularVelocity; + + var averagePower = (power + outPower) / 2; + AssertHelper.AreRelativeEqual(outPower, power); + AssertHelper.AreRelativeEqual(averagePower, power); + } + } + + [TestMethod] + public void SI_CheckForEqualUnit() + { + var si1 = 5.SI<NewtonMeter>(); + var si2 = 5.SI().Newton.Meter; + for (var i = 0; i < 1e7; i++) + { + si1.HasEqualUnit(si2); + } + + var si3 = 5.SI<NewtonMeter>(); + var si4 = 5.SI<Kilogram>(); + for (var i = 0; i < 2e6; i++) + { + si3.HasEqualUnit(si4); + } + + + + } + + + [TestMethod] + public void SI_NeutralArithmeticPerformance() + { + var transmissionCoefficient = 2.8; + for (var i = 0; i < 2e5; i++) + { + var angularVelocity = 1.5.SI().Per.Second; + var torque = 50.SI().Newton; + var power = torque * angularVelocity; + + var outAngularVelocity = angularVelocity / transmissionCoefficient; + var outTorque = torque * transmissionCoefficient; + + var outPower = outTorque * outAngularVelocity; + + var averagePower = (power + outPower) / 2; + AssertHelper.AreRelativeEqual(outPower, power); + AssertHelper.AreRelativeEqual(averagePower, power); + } + } + + [TestMethod] + public void SI_CreateConvert() + { + var si = new SI(); + Assert.AreEqual(0.0, si.Value()); + Assert.AreEqual("0.0000 [-]", si.ToString()); + Assert.IsTrue(si.HasEqualUnit(new SI())); + + var si2 = 5.SI().Watt; + Assert.AreEqual("5.0000 [W]", si2.ToString()); + + var si3 = 2.SI().Radian.Per.Second; + Assert.AreEqual("2.0000 [1/s]", si3.ToString()); + + var si4 = si2 * si3; + Assert.AreEqual("10.0000 [W/s]", si4.ToString()); + Assert.IsTrue(si4.HasEqualUnit(new SI().Watt.Per.Second)); + Assert.AreEqual("10.0000 [kgm^2/s^4]", si4.ToBasicUnits().ToString()); + + var kg = 5.SI().Kilo.Gramm; + Assert.AreEqual(5.0, kg.Value()); + Assert.AreEqual("5.0000 [kg]", kg.ToString()); + + kg = kg.ConvertTo().Kilo.Gramm.Clone(); + Assert.AreEqual(5.0, kg.Value()); + Assert.AreEqual("5.0000 [kg]", kg.ToString()); + + kg = kg.ConvertTo().Gramm.Clone(); + Assert.AreEqual(5000, kg.Value()); + Assert.AreEqual("5000.0000 [g]", kg.ToString()); + + var x = 5.SI(); + Assert.AreEqual((2.0 / 5.0).SI(), 2 / x); + Assert.AreEqual((5.0 / 2.0).SI(), x / 2); + Assert.AreEqual((2.0 * 5.0).SI(), 2 * x); + Assert.AreEqual((5.0 * 2.0).SI(), x * 2); + + Assert.AreEqual((2.0 / 5.0).SI(), 2.0 / x); + Assert.AreEqual((5.0 / 2.0).SI(), x / 2.0); + Assert.AreEqual((2 * 5).SI(), 2.0 * x); + Assert.AreEqual((5 * 2).SI(), x * 2.0); + + Assert.AreEqual(45.0 / 180.0 * Math.PI, Math.Atan(1).SI<Radian>().Value(), 0.000001); + } + + [TestMethod] + public void SI_Comparison_Operators() + { + var v1 = 600.SI<NewtonMeter>(); + var v2 = 455.SI<NewtonMeter>(); + var v3 = 600.SI<NewtonMeter>(); + var v4 = 100.SI<Watt>(); + var d = 700; + + Assert.IsTrue(v1 > v2); + Assert.IsFalse(v1 < v2); + AssertHelper.Exception<VectoException>(() => { var x = v1 < v4; }, + "Operator '<' can only operate on SI Objects with the same unit. Got: 600.0000 [Nm] < 100.0000 [W]"); + AssertHelper.Exception<VectoException>(() => { var x = v1 > v4; }, + "Operator '>' can only operate on SI Objects with the same unit. Got: 600.0000 [Nm] > 100.0000 [W]"); + AssertHelper.Exception<VectoException>(() => { var x = v1 <= v4; }, + "Operator '<=' can only operate on SI Objects with the same unit. Got: 600.0000 [Nm] <= 100.0000 [W]"); + AssertHelper.Exception<VectoException>(() => { var x = v1 >= v4; }, + "Operator '>=' can only operate on SI Objects with the same unit. Got: 600.0000 [Nm] >= 100.0000 [W]"); + + SI si = null; + Assert.IsFalse(si > 3); + Assert.IsFalse(si < 3); + Assert.IsFalse(si >= 3); + Assert.IsFalse(si <= 3); + + Assert.IsFalse(3 > si); + Assert.IsFalse(3 < si); + Assert.IsFalse(si >= 3); + Assert.IsFalse(si <= 3); + + Assert.IsTrue(v2 < v1); + Assert.IsFalse(v2 > v1); + + Assert.IsTrue(v1 >= v2); + Assert.IsFalse(v1 <= v2); + + Assert.IsTrue(v2 <= v1); + Assert.IsFalse(v2 >= v1); + + Assert.IsTrue(v1 <= v3); + Assert.IsTrue(v1 >= v3); + + Assert.IsTrue(v1 < d); + Assert.IsFalse(v1 > d); + Assert.IsFalse(v1 >= d); + Assert.IsTrue(v1 <= d); + + Assert.AreEqual(1, new SI().CompareTo(null)); + Assert.AreEqual(1, new SI().CompareTo("not an SI")); + Assert.AreEqual(-1, new SI().Meter.CompareTo(new SI().Kilo.Meter.Per.Hour)); + Assert.AreEqual(1, new SI().Newton.Meter.CompareTo(new SI().Meter)); + + Assert.AreEqual(0, 1.SI().CompareTo(1.SI())); + Assert.AreEqual(-1, 1.SI().CompareTo(2.SI())); + Assert.AreEqual(1, 2.SI().CompareTo(1.SI())); + } + + [TestMethod] + public void SI_Addition_Subtraction() + { + AssertHelper.AreRelativeEqual(3.SI(), 1.SI() + 2.SI()); + AssertHelper.AreRelativeEqual(-1.SI(), 1.SI() - 2.SI()); + + AssertHelper.AreRelativeEqual(3.SI<Scalar>(), 1.SI<Scalar>() + 2.SI<Scalar>()); + AssertHelper.AreRelativeEqual(3.SI<Scalar>(), 1 + 2.SI<Scalar>()); + AssertHelper.AreRelativeEqual(3.SI<Scalar>(), 1.SI<Scalar>() + 2); + AssertHelper.AreRelativeEqual(-1.SI<Scalar>(), 1.SI<Scalar>() - 2.SI<Scalar>()); + AssertHelper.AreRelativeEqual(-1.SI<Scalar>(), 1 - 2.SI<Scalar>()); + AssertHelper.AreRelativeEqual(-1.SI<Scalar>(), 1.SI<Scalar>() - 2); + + AssertHelper.AreRelativeEqual(3.SI<NewtonMeter>(), 1.SI<NewtonMeter>() + 2.SI<NewtonMeter>()); + AssertHelper.AreRelativeEqual(-1.SI<NewtonMeter>(), 1.SI<NewtonMeter>() - 2.SI<NewtonMeter>()); + + AssertHelper.AreRelativeEqual(3.SI<NewtonMeter>(), 1.SI().Newton.Meter + 2.SI<NewtonMeter>()); + AssertHelper.AreRelativeEqual(-1.SI<NewtonMeter>(), 1.SI().Newton.Meter - 2.SI<NewtonMeter>()); + + AssertHelper.AreRelativeEqual(3.SI<NewtonMeter>(), 1.SI<NewtonMeter>() + 2.SI().Newton.Meter); + AssertHelper.AreRelativeEqual(-1.SI<NewtonMeter>(), 1.SI<NewtonMeter>() - 2.SI().Newton.Meter); + + AssertHelper.Exception<VectoException>(() => { var x = 1.SI().Second - 1.SI<Meter>(); }, + "Operator '-' can only operate on SI Objects with the same unit. Got: 1.0000 [s] - 1.0000 [m]"); + } + + [TestMethod] + public void SI_SpecialUnits() + { + Scalar scalar = 3.SI<Scalar>(); + AssertHelper.AreRelativeEqual(3.SI(), scalar); + double scalarDouble = scalar; + AssertHelper.AreRelativeEqual(3, scalarDouble); + + MeterPerSecond meterPerSecond = 2.SI<MeterPerSecond>(); + AssertHelper.AreRelativeEqual(2.SI().Meter.Per.Second, meterPerSecond); + + Second second = 1.SI<Second>(); + AssertHelper.AreRelativeEqual(1.SI().Second, second); + + Watt watt = 2.SI<Watt>(); + AssertHelper.AreRelativeEqual(2.SI().Watt, watt); + + PerSecond perSecond = 1.SI<PerSecond>(); + AssertHelper.AreRelativeEqual(1.SI().Per.Second, perSecond); + + SI rpm = 20.SI().Rounds.Per.Minute; + AssertHelper.AreRelativeEqual(20.SI().Rounds.Per.Minute, rpm); + AssertHelper.AreRelativeEqual(20.RPMtoRad(), rpm); + AssertHelper.AreRelativeEqual(2.0943951023931953, rpm); + + Radian radian = 30.SI<Radian>(); + AssertHelper.AreRelativeEqual(30.SI().Radian, radian); + AssertHelper.AreRelativeEqual(30, radian); + + Newton newton = 3.SI<Newton>(); + AssertHelper.AreRelativeEqual(3.SI().Newton, newton); + + NewtonMeter newtonMeter = 5.SI<NewtonMeter>(); + AssertHelper.AreRelativeEqual(5.SI().Newton.Meter, newtonMeter); + AssertHelper.AreRelativeEqual(5.SI().Meter.Newton, newtonMeter); + + MeterPerSquareSecond meterPerSquareSecond = 3.SI<MeterPerSquareSecond>(); + AssertHelper.AreRelativeEqual(3.SI().Meter.Per.Square.Second, meterPerSquareSecond); + + Kilogram kilogram = 3.SI<Kilogram>(); + AssertHelper.AreRelativeEqual(3.SI().Kilo.Gramm, kilogram); + AssertHelper.AreRelativeEqual(3, kilogram); + + SquareMeter squareMeter = 3.SI<SquareMeter>(); + AssertHelper.AreRelativeEqual(3.SI().Square.Meter, squareMeter); + + CubicMeter cubicMeter = 3.SI<CubicMeter>(); + AssertHelper.AreRelativeEqual(3.SI().Cubic.Meter, cubicMeter); + + KilogramSquareMeter kilogramSquareMeter = 3.SI<KilogramSquareMeter>(); + AssertHelper.AreRelativeEqual(3.SI().Kilo.Gramm.Square.Meter, kilogramSquareMeter); + + KilogramPerWattSecond kilogramPerWattSecond = 3.SI<KilogramPerWattSecond>(); + AssertHelper.AreRelativeEqual(3.SI().Kilo.Gramm.Per.Watt.Second, kilogramPerWattSecond); + } + + [TestMethod] + public void SI_ReziprokDivision() + { + var test = 2.0.SI<Second>(); + + var actual = 1.0 / test; + var expected = 0.5.SI<PerSecond>(); + + AssertHelper.AreRelativeEqual(expected, actual); + } + + [TestMethod] + public void SI_Multiplication_Division() + { + AssertHelper.AreRelativeEqual(12.SI(), 3.SI() * 4.SI()); + AssertHelper.AreRelativeEqual(12.SI(), 3 * 4.SI()); + AssertHelper.AreRelativeEqual(12.SI(), 3.SI() * 4); + + AssertHelper.AreRelativeEqual(12.SI<NewtonMeter>(), 3.SI<Newton>() * 4.SI<Meter>()); + AssertHelper.AreRelativeEqual(12.SI<NewtonMeter>(), 3 * 4.SI<NewtonMeter>()); + AssertHelper.AreRelativeEqual(12.SI<NewtonMeter>(), 3.SI<NewtonMeter>() * 4); + AssertHelper.AreRelativeEqual(12.SI().Square.Newton.Meter, 3.SI<NewtonMeter>() * 4.SI<NewtonMeter>()); + + AssertHelper.AreRelativeEqual(3.SI(), 12.SI() / 4); + AssertHelper.AreRelativeEqual(3.SI(), 12.SI() / 4.SI()); + AssertHelper.AreRelativeEqual(3.SI(), 12.SI<NewtonMeter>() / 4.SI<NewtonMeter>()); + + AssertHelper.AreRelativeEqual(3.SI<NewtonMeter>(), 12.SI<NewtonMeter>() / 4); + AssertHelper.AreRelativeEqual(3.SI().Per.Newton.Meter, 12 / 4.SI<NewtonMeter>()); + + var newtonMeter = 10.SI<NewtonMeter>(); + var perSecond = 5.SI<PerSecond>(); + var watt = (10 * 5).SI<Watt>(); + var second = (1.0 / 5.0).SI<Second>(); + + AssertHelper.AreRelativeEqual(watt, newtonMeter * perSecond); + AssertHelper.AreRelativeEqual(watt, perSecond * newtonMeter); + + AssertHelper.AreRelativeEqual(newtonMeter, watt / perSecond); + AssertHelper.AreRelativeEqual(perSecond, watt / newtonMeter); + + AssertHelper.AreRelativeEqual(second, newtonMeter / watt); + } + + [TestMethod] + public void SI_MeterPerSecond_Div_Meter() + { + PerSecond actual = 6.SI<MeterPerSecond>() / 2.SI<Meter>(); + AssertHelper.AreRelativeEqual(3.SI().Per.Second, actual); + } + + [TestMethod] + public void SI_SimplifyUnits() + { + AssertHelper.AreRelativeEqual(3.SI(), 18.SI().Kilo.Gramm / 6.SI().Kilo.Gramm); + AssertHelper.AreRelativeEqual(3.SI(), 18.SI<NewtonMeter>() / 6.SI<NewtonMeter>()); + + AssertHelper.AreRelativeEqual(18.SI(), 3.SI().Kilo.Gramm * 6.SI().Per.Kilo.Gramm); + AssertHelper.AreRelativeEqual(18.SI<Meter>(), 3.SI().Kilo.Gramm.Meter * 6.SI().Per.Kilo.Gramm); + + AssertHelper.AreRelativeEqual(3.SI().Kilo.Gramm.Square.Meter.Per.Cubic.Second, 3.SI<Watt>()); + AssertHelper.AreRelativeEqual(3.SI().Kilo.Gramm.Meter.Per.Square.Second, 3.SI<Newton>()); + AssertHelper.AreRelativeEqual(3000.SI().Kilo.Gramm, 3.SI().Ton); + AssertHelper.AreRelativeEqual(3.SI().Kilo.Kilo.Gramm.ConvertTo().Ton, 3000.SI().Kilo.Gramm.ConvertTo().Ton); + + AssertHelper.AreRelativeEqual(3.SI<Meter>(), 3000.SI().Milli.Meter); + + AssertHelper.AreRelativeEqual(36.SI().Square.Newton.Meter, 6.SI<NewtonMeter>() * 6.SI<NewtonMeter>()); + AssertHelper.AreRelativeEqual(36.SI().Newton.Newton.Meter.Meter, 6.SI<NewtonMeter>() * 6.SI<NewtonMeter>()); + + AssertHelper.AreRelativeEqual(3.SI().Meter.Per.Second, 3.SI<Newton>().Second.Per.Kilo.Gramm); + } + + [TestMethod] + public void SI_Math() + { + AssertHelper.AreRelativeEqual(-3, -3.SI().Value()); + AssertHelper.AreRelativeEqual(3.SI(), (-3).SI().Abs()); + } + + [TestMethod] + public void SI_Equality() + { + Assert.AreEqual(3.SI(), 3.SI()); + Assert.AreEqual(3.SI<NewtonMeter>(), 3.SI<NewtonMeter>()); + + Assert.IsFalse(3.SI<NewtonMeter>().IsEqual(4.SI<NewtonMeter>())); + Assert.IsFalse(3.SI<NewtonMeter>().IsEqual(3.SI<Meter>())); + + Assert.IsTrue(3.SI().IsEqual(4, 10)); + + var x = 4.SI(); + var y = x; + var z = 4.SI(); + Assert.IsTrue(x.Equals(y)); + + Assert.IsFalse(3.SI().Equals(null)); + Assert.IsFalse(3.SI().IsEqual(4.SI())); + Assert.IsTrue(z.Equals(x)); + Assert.IsFalse(3.SI().Equals(3.SI<Newton>())); + + var newton1 = 3.SI<Newton>(); + var newton2 = 3.SI<Newton>(); + Assert.IsTrue(newton1.Equals(newton2)); + + Assert.IsTrue(3.SI().IsEqual(3.SI())); + Assert.IsTrue(3.SI().IsEqual(3)); + + Assert.IsFalse(3.SI().IsEqual(2.9.SI())); + Assert.IsFalse(3.SI().IsEqual(2.9)); + } + + [TestMethod] + public void SI_Hash() + { + 3.SI().GetHashCode(); + 3.0.SI().GetHashCode(); + 4.SI<NewtonMeter>().GetHashCode(); + } + + [TestMethod] + public void SI_Output() + { + Assert.AreEqual("3.0000", 3.SI().ToOutputFormat()); + Assert.AreEqual("3.0000 [-]", 3.SI().ToOutputFormat(showUnit: true)); + Assert.AreEqual("3.5000", 3.5.SI().ToOutputFormat()); + Assert.AreEqual("3.5000", 3.5.SI<Newton>().ToOutputFormat()); + Assert.AreEqual("3.50 [N]", 3.5.SI<Newton>().ToOutputFormat(2, showUnit: true)); + Assert.AreEqual("18.00 [m/s]", 5.SI<MeterPerSecond>().ToOutputFormat(2, 3.6, true)); + Assert.AreEqual("18.0000", 5.SI<MeterPerSecond>().ToOutputFormat(outputFactor: 3.6)); + + Assert.AreEqual("10.0000 [m^2]", 10.SI<SquareMeter>().ToOutputFormat(showUnit: true)); + + Assert.AreEqual("10.0000 [m^3]", 10.SI<CubicMeter>().ToOutputFormat(showUnit: true)); + + Assert.AreEqual("0.5000 [m/s^2]", 0.5.SI<MeterPerSquareSecond>().ToOutputFormat(showUnit: true)); + } + } +} \ No newline at end of file diff --git a/VectoSI/VectoTest/VectoTest.csproj b/VectoSI/VectoTest/VectoTest.csproj new file mode 100644 index 0000000000000000000000000000000000000000..919bf2b4941d9642c64630b7706d017c6b7dbbb7 --- /dev/null +++ b/VectoSI/VectoTest/VectoTest.csproj @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="..\packages\MSTest.TestAdapter.1.1.11\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.1.1.11\build\net45\MSTest.TestAdapter.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{9873B975-9C17-4CC9-AAD1-FFB2BDBBA0C2}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>VectoTest</RootNamespace> + <AssemblyName>VectoTest</AssemblyName> + <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion> + <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> + <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath> + <IsCodedUITest>False</IsCodedUITest> + <TestProjectType>UnitTest</TestProjectType> + <NuGetPackageImportStamp> + </NuGetPackageImportStamp> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <ItemGroup> + <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <Private>False</Private> + </Reference> + <Reference Include="System" /> + <Reference Include="System.Core" /> + </ItemGroup> + <ItemGroup> + <Compile Include="AssertHelper.cs" /> + <Compile Include="SITest.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + </ItemGroup> + <ItemGroup> + <None Include="packages.config" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\VectoCommon\VectoCommon.csproj"> + <Project>{228258F5-D390-44CF-B326-9B0057833722}</Project> + <Name>VectoCommon</Name> + </ProjectReference> + </ItemGroup> + <Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" /> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> + <PropertyGroup> + <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> + </PropertyGroup> + <Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.1.11\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.1.11\build\net45\MSTest.TestAdapter.props'))" /> + <Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.1.11\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.1.11\build\net45\MSTest.TestAdapter.targets'))" /> + </Target> + <Import Project="..\packages\MSTest.TestAdapter.1.1.11\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.1.1.11\build\net45\MSTest.TestAdapter.targets')" /> +</Project> \ No newline at end of file diff --git a/VectoSI/VectoTest/packages.config b/VectoSI/VectoTest/packages.config new file mode 100644 index 0000000000000000000000000000000000000000..1ab721871c4b04ddd4dc193062a2d3d10289e539 --- /dev/null +++ b/VectoSI/VectoTest/packages.config @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="MSTest.TestAdapter" version="1.1.11" targetFramework="net452" /> + <package id="MSTest.TestFramework" version="1.1.11" targetFramework="net452" /> +</packages> \ No newline at end of file