├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── dotnet.yml ├── .gitignore ├── Directory.Packages.props ├── LICENSE ├── README.md ├── nuget-delete.sh ├── nuget-pack.sh ├── nuget-push.sh ├── rm.Extensions.sln ├── src └── rm.Extensions │ ├── ArrayExtension.cs │ ├── Base16.cs │ ├── Base16Extension.cs │ ├── Base32DouglasCrockford.cs │ ├── Base32Extension.cs │ ├── Base64Extension.cs │ ├── BitSet.cs │ ├── CharExtension.cs │ ├── CircularQueue.cs │ ├── CircularStack.cs │ ├── DateTimeExtension.cs │ ├── DecimalExtension.cs │ ├── Deque.cs │ ├── DequeNode.cs │ ├── DictionaryExtension.cs │ ├── EmptyException.cs │ ├── EncodingExtension.cs │ ├── EnumExtension.cs │ ├── EnumInternal.cs │ ├── EnumerableExtension.cs │ ├── ExceptionHelper.cs │ ├── GenericComparer.cs │ ├── GenericEqualityComparer.cs │ ├── GraphExtension.cs │ ├── GuidExtension.cs │ ├── HashQueue.cs │ ├── Heap.cs │ ├── Helper.cs │ ├── IntExtension.cs │ ├── ListExtension.cs │ ├── LruCache.cs │ ├── NameValueCollectionExtension.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── RandomExtension.cs │ ├── StopwatchExtension.cs │ ├── StringBuilderExtension.cs │ ├── StringExtension.cs │ ├── ThrowExtension.cs │ ├── TimeSpanExtension.cs │ ├── UnsupportedEnumValueException.TEnum.cs │ ├── UriExtension.cs │ ├── Wrapped.cs │ ├── WrappedExtension.cs │ └── rm.Extensions.csproj └── tests └── rm.ExtensionsTest ├── ArrayExtensionTest.cs ├── Base16ExtensionTest.cs ├── Base32ExtensionTest.cs ├── Base64ExtensionTest.cs ├── BitSetTest.cs ├── CircularQueueTest.cs ├── CircularStackTest.cs ├── DateTimeExtensionTest.cs ├── DecimalExtensionTest.cs ├── DequeTest.cs ├── DictionaryExtensionTest.cs ├── EnumExtensionTest.cs ├── EnumerableExtensionTest.cs ├── GraphExtensionTest.cs ├── GuidExtensionTest.cs ├── HashQueueTest.cs ├── HeapTest.cs ├── HelperTest.cs ├── IntExtensionTest.cs ├── ListExtensionTest.cs ├── LruCacheTest.cs ├── NameValueCollectionExtensionTest.cs ├── Properties └── AssemblyInfo.cs ├── RandomExtensionTest.cs ├── Sample ├── ComparableClass.cs ├── ComparableClass2.cs ├── ComparableClass2Comparer.cs └── SampleClass.cs ├── StopwatchExtensionTest.cs ├── StringBuilderExtensionTest.cs ├── StringExtensionTest.cs ├── ThrowExtensionTest.cs ├── TimeSpanExtensionTest.cs ├── UriExtensionTest.cs ├── WrappedExtensionTest.cs ├── images └── d s .jpg └── rm.ExtensionsTest.csproj /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = crlf 5 | [*.*] 6 | end_of_line = crlf 7 | 8 | [*.cs] 9 | indent_size = 4 10 | indent_style = tab 11 | end_of_line = crlf 12 | insert_final_newline = true 13 | 14 | [*.csproj] 15 | indent_size = 2 16 | indent_style = space 17 | end_of_line = crlf 18 | insert_final_newline = true 19 | 20 | [*.json] 21 | indent_style = space 22 | indent_size = 2 23 | end_of_line = crlf 24 | insert_final_newline = true 25 | 26 | [*.sln] 27 | indent_size = 4 28 | indent_style = tab 29 | end_of_line = crlf 30 | #insert_final_newline = true 31 | 32 | [*.sh] 33 | indent_size = 4 34 | indent_style = tab 35 | end_of_line = crlf 36 | insert_final_newline = true 37 | trim_trailing_whitespace = true 38 | 39 | [*.cs] 40 | csharp_style_namespace_declarations = file_scoped:warning 41 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | name: .NET 2 | 3 | on: [ push, pull_request ] 4 | 5 | jobs: 6 | build-workflow: 7 | uses: rmandvikar/workflows/.github/workflows/dotnet.yml@main 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.pch 68 | *.pdb 69 | *.pgc 70 | *.pgd 71 | *.rsp 72 | *.sbr 73 | *.tlb 74 | *.tli 75 | *.tlh 76 | *.tmp 77 | *.tmp_proj 78 | *.log 79 | *.vspscc 80 | *.vssscc 81 | .builds 82 | *.pidb 83 | *.svclog 84 | *.scc 85 | 86 | # Chutzpah Test files 87 | _Chutzpah* 88 | 89 | # Visual C++ cache files 90 | ipch/ 91 | *.aps 92 | *.ncb 93 | *.opendb 94 | *.opensdf 95 | *.sdf 96 | *.cachefile 97 | *.VC.db 98 | *.VC.VC.opendb 99 | 100 | # Visual Studio profiler 101 | *.psess 102 | *.vsp 103 | *.vspx 104 | *.sap 105 | 106 | # Visual Studio Trace Files 107 | *.e2e 108 | 109 | # TFS 2012 Local Workspace 110 | $tf/ 111 | 112 | # Guidance Automation Toolkit 113 | *.gpState 114 | 115 | # ReSharper is a .NET coding add-in 116 | _ReSharper*/ 117 | *.[Rr]e[Ss]harper 118 | *.DotSettings.user 119 | 120 | # JustCode is a .NET coding add-in 121 | .JustCode 122 | 123 | # TeamCity is a build add-in 124 | _TeamCity* 125 | 126 | # DotCover is a Code Coverage Tool 127 | *.dotCover 128 | 129 | # AxoCover is a Code Coverage Tool 130 | .axoCover/* 131 | !.axoCover/settings.json 132 | 133 | # Visual Studio code coverage results 134 | *.coverage 135 | *.coveragexml 136 | 137 | # NCrunch 138 | _NCrunch_* 139 | .*crunch*.local.xml 140 | nCrunchTemp_* 141 | 142 | # MightyMoose 143 | *.mm.* 144 | AutoTest.Net/ 145 | 146 | # Web workbench (sass) 147 | .sass-cache/ 148 | 149 | # Installshield output folder 150 | [Ee]xpress/ 151 | 152 | # DocProject is a documentation generator add-in 153 | DocProject/buildhelp/ 154 | DocProject/Help/*.HxT 155 | DocProject/Help/*.HxC 156 | DocProject/Help/*.hhc 157 | DocProject/Help/*.hhk 158 | DocProject/Help/*.hhp 159 | DocProject/Help/Html2 160 | DocProject/Help/html 161 | 162 | # Click-Once directory 163 | publish/ 164 | 165 | # Publish Web Output 166 | *.[Pp]ublish.xml 167 | *.azurePubxml 168 | # Note: Comment the next line if you want to checkin your web deploy settings, 169 | # but database connection strings (with potential passwords) will be unencrypted 170 | *.pubxml 171 | *.publishproj 172 | 173 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 174 | # checkin your Azure Web App publish settings, but sensitive information contained 175 | # in these scripts will be unencrypted 176 | PublishScripts/ 177 | 178 | # NuGet Packages 179 | *.nupkg 180 | # The packages folder can be ignored because of Package Restore 181 | **/[Pp]ackages/* 182 | # except build/, which is used as an MSBuild target. 183 | !**/[Pp]ackages/build/ 184 | # Uncomment if necessary however generally it will be regenerated when needed 185 | #!**/[Pp]ackages/repositories.config 186 | # NuGet v3's project.json files produces more ignorable files 187 | *.nuget.props 188 | *.nuget.targets 189 | *.snupkg 190 | 191 | # Microsoft Azure Build Output 192 | csx/ 193 | *.build.csdef 194 | 195 | # Microsoft Azure Emulator 196 | ecf/ 197 | rcf/ 198 | 199 | # Windows Store app package directories and files 200 | AppPackages/ 201 | BundleArtifacts/ 202 | Package.StoreAssociation.xml 203 | _pkginfo.txt 204 | *.appx 205 | 206 | # Visual Studio cache files 207 | # files ending in .cache can be ignored 208 | *.[Cc]ache 209 | # but keep track of directories ending in .cache 210 | !*.[Cc]ache/ 211 | 212 | # Others 213 | ClientBin/ 214 | ~$* 215 | *~ 216 | *.dbmdl 217 | *.dbproj.schemaview 218 | *.jfm 219 | *.pfx 220 | *.publishsettings 221 | orleans.codegen.cs 222 | 223 | # Since there are multiple workflows, uncomment next line to ignore bower_components 224 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 225 | #bower_components/ 226 | 227 | # RIA/Silverlight projects 228 | Generated_Code/ 229 | 230 | # Backup & report files from converting an old project file 231 | # to a newer Visual Studio version. Backup files are not needed, 232 | # because we have git ;-) 233 | _UpgradeReport_Files/ 234 | Backup*/ 235 | UpgradeLog*.XML 236 | UpgradeLog*.htm 237 | 238 | # SQL Server files 239 | *.mdf 240 | *.ldf 241 | *.ndf 242 | 243 | # Business Intelligence projects 244 | *.rdl.data 245 | *.bim.layout 246 | *.bim_*.settings 247 | 248 | # Microsoft Fakes 249 | FakesAssemblies/ 250 | 251 | # GhostDoc plugin setting file 252 | *.GhostDoc.xml 253 | 254 | # Node.js Tools for Visual Studio 255 | .ntvs_analysis.dat 256 | node_modules/ 257 | 258 | # TypeScript v1 declaration files 259 | typings/ 260 | 261 | # Visual Studio 6 build log 262 | *.plg 263 | 264 | # Visual Studio 6 workspace options file 265 | *.opt 266 | 267 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 268 | *.vbw 269 | 270 | # Visual Studio LightSwitch build output 271 | **/*.HTMLClient/GeneratedArtifacts 272 | **/*.DesktopClient/GeneratedArtifacts 273 | **/*.DesktopClient/ModelManifest.xml 274 | **/*.Server/GeneratedArtifacts 275 | **/*.Server/ModelManifest.xml 276 | _Pvt_Extensions 277 | 278 | # Paket dependency manager 279 | .paket/paket.exe 280 | paket-files/ 281 | 282 | # FAKE - F# Make 283 | .fake/ 284 | 285 | # JetBrains Rider 286 | .idea/ 287 | *.sln.iml 288 | 289 | # CodeRush 290 | .cr/ 291 | 292 | # Python Tools for Visual Studio (PTVS) 293 | __pycache__/ 294 | *.pyc 295 | 296 | # Cake - Uncomment if you are using it 297 | # tools/** 298 | # !tools/packages.config 299 | 300 | # Tabs Studio 301 | *.tss 302 | 303 | # Telerik's JustMock configuration file 304 | *.jmconfig 305 | 306 | # BizTalk build output 307 | *.btp.cs 308 | *.btm.cs 309 | *.odx.cs 310 | *.xsd.cs 311 | 312 | # OpenCover UI analysis results 313 | OpenCover/ 314 | 315 | # Azure Stream Analytics local run output 316 | ASALocalRun/ 317 | -------------------------------------------------------------------------------- /Directory.Packages.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | false 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | https://github.com/rmandvikar/csharp-extensions 4 | Copyright (c) 2019 hippy 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /nuget-delete.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | #usage: nuget-delete 4 | 5 | if [[ "$1" == "-h" ]]; then print-file-comments "$0"; exit; fi 6 | 7 | version="$1" 8 | if [[ -z "$version" ]]; then 9 | echo 1>&2 "fatal: version required" 10 | exec print-file-comments "$0" 11 | exit 1 12 | fi 13 | 14 | dotnet nuget delete rmandvikar.Extensions "$version" \ 15 | -k $(< ~/dump/.nuget.apikey) \ 16 | -s https://api.nuget.org/v3/index.json \ 17 | --non-interactive 18 | -------------------------------------------------------------------------------- /nuget-pack.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | #usage: nuget-pack 4 | 5 | if [[ "$1" == "-h" ]]; then print-file-comments "$0"; exit; fi 6 | 7 | version="$1" 8 | if [[ -z "$version" ]]; then 9 | echo 1>&2 "fatal: version required" 10 | exec print-file-comments "$0" 11 | exit 1 12 | fi 13 | 14 | tag="v$version" 15 | commit_hash=$(git rev-parse @) 16 | metadata="$commit_hash" 17 | 18 | dotnet pack src/rm.Extensions/rm.Extensions.csproj \ 19 | -c Release \ 20 | --include-symbols //p:SymbolPackageFormat=snupkg \ 21 | -o .nupkg/ \ 22 | //p:Version="$version+$metadata" \ 23 | //p:PackageVersion="$version+$metadata" \ 24 | //p:PackageReleaseNotes="tag: $tag" \ 25 | && git tag "$tag" -m "Create nuget tag $tag" 26 | -------------------------------------------------------------------------------- /nuget-push.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | #usage: nuget-push 4 | 5 | if [[ "$1" == "-h" ]]; then print-file-comments "$0"; exit; fi 6 | 7 | version="$1" 8 | if [[ -z "$version" ]]; then 9 | echo 1>&2 "fatal: version required" 10 | exec print-file-comments "$0" 11 | exit 1 12 | fi 13 | 14 | tag="v$version" 15 | 16 | dotnet nuget push .nupkg/rmandvikar.Extensions."$version".nupkg \ 17 | -k $(< ~/dump/.nuget.apikey) \ 18 | -s https://api.nuget.org/v3/index.json \ 19 | && git push $(git remote) "$tag" 20 | -------------------------------------------------------------------------------- /rm.Extensions.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.33502.453 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "rm.Extensions", "src\rm.Extensions\rm.Extensions.csproj", "{CDD966D6-4370-4745-9B0E-F8D7E252620E}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "rm.ExtensionsTest", "tests\rm.ExtensionsTest\rm.ExtensionsTest.csproj", "{3C36D356-3426-4951-88D9-073233CBFDB9}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{51F18744-8BB4-4636-B916-51DA6EBFBD48}" 11 | ProjectSection(SolutionItems) = preProject 12 | Directory.Packages.props = Directory.Packages.props 13 | LICENSE = LICENSE 14 | README.md = README.md 15 | EndProjectSection 16 | EndProject 17 | Global 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | Debug|Any CPU = Debug|Any CPU 20 | Release|Any CPU = Release|Any CPU 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {CDD966D6-4370-4745-9B0E-F8D7E252620E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {CDD966D6-4370-4745-9B0E-F8D7E252620E}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {CDD966D6-4370-4745-9B0E-F8D7E252620E}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {CDD966D6-4370-4745-9B0E-F8D7E252620E}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {3C36D356-3426-4951-88D9-073233CBFDB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {3C36D356-3426-4951-88D9-073233CBFDB9}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {3C36D356-3426-4951-88D9-073233CBFDB9}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {3C36D356-3426-4951-88D9-073233CBFDB9}.Release|Any CPU.Build.0 = Release|Any CPU 31 | EndGlobalSection 32 | GlobalSection(SolutionProperties) = preSolution 33 | HideSolutionNode = FALSE 34 | EndGlobalSection 35 | EndGlobal 36 | -------------------------------------------------------------------------------- /src/rm.Extensions/ArrayExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace rm.Extensions; 6 | 7 | /// 8 | /// Array{T} extensions. 9 | /// 10 | public static class Array 11 | { 12 | /// 13 | /// Returns empty T array. 14 | /// 15 | public static T[] Empty => Array.Empty(); 16 | } 17 | -------------------------------------------------------------------------------- /src/rm.Extensions/Base16.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace rm.Extensions; 4 | 5 | public class Base16 6 | { 7 | // note: use int to avoid implicit conversions 8 | public const int MinValue = 0; 9 | public const int MaxValue = 15; 10 | 11 | private const int bitsInBase16Char = 4; 12 | private const int decodeMapLength = sbyte.MaxValue + 1; 13 | private const int defaultValue = byte.MaxValue; 14 | 15 | /// 16 | /// 4-bit int value -> base16 char map. 17 | /// 18 | /// 19 | /// Array index lookup is faster than hashmap, so. 20 | /// It provides a good balance of speed, readability, and maintainability. 21 | /// 22 | /// https://stackoverflow.com/questions/311165/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-and-vice-versa 23 | private static readonly char[] encodeUppercaseMap = GetEncodeUppercaseMap(); 24 | private static char[] GetEncodeUppercaseMap() 25 | { 26 | return new char[] 27 | { 28 | '0', // 0 29 | '1', // 1 30 | '2', // 2 31 | '3', // 3 32 | '4', // 4 33 | '5', // 5 34 | '6', // 6 35 | '7', // 7 36 | '8', // 8 37 | '9', // 9 38 | 'A', // 10 39 | 'B', // 11 40 | 'C', // 12 41 | 'D', // 13 42 | 'E', // 14 43 | 'F', // 15 44 | }; 45 | } 46 | 47 | /// 48 | private static readonly char[] encodeLowercaseMap = GetEncodeLowercaseMap(); 49 | private static char[] GetEncodeLowercaseMap() 50 | { 51 | return new char[] 52 | { 53 | '0', // 0 54 | '1', // 1 55 | '2', // 2 56 | '3', // 3 57 | '4', // 4 58 | '5', // 5 59 | '6', // 6 60 | '7', // 7 61 | '8', // 8 62 | '9', // 9 63 | 'a', // 10 64 | 'b', // 11 65 | 'c', // 12 66 | 'd', // 13 67 | 'e', // 14 68 | 'f', // 15 69 | }; 70 | } 71 | 72 | /// 73 | /// Base16 char -> 4-bit int value map. 74 | /// 75 | /// 76 | /// Array index lookup is faster than hashmap, so. 77 | /// It provides a good balance of speed, readability, and maintainability. 78 | /// 79 | /// https://stackoverflow.com/questions/311165/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-and-vice-versa 80 | private static readonly int[] decodeMap = GetDecodeMap(); 81 | private static int[] GetDecodeMap() 82 | { 83 | var decodeMap = new int[decodeMapLength]; 84 | for (int i = 0; i < decodeMap.Length; i++) 85 | { 86 | decodeMap[i] = defaultValue; 87 | } 88 | 89 | decodeMap['0'] = 0; 90 | decodeMap['1'] = 1; 91 | decodeMap['2'] = 2; 92 | decodeMap['3'] = 3; 93 | decodeMap['4'] = 4; 94 | decodeMap['5'] = 5; 95 | decodeMap['6'] = 6; 96 | decodeMap['7'] = 7; 97 | decodeMap['8'] = 8; 98 | decodeMap['9'] = 9; 99 | decodeMap['A'] = 10; 100 | decodeMap['a'] = 10; // lowercase 101 | decodeMap['B'] = 11; 102 | decodeMap['b'] = 11; // lowercase 103 | decodeMap['C'] = 12; 104 | decodeMap['c'] = 12; // lowercase 105 | decodeMap['D'] = 13; 106 | decodeMap['d'] = 13; // lowercase 107 | decodeMap['E'] = 14; 108 | decodeMap['e'] = 14; // lowercase 109 | decodeMap['F'] = 15; 110 | decodeMap['f'] = 15; // lowercase 111 | 112 | return decodeMap; 113 | } 114 | 115 | /// 116 | /// Returns Base16 encoded (hex) string from . 117 | /// 118 | public string Encode(byte[] bytes) 119 | { 120 | return EncodeInner(bytes, encodeUppercaseMap); 121 | } 122 | 123 | /// 124 | public string EncodeUppercase(byte[] bytes) 125 | { 126 | return EncodeInner(bytes, encodeUppercaseMap); 127 | } 128 | 129 | /// 130 | public string EncodeLowercase(byte[] bytes) 131 | { 132 | return EncodeInner(bytes, encodeLowercaseMap); 133 | } 134 | 135 | private string EncodeInner(byte[] bytes, char[] encodeMap) 136 | { 137 | _ = bytes 138 | ?? throw new ArgumentNullException(nameof(bytes)); 139 | 140 | var bytesLength = bytes.Length; 141 | var base16 = new char[bytesLength << 1]; 142 | for (int i = 0; i < bytesLength; i++) 143 | { 144 | var @byte = bytes[i]; 145 | var itarget = i << 1; 146 | base16[itarget] = encodeMap[(@byte >> bitsInBase16Char) & 0b_1111]; 147 | base16[itarget + 1] = encodeMap[(@byte) & 0b_1111]; 148 | } 149 | return new string(base16); 150 | } 151 | 152 | /// 153 | /// Returns decoded bytes from encoded (hex) string. 154 | /// 155 | public byte[] Decode(string base16) 156 | { 157 | _ = base16 158 | ?? throw new ArgumentNullException(nameof(base16)); 159 | 160 | var base16Length = base16.Length; 161 | if ((base16Length & 0b_1) != 0) 162 | { 163 | throw new ArgumentException($"Invalid length: {base16}", nameof(base16)); 164 | } 165 | 166 | var bytes = new byte[base16Length >> 1]; 167 | for (int i = 0; i < base16Length; i += 2) 168 | { 169 | var msNibble = decodeMap[base16[i]]; 170 | var lsNibble = decodeMap[base16[i + 1]]; 171 | if (msNibble == defaultValue) 172 | { 173 | var base16Char = base16[i]; 174 | throw new ArgumentOutOfRangeException(nameof(base16), base16Char, base16); 175 | } 176 | if (lsNibble == defaultValue) 177 | { 178 | var base16Char = base16[i + 1]; 179 | throw new ArgumentOutOfRangeException(nameof(base16), base16Char, base16); 180 | } 181 | bytes[i >> 1] = (byte)((msNibble << bitsInBase16Char) | lsNibble); 182 | } 183 | 184 | return bytes; 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /src/rm.Extensions/Base16Extension.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace rm.Extensions; 4 | 5 | /// 6 | /// Base16 extensions. 7 | /// 8 | public static class Base16Extension 9 | { 10 | private static readonly Base16 base16Converter = new Base16(); 11 | 12 | /// . 13 | [DebuggerStepThrough] 14 | public static string Base16Encode(this byte[] bytes) 15 | { 16 | return base16Converter.Encode(bytes); 17 | } 18 | 19 | /// . 20 | [DebuggerStepThrough] 21 | public static string Base16EncodeUppercase(this byte[] bytes) 22 | { 23 | return base16Converter.EncodeUppercase(bytes); 24 | } 25 | 26 | /// . 27 | [DebuggerStepThrough] 28 | public static string Base16EncodeLowercase(this byte[] bytes) 29 | { 30 | return base16Converter.EncodeLowercase(bytes); 31 | } 32 | 33 | /// . 34 | [DebuggerStepThrough] 35 | public static string ToHexString(this byte[] bytes) 36 | { 37 | return base16Converter.Encode(bytes); 38 | } 39 | 40 | /// . 41 | [DebuggerStepThrough] 42 | public static byte[] Base16Decode(this string base16) 43 | { 44 | return base16Converter.Decode(base16); 45 | } 46 | 47 | /// . 48 | [DebuggerStepThrough] 49 | public static byte[] FromHexString(this string base16) 50 | { 51 | return base16Converter.Decode(base16); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/rm.Extensions/Base32DouglasCrockford.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Linq; 4 | 5 | namespace rm.Extensions; 6 | 7 | /// 8 | /// Base32 encoding, decoding. 9 | /// 10 | /// Douglas Crockford variant. 11 | /// 12 | /// See https://www.crockford.com/base32.html 13 | /// 14 | public class Base32DouglasCrockford 15 | { 16 | // note: use int to avoid implicit conversions 17 | public const int MinValue = 0; 18 | public const int MaxValue = 31; 19 | public const int MaxValueIncludingCheckSymbol = 36; 20 | 21 | private const int bitsInByte = 8; 22 | private const int bitsInInteger = 32; 23 | private const int bitsInBase32Char = 5; 24 | 25 | /// 26 | /// Returns Base32 encoded string from . 27 | /// 28 | public string Encode(byte[] bytes) 29 | { 30 | _ = bytes 31 | ?? throw new ArgumentNullException(nameof(bytes)); 32 | 33 | var base32 = new StringBuilder((bytes.Length * bitsInByte / bitsInBase32Char) + 1); 34 | 35 | // bitwise impl of 5B/40b for speed 36 | var slices = bytes.Length / bitsInBase32Char; 37 | int sliceStartIndex; 38 | for (int iSlice = 0; iSlice < slices; iSlice++) 39 | { 40 | sliceStartIndex = iSlice * bitsInBase32Char; 41 | 42 | // read bits as string representation, L->R within byte 43 | var base32CharOfSlice1 = (bytes[sliceStartIndex + 0] >> 3) & 0b1_1111; 44 | var base32CharOfSlice2 = ((bytes[sliceStartIndex + 0] << 2) & 0b1_1100) | ((bytes[sliceStartIndex + 1] >> 6) & 0b0_0011); 45 | var base32CharOfSlice3 = (bytes[sliceStartIndex + 1] >> 1) & 0b1_1111; 46 | var base32CharOfSlice4 = ((bytes[sliceStartIndex + 1] << 4) & 0b1_0000) | ((bytes[sliceStartIndex + 2] >> 4) & 0b0_1111); 47 | var base32CharOfSlice5 = ((bytes[sliceStartIndex + 2] << 1) & 0b1_1110) | ((bytes[sliceStartIndex + 3] >> 7) & 0b0_0001); 48 | var base32CharOfSlice6 = (bytes[sliceStartIndex + 3] >> 2) & 0b1_1111; 49 | var base32CharOfSlice7 = ((bytes[sliceStartIndex + 3] << 3) & 0b1_1000) | ((bytes[sliceStartIndex + 4] >> 5) & 0b0_0111); 50 | var base32CharOfSlice8 = bytes[sliceStartIndex + 4] & 0b1_1111; 51 | 52 | base32.Append(base32CharOfSlice1.GetBase32Char()); 53 | base32.Append(base32CharOfSlice2.GetBase32Char()); 54 | base32.Append(base32CharOfSlice3.GetBase32Char()); 55 | base32.Append(base32CharOfSlice4.GetBase32Char()); 56 | base32.Append(base32CharOfSlice5.GetBase32Char()); 57 | base32.Append(base32CharOfSlice6.GetBase32Char()); 58 | base32.Append(base32CharOfSlice7.GetBase32Char()); 59 | base32.Append(base32CharOfSlice8.GetBase32Char()); 60 | } 61 | 62 | // handle surplus bytes (max 4) if any 63 | var surplusBytes = bytes.Length % bitsInBase32Char; 64 | sliceStartIndex = bytes.Length - surplusBytes; 65 | if (surplusBytes > 0) 66 | { 67 | int buffer = 0; 68 | var iSurplusByte = sliceStartIndex; 69 | for (int ibuffer = (bitsInBase32Char - 1) - 1; ibuffer >= 0 && iSurplusByte < bytes.Length; ibuffer--) 70 | { 71 | buffer = buffer | bytes[iSurplusByte] << (ibuffer * bitsInByte); 72 | iSurplusByte++; 73 | } 74 | var surplusBase32Chars = (surplusBytes * bitsInByte) / bitsInBase32Char + 1; 75 | for (int iSurplusBase32Char = 0; iSurplusBase32Char < surplusBase32Chars; iSurplusBase32Char++) 76 | { 77 | var surplusBase32Char = ((buffer >> (bitsInInteger - bitsInBase32Char)) & 0b_1_1111).GetBase32Char(); 78 | base32.Append(surplusBase32Char); 79 | buffer = buffer << bitsInBase32Char; 80 | } 81 | } 82 | 83 | return base32.ToString(); 84 | } 85 | 86 | /// 87 | /// Returns decoded bytes from encoded string. 88 | /// 89 | public byte[] Decode(string base32) 90 | { 91 | _ = base32 92 | ?? throw new ArgumentNullException(nameof(base32)); 93 | 94 | // HACK: Improve '-' symbol check perf 95 | var base32Tokens = base32.Split('-'); 96 | var base32TokenBytes = base32Tokens.Select(DecodeInner); 97 | var bytes = Helper.Concat(base32TokenBytes.ToArray()); 98 | return bytes; 99 | } 100 | 101 | private byte[] DecodeInner(string base32) 102 | { 103 | var size = base32.Length * bitsInBase32Char / bitsInByte; 104 | var bytes = new byte[size]; 105 | 106 | // bitwise impl of 5B/40b for speed 107 | var slices = base32.Length / bitsInByte; 108 | int sliceStartIndex; 109 | var iByte = 0; 110 | for (int iSlice = 0; iSlice < slices; iSlice++) 111 | { 112 | sliceStartIndex = iSlice * bitsInByte; 113 | 114 | // read bits as string representation, L->R within char 115 | var base32Value1 = base32[sliceStartIndex + 0].GetBase32Value(); 116 | var base32Value2 = base32[sliceStartIndex + 1].GetBase32Value(); 117 | var base32Value3 = base32[sliceStartIndex + 2].GetBase32Value(); 118 | var base32Value4 = base32[sliceStartIndex + 3].GetBase32Value(); 119 | var base32Value5 = base32[sliceStartIndex + 4].GetBase32Value(); 120 | var base32Value6 = base32[sliceStartIndex + 5].GetBase32Value(); 121 | var base32Value7 = base32[sliceStartIndex + 6].GetBase32Value(); 122 | var base32Value8 = base32[sliceStartIndex + 7].GetBase32Value(); 123 | 124 | bytes[iByte++] = (byte)(base32Value1 << 3 | base32Value2 >> 2); 125 | bytes[iByte++] = (byte)(base32Value2 << 6 | base32Value3 << 1 | base32Value4 >> 4); 126 | bytes[iByte++] = (byte)(base32Value4 << 4 | base32Value5 >> 1); 127 | bytes[iByte++] = (byte)(base32Value5 << 7 | base32Value6 << 2 | base32Value7 >> 3); 128 | bytes[iByte++] = (byte)(base32Value7 << 5 | base32Value8); 129 | } 130 | 131 | // handle surplus chars (max 7) if any 132 | var surplusChars = base32.Length % bitsInByte; 133 | sliceStartIndex = base32.Length - surplusChars; 134 | if (surplusChars > 0) 135 | { 136 | int buffer = 0; 137 | var shiftSurplusChar = bitsInInteger; 138 | for (int iSurplusChar = sliceStartIndex; iSurplusChar < base32.Length; iSurplusChar++) 139 | { 140 | var base32Value = base32[iSurplusChar].GetBase32Value(); 141 | shiftSurplusChar -= bitsInBase32Char; 142 | buffer = buffer | (shiftSurplusChar > 0 ? base32Value << shiftSurplusChar : base32Value >> -shiftSurplusChar); 143 | } 144 | var shiftBuffer = (bitsInBase32Char - 1) - 1; 145 | while (iByte < bytes.Length) 146 | { 147 | var @byte = buffer >> (shiftBuffer * bitsInByte); 148 | bytes[iByte] = (byte)@byte; 149 | iByte++; 150 | shiftBuffer--; 151 | } 152 | } 153 | 154 | return bytes; 155 | } 156 | } 157 | 158 | /// 159 | /// Helper class. 160 | /// 161 | internal static class Base32DouglasCrockfordExtension 162 | { 163 | // note: use int to avoid implicit conversions 164 | private const int minValue = Base32DouglasCrockford.MinValue; 165 | private const int maxValue = Base32DouglasCrockford.MaxValue; 166 | private const int maxValueIncludingCheckSymbol = Base32DouglasCrockford.MaxValueIncludingCheckSymbol; 167 | private const int defaultValue = byte.MaxValue; 168 | 169 | /// 170 | /// 5-bit int value -> base32 char map. 171 | /// 172 | /// Array index lookup is faster than hashmap, so. 173 | private static readonly char[] encodeMap = GetEncodeMap(); 174 | 175 | private static char[] GetEncodeMap() 176 | { 177 | return new char[] 178 | { 179 | '0', // 0 180 | '1', // 1 181 | '2', // 2 182 | '3', // 3 183 | '4', // 4 184 | '5', // 5 185 | '6', // 6 186 | '7', // 7 187 | '8', // 8 188 | '9', // 9 189 | 'A', // 10 190 | 'B', // 11 191 | 'C', // 12 192 | 'D', // 13 193 | 'E', // 14 194 | 'F', // 15 195 | 'G', // 16 196 | 'H', // 17 197 | 'J', // 18 // skip I 198 | 'K', // 19 199 | 'M', // 20 // skip L 200 | 'N', // 21 201 | 'P', // 22 // skip O 202 | 'Q', // 23 203 | 'R', // 24 204 | 'S', // 25 205 | 'T', // 26 206 | 'V', // 27 // skip U 207 | 'W', // 28 208 | 'X', // 29 209 | 'Y', // 30 210 | 'Z', // 31 211 | '*', // 32 // check symbol 212 | '~', // 33 // check symbol 213 | '$', // 34 // check symbol 214 | '=', // 35 // check symbol 215 | 'U', // 36 // check symbol 216 | }; 217 | } 218 | 219 | /// 220 | /// Base32 char -> 5-bit int value map. 221 | /// 222 | /// Array index lookup is faster than hashmap, so. 223 | private static readonly int[] decodeMap = GetDecodeMap(); 224 | 225 | private const int decodeMapLength = sbyte.MaxValue + 1; 226 | 227 | private static int[] GetDecodeMap() 228 | { 229 | var decodeMap = new int[decodeMapLength]; 230 | for (int i = 0; i < decodeMap.Length; i++) 231 | { 232 | decodeMap[i] = defaultValue; 233 | } 234 | 235 | decodeMap['0'] = 0; // 0 O o 236 | decodeMap['O'] = 0; // 0 O o 237 | decodeMap['o'] = 0; // 0 O o 238 | decodeMap['1'] = 1; // 1 I i L l 239 | decodeMap['I'] = 1; // 1 I i L l 240 | decodeMap['i'] = 1; // 1 I i L l 241 | decodeMap['L'] = 1; // 1 I i L l 242 | decodeMap['l'] = 1; // 1 I i L l 243 | decodeMap['2'] = 2; 244 | decodeMap['3'] = 3; 245 | decodeMap['4'] = 4; 246 | decodeMap['5'] = 5; 247 | decodeMap['6'] = 6; 248 | decodeMap['7'] = 7; 249 | decodeMap['8'] = 8; 250 | decodeMap['9'] = 9; 251 | decodeMap['A'] = 10; 252 | decodeMap['a'] = 10; 253 | decodeMap['B'] = 11; 254 | decodeMap['b'] = 11; 255 | decodeMap['C'] = 12; 256 | decodeMap['c'] = 12; 257 | decodeMap['D'] = 13; 258 | decodeMap['d'] = 13; 259 | decodeMap['E'] = 14; 260 | decodeMap['e'] = 14; 261 | decodeMap['F'] = 15; 262 | decodeMap['f'] = 15; 263 | decodeMap['G'] = 16; 264 | decodeMap['g'] = 16; 265 | decodeMap['H'] = 17; 266 | decodeMap['h'] = 17; 267 | decodeMap['J'] = 18; // skip I 268 | decodeMap['j'] = 18; 269 | decodeMap['K'] = 19; 270 | decodeMap['k'] = 19; 271 | decodeMap['M'] = 20; // skip L 272 | decodeMap['m'] = 20; 273 | decodeMap['N'] = 21; 274 | decodeMap['n'] = 21; 275 | decodeMap['P'] = 22; // skip O 276 | decodeMap['p'] = 22; 277 | decodeMap['Q'] = 23; 278 | decodeMap['q'] = 23; 279 | decodeMap['R'] = 24; 280 | decodeMap['r'] = 24; 281 | decodeMap['S'] = 25; 282 | decodeMap['s'] = 25; 283 | decodeMap['T'] = 26; 284 | decodeMap['t'] = 26; 285 | decodeMap['V'] = 27; // skip U 286 | decodeMap['v'] = 27; 287 | decodeMap['W'] = 28; 288 | decodeMap['w'] = 28; 289 | decodeMap['X'] = 29; 290 | decodeMap['x'] = 29; 291 | decodeMap['Y'] = 30; 292 | decodeMap['y'] = 30; 293 | decodeMap['Z'] = 31; 294 | decodeMap['z'] = 31; 295 | decodeMap['*'] = 32; // check symbol 296 | decodeMap['~'] = 33; // check symbol 297 | decodeMap['$'] = 34; // check symbol 298 | decodeMap['='] = 35; // check symbol 299 | decodeMap['U'] = 36; // check symbol 300 | decodeMap['u'] = 36; // check symbol 301 | 302 | return decodeMap; 303 | } 304 | 305 | /// 306 | /// Returns Base32 encoded char for 5-bit int . 307 | /// 308 | internal static char GetBase32Char(this int value) 309 | { 310 | if (!(minValue <= value && value <= maxValue)) 311 | { 312 | throw new ArgumentOutOfRangeException(nameof(value), value, null); 313 | } 314 | return encodeMap[value]; 315 | } 316 | 317 | /// 318 | /// Returns 5-bit int decoded value for . 319 | /// 320 | internal static int GetBase32Value(this char base32Char) 321 | { 322 | int value; 323 | if (base32Char >= decodeMapLength || 324 | (value = decodeMap[base32Char]) == defaultValue || 325 | !(minValue <= value && value <= maxValue)) 326 | { 327 | throw new ArgumentOutOfRangeException(nameof(base32Char), base32Char, null); 328 | } 329 | return value; 330 | } 331 | 332 | /// 333 | /// Returns 5-bit int decoded value for . 334 | /// 335 | internal static int GetBase32ValueIncludingCheckSymbol(this char base32Char) 336 | { 337 | int value; 338 | if (base32Char >= decodeMapLength || 339 | (value = decodeMap[base32Char]) == defaultValue || 340 | !(minValue <= value && value <= maxValueIncludingCheckSymbol)) 341 | { 342 | throw new ArgumentOutOfRangeException(nameof(base32Char), base32Char, null); 343 | } 344 | return value; 345 | } 346 | } 347 | -------------------------------------------------------------------------------- /src/rm.Extensions/Base32Extension.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace rm.Extensions; 4 | 5 | /// 6 | /// Base32 extensions. 7 | /// 8 | public static class Base32Extension 9 | { 10 | private static readonly Base32DouglasCrockford base32ConverterDouglasCrockford = new Base32DouglasCrockford(); 11 | 12 | /// . 13 | [DebuggerStepThrough] 14 | public static string Base32Encode(this byte[] bytes) 15 | { 16 | return base32ConverterDouglasCrockford.Encode(bytes); 17 | } 18 | 19 | /// . 20 | [DebuggerStepThrough] 21 | public static byte[] Base32Decode(this string base32) 22 | { 23 | return base32ConverterDouglasCrockford.Decode(base32); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/rm.Extensions/Base64Extension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace rm.Extensions; 5 | 6 | /// 7 | /// Base64 extensions. 8 | /// 9 | public static class Base64Extension 10 | { 11 | public static string Base64Encode(this byte[] bytes) 12 | { 13 | return Convert.ToBase64String(bytes); 14 | } 15 | 16 | public static byte[] Base64Decode(this string base64) 17 | { 18 | return Convert.FromBase64String(base64); 19 | } 20 | 21 | public static string Base64UrlEncode(this byte[] bytes) 22 | { 23 | var base64 = bytes.Base64Encode(); 24 | return new StringBuilder(base64) 25 | .Replace('+', '-') 26 | .Replace('/', '_') 27 | .Replace("=", "") 28 | .ToString(); 29 | } 30 | 31 | public static byte[] Base64UrlDecode(this string base64Url) 32 | { 33 | const int maxPad = 0b_0100; 34 | var pad = new string('=', (maxPad - (base64Url.Length & 0b_0011)) & 0b_0011); 35 | return new StringBuilder(base64Url) 36 | .Replace('-', '+') 37 | .Replace('_', '/') 38 | .Append(pad) 39 | .ToString() 40 | .Base64Decode(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/rm.Extensions/BitSet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using Ex = rm.Extensions.ExceptionHelper; 5 | 6 | namespace rm.Extensions; 7 | 8 | /// 9 | /// BitSet is a set that uses bit arithmatic to save space. 10 | /// 11 | /// 12 | /// BitSet is space efficient compared to bool[] if the set is dense and not sparse. 13 | /// 14 | /// Note: Cannot implement IEnumerable{int}.GetEnumerator() without loss of information. 15 | /// 16 | /// 17 | public class BitSet : IEnumerable, ICollection, IReadOnlyCollection 18 | { 19 | #region data members 20 | 21 | /// 22 | /// int array used as bit array for flags (32 bits for each int). 23 | /// 24 | /// 32, bytes: 4, binary: 11111 (+1), mask: 0x1f, power: 2^5 25 | internal readonly int[] flags; 26 | 27 | /// 28 | /// The max int allowed in BitSet. 29 | /// 30 | public uint Max { get; } 31 | 32 | /// 33 | /// The number of elements in BitSet. 34 | /// 35 | public int Count { get; private set; } 36 | 37 | #endregion 38 | 39 | #region ctors 40 | 41 | /// 42 | /// Creates a BitSet for 0 to max, both inclusive. 43 | /// 44 | /// Max (inclusive), including 0. 45 | public BitSet(uint max) 46 | { 47 | max.ThrowIfArgumentOutOfRange(nameof(max)); 48 | Max = max; 49 | int capacity = (int)((max >> 5) + 1); 50 | flags = new int[capacity]; 51 | } 52 | 53 | /// 54 | /// Creates a BitSet for 0 to max, both inclusive. 55 | /// 56 | /// Max (inclusive), including 0. 57 | public BitSet(int max) 58 | : this((uint)(max.ThrowIfArgumentOutOfRange(nameof(max)))) 59 | { } 60 | 61 | #endregion 62 | 63 | #region methods 64 | 65 | /// 66 | /// Returns index and offset for . 67 | /// 68 | private (uint, int) GetIndexOffset(uint n) 69 | { 70 | var index = (n >> 5); 71 | var offset = (int)(n & 0b1_1111); 72 | return (index, offset); 73 | } 74 | 75 | /// 76 | /// Returns true if BitSet contains . 77 | /// 78 | public bool Has(uint n) 79 | { 80 | n.ThrowIfArgumentOutOfRange(nameof(n), maxRange: Max); 81 | (uint index, int offset) = GetIndexOffset(n); 82 | return Has(index, offset); 83 | } 84 | 85 | /// 86 | /// Returns true if BitSet has the flags[]'s 87 | /// bit set. 88 | /// 89 | private bool Has(uint index, int offset) 90 | { 91 | return ((flags[index] >> offset) & 1) == 1; 92 | } 93 | 94 | /// 95 | /// Returns true if BitSet contains . 96 | /// 97 | public bool Has(int n) 98 | { 99 | Ex.ThrowIfArgumentOutOfRange(!(0 <= n && n <= Max), nameof(n)); 100 | return Has((uint)n); 101 | } 102 | 103 | /// 104 | /// Adds . 105 | /// 106 | public void Add(uint n) 107 | { 108 | n.ThrowIfArgumentOutOfRange(nameof(n), maxRange: Max); 109 | (uint index, int offset) = GetIndexOffset(n); 110 | if (Has(index, offset)) 111 | { 112 | return; 113 | } 114 | flags[index] |= (1 << offset); 115 | Count++; 116 | } 117 | 118 | /// 119 | /// Adds . 120 | /// 121 | public void Add(int n) 122 | { 123 | Ex.ThrowIfArgumentOutOfRange(!(0 <= n && n <= Max), nameof(n)); 124 | Add((uint)n); 125 | } 126 | 127 | /// 128 | /// Removes . 129 | /// 130 | /// Returns true if removed else false. 131 | public bool Remove(uint n) 132 | { 133 | n.ThrowIfArgumentOutOfRange(nameof(n), maxRange: Max); 134 | (uint index, int offset) = GetIndexOffset(n); 135 | if (!Has(index, offset)) 136 | { 137 | return false; 138 | } 139 | flags[index] &= ~(1 << offset); 140 | Count--; 141 | return true; 142 | } 143 | 144 | /// 145 | /// Removes . 146 | /// 147 | /// Returns true if removed else false. 148 | public bool Remove(int n) 149 | { 150 | Ex.ThrowIfArgumentOutOfRange(!(0 <= n && n <= Max), nameof(n)); 151 | return Remove((uint)n); 152 | } 153 | 154 | /// 155 | /// Toggles . 156 | /// 157 | public void Toggle(uint n) 158 | { 159 | n.ThrowIfArgumentOutOfRange(nameof(n), maxRange: Max); 160 | (uint index, int offset) = GetIndexOffset(n); 161 | flags[index] ^= (1 << offset); 162 | Count += Has(index, offset) ? +1 : -1; 163 | } 164 | 165 | /// 166 | /// Toggles . 167 | /// 168 | public void Toggle(int n) 169 | { 170 | Ex.ThrowIfArgumentOutOfRange(!(0 <= n && n <= Max), nameof(n)); 171 | Toggle((uint)n); 172 | } 173 | 174 | /// 175 | /// Clears the BitSet. 176 | /// 177 | /// This is expensive. 178 | public void Clear() 179 | { 180 | for (int i = 0; i < flags.Length; i++) 181 | { 182 | flags[i] = 0; 183 | } 184 | Count = 0; 185 | } 186 | 187 | #region IEnumerable methods 188 | 189 | public IEnumerator GetEnumerator() 190 | { 191 | var ycount = 0; 192 | for (uint i = 0; i <= Max; i++) 193 | { 194 | if (ycount == Count) 195 | { 196 | yield break; 197 | } 198 | if (Has(i)) 199 | { 200 | yield return i; 201 | ycount++; 202 | } 203 | } 204 | } 205 | 206 | IEnumerator IEnumerable.GetEnumerator() 207 | { 208 | return GetEnumerator(); 209 | } 210 | 211 | // Note: Cannot implement IEnumerable.GetEnumerator() without loss of information 212 | 213 | #endregion 214 | 215 | #region ICollection methods 216 | 217 | public bool Contains(uint n) 218 | { 219 | return Has(n); 220 | } 221 | 222 | public bool Contains(int n) 223 | { 224 | return Has((uint)n); 225 | } 226 | 227 | public bool IsReadOnly => false; 228 | 229 | [Obsolete("Not implemented", true)] 230 | public void CopyTo(uint[] array, int arrayIndex) 231 | { 232 | throw new NotImplementedException(); 233 | } 234 | 235 | #endregion 236 | 237 | #endregion 238 | } 239 | -------------------------------------------------------------------------------- /src/rm.Extensions/CharExtension.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | 3 | namespace rm.Extensions; 4 | 5 | /// 6 | /// Char extensions. 7 | /// 8 | public static class CharExtension 9 | { 10 | /// 11 | public static bool IsDigit(this char c) 12 | { 13 | return char.IsDigit(c); 14 | } 15 | 16 | /// 17 | public static bool IsLetter(this char c) 18 | { 19 | return char.IsLetter(c); 20 | } 21 | 22 | /// 23 | public static bool IsLetterOrDigit(this char c) 24 | { 25 | return char.IsLetterOrDigit(c); 26 | } 27 | 28 | /// 29 | public static bool IsNumber(this char c) 30 | { 31 | return char.IsNumber(c); 32 | } 33 | 34 | /// 35 | public static bool IsWhiteSpace(this char c) 36 | { 37 | return char.IsWhiteSpace(c); 38 | } 39 | 40 | /// 41 | public static bool IsPunctuation(this char c) 42 | { 43 | return char.IsPunctuation(c); 44 | } 45 | 46 | /// 47 | public static bool IsSymbol(this char c) 48 | { 49 | return char.IsSymbol(c); 50 | } 51 | 52 | /// 53 | public static bool IsControl(this char c) 54 | { 55 | return char.IsControl(c); 56 | } 57 | 58 | /// 59 | public static bool IsSeparator(this char c) 60 | { 61 | return char.IsSeparator(c); 62 | } 63 | 64 | /// 65 | public static bool IsSurrogate(this char c) 66 | { 67 | return char.IsSurrogate(c); 68 | } 69 | 70 | /// 71 | public static bool IsHighSurrogate(this char c) 72 | { 73 | return char.IsHighSurrogate(c); 74 | } 75 | 76 | /// 77 | public static bool IsLowSurrogate(this char c) 78 | { 79 | return char.IsLowSurrogate(c); 80 | } 81 | 82 | /// 83 | /// Returns true for all characters below or equal U+00ff, which is ASCII + Latin-1 Supplement. 84 | /// 85 | public static bool IsLatin1(this char c) 86 | { 87 | return (uint)c <= '\x00ff'; 88 | } 89 | 90 | /// 91 | /// Returns true for all characters below or equal U+007f, which is ASCII. 92 | /// 93 | public static bool IsAscii(this char c) 94 | { 95 | return (uint)c <= '\x007f'; 96 | } 97 | 98 | /// 99 | public static bool IsUpper(this char c) 100 | { 101 | return char.IsUpper(c); 102 | } 103 | 104 | /// 105 | public static bool IsLower(this char c) 106 | { 107 | return char.IsLower(c); 108 | } 109 | 110 | /// 111 | public static char ToUpper(this char c) 112 | { 113 | return char.ToUpper(c); 114 | } 115 | 116 | /// 117 | public static char ToUpper(this char c, CultureInfo cultureInfo) 118 | { 119 | return char.ToUpper(c, cultureInfo); 120 | } 121 | 122 | /// 123 | public static char ToUpperInvariant(this char c) 124 | { 125 | return char.ToUpperInvariant(c); 126 | } 127 | 128 | /// 129 | public static char ToLower(this char c) 130 | { 131 | return char.ToLower(c); 132 | } 133 | 134 | /// 135 | public static char ToLower(this char c, CultureInfo cultureInfo) 136 | { 137 | return char.ToLower(c, cultureInfo); 138 | } 139 | 140 | /// 141 | public static char ToLowerInvariant(this char c) 142 | { 143 | return char.ToLowerInvariant(c); 144 | } 145 | 146 | /// 147 | public static UnicodeCategory GetUnicodeCategory(this char c) 148 | { 149 | return char.GetUnicodeCategory(c); 150 | } 151 | 152 | /// 153 | public static double GetNumericValue(char c) 154 | { 155 | return char.GetNumericValue(c); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/rm.Extensions/CircularQueue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | namespace rm.Extensions; 6 | 7 | /// 8 | /// Defines circular queue methods. 9 | /// 10 | public interface ICircularQueue 11 | { 12 | /// 13 | /// Enqueues into queue. 14 | /// 15 | void Enqueue(T x); 16 | 17 | /// 18 | /// Dequeues head from queue. 19 | /// 20 | T Dequeue(); 21 | 22 | /// 23 | /// Peeks head of queue. 24 | /// 25 | T Peek(); 26 | 27 | /// 28 | /// Peeks tail of queue. 29 | /// 30 | T PeekTail(); 31 | 32 | /// 33 | /// Returns true if queue is empty. 34 | /// 35 | bool IsEmpty(); 36 | 37 | /// 38 | /// Returns count of queue. 39 | /// 40 | int Count(); 41 | 42 | /// 43 | /// Returns capacity of queue. 44 | /// 45 | int Capacity(); 46 | 47 | /// 48 | /// Clears queue. 49 | /// 50 | void Clear(); 51 | } 52 | 53 | /// 54 | /// Circular queue. 55 | /// 56 | /// Uses array as backing store. 57 | public class CircularQueue : ICircularQueue, IEnumerable 58 | { 59 | #region members 60 | 61 | private readonly T[] store; 62 | private readonly int capacity; 63 | private int count; 64 | private int head; 65 | private int tail; 66 | 67 | #endregion 68 | 69 | #region ctors 70 | 71 | public CircularQueue(int capacity = 8) 72 | { 73 | capacity.ThrowIfArgumentOutOfRange(minRange: 1, exMessage: nameof(capacity)); 74 | store = new T[capacity]; 75 | this.capacity = capacity; 76 | head = tail = count = 0; 77 | } 78 | 79 | #endregion 80 | 81 | #region ICircularQueue methods 82 | 83 | /// 84 | /// Enqueues into queue. 85 | /// 86 | public void Enqueue(T x) 87 | { 88 | store[tail] = x; 89 | tail = WrapIndex(tail + 1); 90 | if (count == capacity) 91 | { 92 | head = tail; 93 | } 94 | if (count < capacity) 95 | { 96 | count++; 97 | } 98 | } 99 | 100 | /// 101 | /// Dequeues head from queue. 102 | /// 103 | public T Dequeue() 104 | { 105 | if (IsEmpty()) 106 | { 107 | throw new InvalidOperationException("Queue is empty."); 108 | } 109 | var item = store[head]; 110 | head = WrapIndex(head + 1); 111 | count--; 112 | return item; 113 | } 114 | 115 | /// 116 | /// Peeks head of queue. 117 | /// 118 | public T Peek() 119 | { 120 | if (IsEmpty()) 121 | { 122 | throw new InvalidOperationException("Queue is empty."); 123 | } 124 | return store[head]; 125 | } 126 | 127 | /// 128 | /// Peeks tail of queue. 129 | /// 130 | public T PeekTail() 131 | { 132 | if (IsEmpty()) 133 | { 134 | throw new InvalidOperationException("Queue is empty."); 135 | } 136 | return store[WrapIndex(tail - 1)]; 137 | } 138 | 139 | /// 140 | /// Returns true if queue is empty. 141 | /// 142 | public bool IsEmpty() 143 | { 144 | return count == 0; 145 | } 146 | 147 | /// 148 | /// Returns count of queue. 149 | /// 150 | public int Count() 151 | { 152 | return count; 153 | } 154 | 155 | /// 156 | /// Returns capacity of queue. 157 | /// 158 | public int Capacity() 159 | { 160 | return capacity; 161 | } 162 | 163 | /// 164 | /// Clears queue. 165 | /// 166 | public void Clear() 167 | { 168 | head = tail = count = 0; 169 | } 170 | 171 | #endregion 172 | 173 | private int WrapIndex(int index) 174 | { 175 | var wIndex = index % capacity; 176 | if (wIndex < 0) 177 | { 178 | wIndex += capacity; 179 | } 180 | return wIndex; 181 | } 182 | 183 | #region IEnumerable methods 184 | 185 | public IEnumerator GetEnumerator() 186 | { 187 | var index = head; 188 | for (int c = 0; c < count; c++) 189 | { 190 | yield return store[index]; 191 | index = WrapIndex(index + 1); 192 | } 193 | } 194 | 195 | IEnumerator IEnumerable.GetEnumerator() 196 | { 197 | return GetEnumerator(); 198 | } 199 | 200 | #endregion 201 | } 202 | -------------------------------------------------------------------------------- /src/rm.Extensions/CircularStack.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | namespace rm.Extensions; 6 | 7 | /// 8 | /// Defines circular stack methods. 9 | /// 10 | public interface ICircularStack 11 | { 12 | /// 13 | /// Pushes into stack. 14 | /// 15 | void Push(T x); 16 | 17 | /// 18 | /// Pops top from stack. 19 | /// 20 | T Pop(); 21 | 22 | /// 23 | /// Peeks top of stack. 24 | /// 25 | T Peek(); 26 | 27 | /// 28 | /// Peeks bottom of stack. 29 | /// 30 | T PeekBottom(); 31 | 32 | /// 33 | /// Returns true if stack is empty. 34 | /// 35 | bool IsEmpty(); 36 | 37 | /// 38 | /// Returns count of stack. 39 | /// 40 | int Count(); 41 | 42 | /// 43 | /// Returns capacity of stack. 44 | /// 45 | int Capacity(); 46 | 47 | /// 48 | /// Clears stack. 49 | /// 50 | void Clear(); 51 | } 52 | 53 | /// 54 | /// Circular stack. 55 | /// 56 | /// Uses array as backing store. 57 | public class CircularStack : ICircularStack, IEnumerable 58 | { 59 | #region members 60 | 61 | private readonly T[] store; 62 | private readonly int capacity; 63 | private int count; 64 | private int top; 65 | 66 | #endregion 67 | 68 | #region ctors 69 | 70 | public CircularStack(int capacity = 8) 71 | { 72 | capacity.ThrowIfArgumentOutOfRange(minRange: 1, exMessage: nameof(capacity)); 73 | store = new T[capacity]; 74 | this.capacity = capacity; 75 | top = count = 0; 76 | } 77 | 78 | #endregion 79 | 80 | #region ICircularStack methods 81 | 82 | /// 83 | /// Pushes into stack. 84 | /// 85 | public void Push(T x) 86 | { 87 | store[top] = x; 88 | top = WrapIndex(top + 1); 89 | if (count < capacity) 90 | { 91 | count++; 92 | } 93 | } 94 | 95 | /// 96 | /// Pops top from stack. 97 | /// 98 | public T Pop() 99 | { 100 | if (IsEmpty()) 101 | { 102 | throw new InvalidOperationException("Stack is empty."); 103 | } 104 | top = WrapIndex(top - 1); 105 | var item = store[top]; 106 | count--; 107 | return item; 108 | } 109 | 110 | /// 111 | /// Peeks top of stack. 112 | /// 113 | public T Peek() 114 | { 115 | if (IsEmpty()) 116 | { 117 | throw new InvalidOperationException("Stack is empty."); 118 | } 119 | return store[WrapIndex(top - 1)]; 120 | } 121 | 122 | /// 123 | /// Peeks bottom of stack. 124 | /// 125 | public T PeekBottom() 126 | { 127 | if (IsEmpty()) 128 | { 129 | throw new InvalidOperationException("Stack is empty."); 130 | } 131 | return store[WrapIndex(top - count)]; 132 | } 133 | 134 | /// 135 | /// Returns true if stack is empty. 136 | /// 137 | public bool IsEmpty() 138 | { 139 | return count == 0; 140 | } 141 | 142 | /// 143 | /// Returns count of stack. 144 | /// 145 | public int Count() 146 | { 147 | return count; 148 | } 149 | 150 | /// 151 | /// Returns capacity of stack. 152 | /// 153 | public int Capacity() 154 | { 155 | return capacity; 156 | } 157 | 158 | /// 159 | /// Clears stack. 160 | /// 161 | public void Clear() 162 | { 163 | top = count = 0; 164 | } 165 | 166 | #endregion 167 | 168 | private int WrapIndex(int index) 169 | { 170 | var wIndex = index % capacity; 171 | if (wIndex < 0) 172 | { 173 | wIndex += capacity; 174 | } 175 | return wIndex; 176 | } 177 | 178 | #region IEnumerable methods 179 | 180 | public IEnumerator GetEnumerator() 181 | { 182 | var index = top; 183 | for (int c = 0; c < count; c++) 184 | { 185 | index = WrapIndex(index - 1); 186 | yield return store[index]; 187 | } 188 | } 189 | 190 | IEnumerator IEnumerable.GetEnumerator() 191 | { 192 | return GetEnumerator(); 193 | } 194 | 195 | #endregion 196 | } 197 | -------------------------------------------------------------------------------- /src/rm.Extensions/DateTimeExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.SqlTypes; 3 | 4 | namespace rm.Extensions; 5 | 6 | /// 7 | /// DateTime extensions. 8 | /// 9 | public static class DateTimeExtension 10 | { 11 | /// 12 | /// UTC date format string. 13 | /// 14 | public static readonly string UtcDateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'"; 15 | 16 | /// 17 | /// Min value for Sql datetime to save in sql db. 18 | /// 19 | public static readonly DateTime SqlDateTimeMinUtc = SqlDateTime.MinValue.Value.AsUtcKind(); 20 | 21 | /// 22 | /// Gets the UTC datetime format for the date. 23 | /// 24 | /// 25 | /// 26 | public static string ToUtcFormatString(this DateTime date) 27 | { 28 | return date.ToUniversalTime().ToString(UtcDateFormat); 29 | } 30 | 31 | /// 32 | /// Gets the min value for Sql datetime. 33 | /// 34 | public static DateTime ToSqlDateTimeMinUtc(this DateTime date) 35 | { 36 | return SqlDateTimeMinUtc; 37 | } 38 | 39 | /// 40 | /// Specifies datetime's kind as UTC. 41 | /// 42 | /// 43 | /// 44 | /// 45 | /// Date read from db or parsed from string has its Kind as Unspecified. 46 | /// Specifying its kind as UTC is needed if date is expected to be UTC. 47 | /// ToUniversalTime() assumes that the kind is local while converting it and is undesirable. 48 | /// 49 | public static DateTime AsUtcKind(this DateTime datetime) 50 | { 51 | return DateTime.SpecifyKind(datetime, DateTimeKind.Utc); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/rm.Extensions/DecimalExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace rm.Extensions; 4 | 5 | /// 6 | /// Decimal extensions. 7 | /// 8 | public static class DecimalExtension 9 | { 10 | /// 11 | /// Truncates decimal to . 12 | /// 13 | public static decimal TruncateTo(this decimal n, uint digits) 14 | { 15 | // note: Math.Round(n, digits, ...) doesn't work 16 | var wholePart = decimal.Truncate(n); 17 | 18 | var decimalPart = n - wholePart; 19 | var factor = checked(Math.Pow(10, digits)); 20 | var decimalPartTruncated = Math.Truncate(decimalPart * (decimal)factor) / (decimal)factor; 21 | 22 | return wholePart + decimalPartTruncated; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/rm.Extensions/Deque.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using rm.Extensions.Deque; 5 | using Ex = rm.Extensions.ExceptionHelper; 6 | 7 | namespace rm.Extensions; 8 | 9 | /// 10 | /// Defines deque methods. 11 | /// 12 | interface IDeque : IEnumerable 13 | { 14 | /// 15 | /// Enqueues into deque. 16 | /// 17 | Node Enqueue(T x); 18 | 19 | /// 20 | /// Dequeues head from deque. 21 | /// 22 | T Dequeue(); 23 | 24 | /// 25 | /// Peeks head of deque. 26 | /// 27 | T Peek(); 28 | 29 | /// 30 | /// Peeks tail of deque. 31 | /// 32 | T PeekTail(); 33 | 34 | /// 35 | /// Deletes given from deque. 36 | /// 37 | void Delete(Node node); 38 | 39 | /// 40 | /// Returns true if deque is empty. 41 | /// 42 | bool IsEmpty(); 43 | 44 | /// 45 | /// Returns count of deque. 46 | /// 47 | int Count(); 48 | 49 | /// 50 | /// Returns long count of deque. 51 | /// 52 | long LongCount(); 53 | 54 | /// 55 | /// Clears deque. 56 | /// 57 | void Clear(); 58 | 59 | /// 60 | /// Inserts before given . 61 | /// 62 | Node InsertBefore(Node node, T x); 63 | 64 | /// 65 | /// Inserts after given . 66 | /// 67 | Node InsertAfter(Node node, T x); 68 | 69 | /// 70 | /// Inserts as head. 71 | /// 72 | void InsertHead(Node node); 73 | 74 | /// 75 | /// Inserts as tail. 76 | /// 77 | void InsertTail(Node node); 78 | 79 | /// 80 | /// Make as head. 81 | /// 82 | void MakeHead(Node node); 83 | 84 | /// 85 | /// Make as tail. 86 | /// 87 | void MakeTail(Node node); 88 | 89 | // HACK: Either encapsulate or refactor with Remove(T x). 90 | /// 91 | /// Iterates over all nodes. 92 | /// 93 | IEnumerable> Nodes(); 94 | } 95 | 96 | /// 97 | /// Deque. 98 | /// 99 | /// 100 | /// Uses linked list as backing store. 101 | /// 102 | /// All methods are O(1) time unless noted. 103 | /// 104 | public class Deque : IDeque 105 | { 106 | #region members 107 | 108 | private Node head; 109 | private Node tail; 110 | private long count; 111 | 112 | #endregion 113 | 114 | #region ctors 115 | 116 | public Deque() 117 | { } 118 | 119 | #endregion 120 | 121 | #region IDeque methods 122 | 123 | /// 124 | /// Enqueues into deque. 125 | /// 126 | /// The node for . 127 | public Node Enqueue(T x) 128 | { 129 | var node = new Node(x, this); 130 | if (IsEmpty()) 131 | { 132 | head = tail = node; 133 | } 134 | else 135 | { 136 | tail.next = node; 137 | node.prev = tail; 138 | tail = node; 139 | } 140 | count++; 141 | return node; 142 | } 143 | 144 | /// 145 | /// Dequeues head from deque. 146 | /// 147 | public T Dequeue() 148 | { 149 | Ex.ThrowIfEmpty(IsEmpty(), "Deque is empty."); 150 | count--; 151 | var node = head; 152 | node.owner = null; 153 | if (head == tail) 154 | { 155 | head = tail = null; 156 | } 157 | else 158 | { 159 | head = head.next; 160 | node.next = head.prev = null; 161 | } 162 | return node.Value; 163 | } 164 | 165 | /// 166 | /// Peeks head of deque. 167 | /// 168 | public T Peek() 169 | { 170 | Ex.ThrowIfEmpty(IsEmpty(), "Deque is empty."); 171 | return head.Value; 172 | } 173 | 174 | /// 175 | /// Peeks tail of deque. 176 | /// 177 | public T PeekTail() 178 | { 179 | Ex.ThrowIfEmpty(IsEmpty(), "Deque is empty."); 180 | return tail.Value; 181 | } 182 | 183 | /// 184 | /// Deletes given from deque. 185 | /// 186 | public void Delete(Node node) 187 | { 188 | node.ThrowIfArgumentNull(nameof(node)); 189 | if (node.owner != this) 190 | { 191 | throw new InvalidOperationException("Node does not belong to the deque."); 192 | } 193 | Ex.ThrowIfEmpty(IsEmpty(), "Deque is empty."); 194 | count--; 195 | node.owner = null; 196 | if (node == head && node == tail) 197 | { 198 | head = tail = null; 199 | } 200 | else if (node == head) 201 | { 202 | head = head.next; 203 | node.next = head.prev = null; 204 | } 205 | else if (node == tail) 206 | { 207 | tail = tail.prev; 208 | tail.next = node.prev = null; 209 | } 210 | else 211 | { 212 | node.prev.next = node.next; 213 | node.next.prev = node.prev; 214 | node.next = node.prev = null; 215 | } 216 | } 217 | 218 | /// 219 | /// Returns true if deque is empty. 220 | /// 221 | public bool IsEmpty() 222 | { 223 | return count == 0; 224 | } 225 | 226 | /// 227 | /// Returns count of deque. 228 | /// 229 | public int Count() 230 | { 231 | return checked((int)count); 232 | } 233 | 234 | /// 235 | /// Returns long count of deque. 236 | /// 237 | public long LongCount() 238 | { 239 | return count; 240 | } 241 | 242 | /// 243 | /// Clears deque. 244 | /// 245 | public void Clear() 246 | { 247 | head = tail = null; 248 | count = 0; 249 | } 250 | 251 | /// 252 | /// Inserts before given . 253 | /// 254 | /// The node for . 255 | public Node InsertBefore(Node node, T x) 256 | { 257 | node.ThrowIfArgumentNull(nameof(node)); 258 | if (node.owner != this) 259 | { 260 | throw new InvalidOperationException("Node does not belong to the deque."); 261 | } 262 | count++; 263 | var prev = node.prev; 264 | var xnode = new Node(x, this); 265 | var next = node; 266 | xnode.next = next; 267 | next.prev = xnode; 268 | if (next == head) 269 | { 270 | head = xnode; 271 | } 272 | else 273 | { 274 | prev.next = xnode; 275 | xnode.prev = prev; 276 | } 277 | return xnode; 278 | } 279 | 280 | /// 281 | /// Inserts after given . 282 | /// 283 | /// The node for . 284 | public Node InsertAfter(Node node, T x) 285 | { 286 | node.ThrowIfArgumentNull(nameof(node)); 287 | if (node.owner != this) 288 | { 289 | throw new InvalidOperationException("Node does not belong to the deque."); 290 | } 291 | count++; 292 | var prev = node; 293 | var xnode = new Node(x, this); 294 | var next = node.next; 295 | xnode.prev = prev; 296 | prev.next = xnode; 297 | if (prev == tail) 298 | { 299 | tail = xnode; 300 | } 301 | else 302 | { 303 | xnode.next = next; 304 | next.prev = xnode; 305 | } 306 | return xnode; 307 | } 308 | 309 | /// 310 | /// Inserts as head. 311 | /// 312 | public void InsertHead(Node node) 313 | { 314 | node.ThrowIfArgumentNull(nameof(node)); 315 | if (node.owner != null) 316 | { 317 | throw new InvalidOperationException("Node is already in deque."); 318 | } 319 | node.owner = this; 320 | if (IsEmpty()) 321 | { 322 | head = tail = node; 323 | } 324 | else 325 | { 326 | node.next = head; 327 | head.prev = node; 328 | head = node; 329 | } 330 | count++; 331 | } 332 | 333 | /// 334 | /// Inserts as tail. 335 | /// 336 | public void InsertTail(Node node) 337 | { 338 | node.ThrowIfArgumentNull(nameof(node)); 339 | if (node.owner != null) 340 | { 341 | throw new InvalidOperationException("Node is already in deque."); 342 | } 343 | node.owner = this; 344 | if (IsEmpty()) 345 | { 346 | head = tail = node; 347 | } 348 | else 349 | { 350 | tail.next = node; 351 | node.prev = tail; 352 | tail = node; 353 | } 354 | count++; 355 | } 356 | 357 | /// 358 | /// Make as head. 359 | /// 360 | public void MakeHead(Node node) 361 | { 362 | node.ThrowIfArgumentNull(nameof(node)); 363 | if (node.owner != this) 364 | { 365 | throw new InvalidOperationException("Node does not belong to the deque."); 366 | } 367 | if (node == head) 368 | { 369 | return; 370 | } 371 | if (node == tail) 372 | { 373 | tail = node.prev; 374 | tail.next = node.prev = null; 375 | } 376 | else 377 | { 378 | node.prev.next = node.next; 379 | node.next.prev = node.prev; 380 | node.next = node.prev = null; 381 | } 382 | head.prev = node; 383 | node.next = head; 384 | head = node; 385 | } 386 | 387 | /// 388 | /// Make as tail. 389 | /// 390 | public void MakeTail(Node node) 391 | { 392 | node.ThrowIfArgumentNull(nameof(node)); 393 | if (node.owner != this) 394 | { 395 | throw new InvalidOperationException("Node does not belong to the deque."); 396 | } 397 | if (node == tail) 398 | { 399 | return; 400 | } 401 | if (node == head) 402 | { 403 | head = node.next; 404 | node.next = head.prev = null; 405 | } 406 | else 407 | { 408 | node.prev.next = node.next; 409 | node.next.prev = node.prev; 410 | node.next = node.prev = null; 411 | } 412 | tail.next = node; 413 | node.prev = tail; 414 | tail = node; 415 | } 416 | 417 | /// 418 | /// Iterates over all nodes. 419 | /// 420 | public IEnumerable> Nodes() 421 | { 422 | var node = head; 423 | while (node != null) 424 | { 425 | yield return node; 426 | node = node.next; 427 | } 428 | } 429 | 430 | #endregion 431 | 432 | #region IEnumerable methods 433 | 434 | public IEnumerator GetEnumerator() 435 | { 436 | var node = head; 437 | while (node != null) 438 | { 439 | yield return node.Value; 440 | node = node.next; 441 | } 442 | } 443 | 444 | IEnumerator IEnumerable.GetEnumerator() 445 | { 446 | return GetEnumerator(); 447 | } 448 | 449 | #endregion 450 | } 451 | -------------------------------------------------------------------------------- /src/rm.Extensions/DequeNode.cs: -------------------------------------------------------------------------------- 1 | namespace rm.Extensions.Deque; 2 | 3 | /// 4 | /// Deque node. 5 | /// 6 | public class Node 7 | { 8 | public readonly T Value; 9 | internal Node prev; 10 | internal Node next; 11 | internal Deque owner; 12 | 13 | internal Node(T value, Deque owner) 14 | { 15 | this.Value = value; 16 | this.owner = owner; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/rm.Extensions/DictionaryExtension.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.ObjectModel; 3 | 4 | namespace rm.Extensions; 5 | 6 | /// 7 | /// Dictionary extensions. 8 | /// 9 | public static class DictionaryExtension 10 | { 11 | /// 12 | /// Returns value for key if exists or default{}. 13 | /// 14 | public static TValue GetValueOrDefault(this IDictionary dictionary, 15 | TKey key) 16 | { 17 | return dictionary.GetValueOrDefault(key, default(TValue)); 18 | } 19 | 20 | /// 21 | /// Returns value for key if exists or . 22 | /// 23 | public static TValue GetValueOrDefault(this IDictionary dictionary, 24 | TKey key, TValue defaultValue) 25 | { 26 | TValue value; 27 | if (dictionary.TryGetValue(key, out value)) 28 | { 29 | return value; 30 | } 31 | return defaultValue; 32 | } 33 | 34 | /// 35 | /// Returns the as read-only. 36 | /// 37 | public static IDictionary AsReadOnly(this IDictionary dictionary) 38 | { 39 | return new ReadOnlyDictionary(dictionary); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/rm.Extensions/EmptyException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | namespace rm.Extensions; 5 | 6 | /// 7 | /// The exception that is thrown when the object is empty. 8 | /// 9 | [Serializable] 10 | public class EmptyException : Exception 11 | { 12 | public EmptyException() { } 13 | public EmptyException(string message) 14 | : base(message) { } 15 | public EmptyException(string message, Exception inner) 16 | : base(message, inner) { } 17 | protected EmptyException(SerializationInfo info, StreamingContext context) 18 | : base(info, context) { } 19 | } 20 | -------------------------------------------------------------------------------- /src/rm.Extensions/EncodingExtension.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace rm.Extensions; 4 | 5 | /// 6 | /// Encoding extensions. 7 | /// 8 | public static class EncodingExtension 9 | { 10 | public static byte[] ToUtf8Bytes(this string s) 11 | { 12 | return Encoding.UTF8.GetBytes(s); 13 | } 14 | 15 | public static string ToUtf8String(this byte[] utf8Bytes) 16 | { 17 | return Encoding.UTF8.GetString(utf8Bytes); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/rm.Extensions/EnumExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace rm.Extensions; 6 | 7 | /// 8 | /// Enum extensions. 9 | /// 10 | public static class EnumExtension 11 | { 12 | /// 13 | /// TryParses the string to enum of type . 14 | /// 15 | public static bool TryParse(this string name, out T result, bool ignoreCase = false) 16 | where T : struct 17 | { 18 | return Enum.TryParse(name, ignoreCase, out result); 19 | } 20 | 21 | /// 22 | /// Parses the string to enum of type . 23 | /// 24 | public static T Parse(this string name, bool ignoreCase = false) 25 | where T : struct 26 | { 27 | return (T)Enum.Parse(typeof(T), name, ignoreCase); 28 | } 29 | 30 | /// 31 | /// Gets values for type enum. 32 | /// 33 | public static T[] GetEnumValues() 34 | where T : struct 35 | { 36 | return EnumInternal.ValueToNameMap.Select(x => x.Key).ToArray(); 37 | } 38 | 39 | /// 40 | /// Gets names (strings) for type enum. 41 | /// 42 | public static string[] GetEnumNames() 43 | where T : struct 44 | { 45 | return EnumInternal.NameToValueMap.Select(x => x.Key).ToArray(); 46 | } 47 | 48 | /// 49 | /// Gets enum name (string) -> description (string) map for 50 | /// type enum. 51 | /// 52 | public static IDictionary GetEnumNameToDescriptionMap() 53 | where T : struct 54 | { 55 | return EnumInternal.ValueToDescriptionMap 56 | .ToDictionary(x => x.Key.GetEnumName(), x => x.Value).AsReadOnly(); 57 | } 58 | 59 | /// 60 | /// Gets the name (string) for the enum value or throws exception if not exists. 61 | /// 62 | public static string GetEnumName(this T enumValue) 63 | where T : struct 64 | { 65 | string enumName; 66 | if (EnumInternal.ValueToNameMap.TryGetValue(enumValue, out enumName)) 67 | { 68 | return enumName; 69 | } 70 | throw new UnsupportedEnumValueException(enumValue); 71 | } 72 | 73 | /// 74 | /// Gets enum name (string) for description or throws exception if not exists. 75 | /// 76 | public static string GetEnumNameFromDescription(this string description) 77 | where T : struct 78 | { 79 | T enumValue; 80 | if (EnumInternal.DescriptionToValueMap.TryGetValue(description, out enumValue)) 81 | { 82 | return enumValue.GetEnumName(); 83 | } 84 | throw new UnsupportedEnumValueException( 85 | $"{nameof(description)} {description} of enum {typeof(T).Name} is not supported."); 86 | } 87 | 88 | /// 89 | /// Gets the value for the enum name (string) or throws exception if not exists. 90 | /// 91 | public static T GetEnumValue(this string name) 92 | where T : struct 93 | { 94 | T enumValue; 95 | if (EnumInternal.NameToValueMap.TryGetValue(name, out enumValue)) 96 | { 97 | return enumValue; 98 | } 99 | throw new UnsupportedEnumValueException( 100 | $"{nameof(name)} {name} of enum {typeof(T).Name} is not supported."); 101 | } 102 | 103 | /// 104 | /// Gets the value for the enum description (string) or throws exception if not exists. 105 | /// 106 | public static T GetEnumValueFromDescription(this string description) 107 | where T : struct 108 | { 109 | T enumValue; 110 | if (EnumInternal.DescriptionToValueMap.TryGetValue(description, out enumValue)) 111 | { 112 | return enumValue; 113 | } 114 | throw new UnsupportedEnumValueException( 115 | $"{nameof(description)} {description} of enum {typeof(T).Name} is not supported."); 116 | } 117 | 118 | /// 119 | /// Gets the description (DescriptionAttribute) for the enum value 120 | /// or throws exception if not exists. 121 | /// 122 | public static string GetDescription(this T enumValue) 123 | where T : struct 124 | { 125 | string description; 126 | if (EnumInternal.ValueToDescriptionMap.TryGetValue(enumValue, out description)) 127 | { 128 | return description; 129 | } 130 | throw new UnsupportedEnumValueException(enumValue); 131 | } 132 | 133 | /// 134 | /// Generic method for . 135 | /// 136 | public static bool IsDefined(this int value) 137 | where T : struct 138 | { 139 | // avoid Enum.IsDefined(typeof(T), value) due to boxing 140 | return EnumInternal.ValueIntToValueMap.ContainsKey(value); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/rm.Extensions/EnumInternal.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace rm.Extensions; 8 | 9 | /// 10 | /// Enum helper class. 11 | /// 12 | /// https://code.google.com/p/unconstrained-melody/source/browse/trunk/UnconstrainedMelody/EnumInternals.cs 13 | public static class EnumInternal 14 | where T : struct 15 | { 16 | /// 17 | /// enum name -> enum value 18 | /// 19 | internal static readonly IDictionary NameToValueMap = 20 | new Dictionary(); 21 | 22 | /// 23 | /// enum value -> enum name 24 | /// 25 | internal static readonly IDictionary ValueToNameMap = 26 | new Dictionary(); 27 | 28 | /// 29 | /// enum value -> description 30 | /// 31 | internal static readonly IDictionary ValueToDescriptionMap = 32 | new Dictionary(); 33 | 34 | /// 35 | /// enum description -> value 36 | /// 37 | internal static readonly IDictionary DescriptionToValueMap = 38 | new Dictionary(); 39 | 40 | /// 41 | /// value int -> enum value 42 | /// 43 | internal static readonly IDictionary ValueIntToValueMap = 44 | new Dictionary(); 45 | 46 | /// 47 | /// Builds maps. 48 | /// 49 | static EnumInternal() 50 | { 51 | foreach (T enumValue in Enum.GetValues(typeof(T))) 52 | { 53 | var enumName = Enum.GetName(typeof(T), enumValue); 54 | NameToValueMap.Add(enumName, enumValue); 55 | ValueToNameMap.Add(enumValue, enumName); 56 | var description = GetDescription(enumValue); 57 | ValueToDescriptionMap.Add(enumValue, description); 58 | DescriptionToValueMap.Add(description, enumValue); 59 | var valueInt = (int)Convert.ChangeType(enumValue, typeof(T)); 60 | ValueIntToValueMap.Add(valueInt, enumValue); 61 | } 62 | } 63 | 64 | /// 65 | /// Gets description (DescriptionAttribute) for enum value or string representation if not exists. 66 | /// 67 | private static string GetDescription(T enumValue) 68 | { 69 | var field = enumValue.GetType().GetField(enumValue.ToString()); 70 | var description = 71 | field.GetCustomAttributes(typeof(DescriptionAttribute), false) 72 | .Cast() 73 | .Select(x => x.Description) 74 | .SingleOrDefault(); 75 | if (description.IsNullOrEmpty()) 76 | { 77 | description = enumValue.ToString(); 78 | } 79 | return description; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/rm.Extensions/ExceptionHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace rm.Extensions; 4 | 5 | /// 6 | /// Exception helper class. 7 | /// 8 | internal class ExceptionHelper 9 | { 10 | /// 11 | /// Throws NullReferenceException if true with message. 12 | /// 13 | internal static void ThrowIfNull(bool throwEx, string exMessage) 14 | { 15 | if (throwEx) 16 | { 17 | throw new NullReferenceException(exMessage); 18 | } 19 | } 20 | 21 | /// 22 | /// Throws ArgumentNullException if true with message. 23 | /// 24 | internal static void ThrowIfArgumentNull(bool throwEx, string exMessage) 25 | { 26 | if (throwEx) 27 | { 28 | throw new ArgumentNullException(exMessage); 29 | } 30 | } 31 | 32 | /// 33 | /// Throws EmptyException if true with message. 34 | /// 35 | internal static void ThrowIfEmpty(bool throwEx, string exMessage) 36 | { 37 | if (throwEx) 38 | { 39 | throw new EmptyException(exMessage); 40 | } 41 | } 42 | 43 | /// 44 | /// Throws ArgumentOutOfRangeException if true with message. 45 | /// 46 | internal static void ThrowIfArgumentOutOfRange(bool throwEx, string exMessage) 47 | { 48 | if (throwEx) 49 | { 50 | throw new ArgumentOutOfRangeException(exMessage); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/rm.Extensions/GenericComparer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace rm.Extensions; 5 | 6 | /// 7 | /// Generic class that implements IComparer{T}. 8 | /// 9 | public class GenericComparer : IComparer 10 | { 11 | private Func compare; 12 | 13 | public GenericComparer(Func compare) 14 | { 15 | compare.ThrowIfArgumentNull(nameof(compare)); 16 | this.compare = compare; 17 | } 18 | 19 | #region IComparer methods 20 | 21 | public int Compare(T x, T y) 22 | { 23 | return compare(x, y); 24 | } 25 | 26 | #endregion 27 | } 28 | -------------------------------------------------------------------------------- /src/rm.Extensions/GenericEqualityComparer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace rm.Extensions; 5 | 6 | /// 7 | /// Generic class that implements IEqualityComparer{T} for {TKey} key selector. 8 | /// 9 | /// http://stackoverflow.com/questions/188120/can-i-specify-my-explicit-type-comparator-inline 10 | public class GenericEqualityComparer : IEqualityComparer 11 | { 12 | private Func projection; 13 | 14 | public GenericEqualityComparer(Func projection) 15 | { 16 | projection.ThrowIfArgumentNull(nameof(projection)); 17 | this.projection = projection; 18 | } 19 | 20 | #region IEqualityComparer methods 21 | 22 | public bool Equals(T x, T y) 23 | { 24 | if (x == null && y == null) 25 | { 26 | return true; 27 | } 28 | if (x == null || y == null) 29 | { 30 | return false; 31 | } 32 | // note: projection(x).Equals(projection(y)) uses object.Equals(object), 33 | // instead of TKey.Equals(Tkey), which gives incorrect results. 34 | return EqualityComparer.Default.Equals(projection(x), projection(y)); 35 | } 36 | 37 | public int GetHashCode(T obj) 38 | { 39 | return projection(obj).GetHashCode(); 40 | } 41 | 42 | #endregion 43 | } 44 | 45 | /// 46 | /// Helper class to create GenericEqualityComparer{T, TKey}. 47 | /// 48 | public static class GenericEqualityComparer 49 | { 50 | public static IEqualityComparer By(Func projection) 51 | { 52 | return new GenericEqualityComparer(projection); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/rm.Extensions/GraphExtension.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace rm.Extensions; 4 | 5 | /// 6 | /// Graph extensions. 7 | /// 8 | public static class GraphExtension 9 | { 10 | /// 11 | /// Returns true if graph is cyclic. 12 | /// 13 | public static bool IsCyclic(this IGraph graph) 14 | { 15 | graph.ThrowIfArgumentNull(nameof(graph)); 16 | graph.Nodes.ThrowIfArgumentNull(nameof(graph.Nodes)); 17 | var acyclicNodes = new HashSet(); 18 | var path = new HashSet(); 19 | foreach (var node in graph.Nodes) 20 | { 21 | if (IsCyclic(node, path, acyclicNodes)) 22 | { 23 | return true; 24 | } 25 | } 26 | return false; 27 | } 28 | 29 | /// 30 | /// Returns true if graph node is cyclic. 31 | /// 32 | /// Graph node. 33 | /// Path from a graph node to this . 34 | /// Nodes from which a cycle does not exist. 35 | /// Returns true if graph node is cyclic. 36 | private static bool IsCyclic(IGraphNode node, ISet path, ISet acyclicNodes) 37 | { 38 | node.ThrowIfArgumentNull(nameof(node)); 39 | node.Id.ThrowIfArgumentNull(nameof(node.Id)); 40 | node.Neighbors.ThrowIfArgumentNull(nameof(node.Neighbors)); 41 | if (acyclicNodes.Contains(node.Id)) 42 | { 43 | return false; 44 | } 45 | if (path.Contains(node.Id)) 46 | { 47 | return true; 48 | } 49 | path.Add(node.Id); 50 | foreach (var neighbor in node.Neighbors) 51 | { 52 | if (IsCyclic(neighbor, path, acyclicNodes)) 53 | { 54 | return true; 55 | } 56 | } 57 | path.Remove(node.Id); 58 | acyclicNodes.Add(node.Id); 59 | return false; 60 | } 61 | } 62 | 63 | #region Graph interfaces 64 | 65 | /// 66 | /// Defines Graph. 67 | /// 68 | public interface IGraph 69 | { 70 | /// 71 | /// Nodes in the graph. 72 | /// 73 | IEnumerable Nodes { get; } 74 | } 75 | 76 | /// 77 | /// Defines Graph node. 78 | /// 79 | public interface IGraphNode 80 | { 81 | /// 82 | /// Node's Id. 83 | /// 84 | string Id { get; } 85 | 86 | /// 87 | /// Node's neighbors. 88 | /// 89 | IEnumerable Neighbors { get; } 90 | } 91 | 92 | #endregion 93 | -------------------------------------------------------------------------------- /src/rm.Extensions/GuidExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace rm.Extensions; 4 | 5 | /// 6 | /// Guid extensions. 7 | /// 8 | public static class GuidExtension 9 | { 10 | /// 11 | /// Returns a 16-element byte array that contains the value of this instance 12 | /// matching its string representation (endian-agnostic). 13 | /// 14 | /// See https://stackoverflow.com/questions/9195551/why-does-guid-tobytearray-order-the-bytes-the-way-it-does 15 | /// 16 | /// 17 | /// Note: The byte[] returned by will not yield 18 | /// the original Guid with ctor. 19 | /// 20 | /// 21 | public static byte[] ToByteArrayMatchingStringRepresentation(this Guid guid) 22 | { 23 | var bytes = guid.ToByteArray(); 24 | TweakOrderOfGuidBytesToMatchStringRepresentation(bytes); 25 | return bytes; 26 | } 27 | 28 | /// 29 | /// Initializes a new instance of the structure by using the specified array of bytes 30 | /// matching its string representation (endian-agnostic). 31 | /// 32 | /// See https://stackoverflow.com/questions/9195551/why-does-guid-tobytearray-order-the-bytes-the-way-it-does 33 | /// 34 | /// 35 | /// Note: The Guid returned by will not yield 36 | /// the original byte[] with . 37 | /// 38 | /// 39 | public static Guid ToGuidMatchingStringRepresentation(this byte[] bytes) 40 | { 41 | _ = bytes ?? 42 | throw new ArgumentNullException(nameof(bytes)); 43 | if (bytes.Length != 16) 44 | { 45 | throw new ArgumentException("Length should be 16.", nameof(bytes)); 46 | } 47 | TweakOrderOfGuidBytesToMatchStringRepresentation(bytes); 48 | return new Guid(bytes); 49 | } 50 | 51 | /// 52 | /// Tweaks the order of in-place as per endianness. 53 | /// 54 | /// 55 | /// This method should be kept private to avoid confusion as it tweaks in-place. 56 | /// 57 | private static void TweakOrderOfGuidBytesToMatchStringRepresentation(byte[] guidBytes) 58 | { 59 | if (BitConverter.IsLittleEndian) 60 | { 61 | Array.Reverse(guidBytes, 0, 4); 62 | Array.Reverse(guidBytes, 4, 2); 63 | Array.Reverse(guidBytes, 6, 2); 64 | } 65 | } 66 | 67 | /// 68 | /// Returns guid's bytes (matching string representation) in Base64 format. 69 | /// 70 | /// 71 | /// See https://cryptii.com/pipes/text-to-base64. 72 | /// 73 | /// 74 | public static string ToBase64String(this Guid guid) 75 | { 76 | return guid.ToByteArrayMatchingStringRepresentation().Base64Encode(); 77 | } 78 | 79 | /// 80 | /// Returns guid from Base64 format (matching string representation). 81 | /// 82 | /// 83 | /// See https://cryptii.com/pipes/text-to-base64. 84 | /// 85 | /// 86 | public static Guid FromBase64String(this string guidString) 87 | { 88 | return guidString.Base64Decode().ToGuidMatchingStringRepresentation(); 89 | } 90 | 91 | /// 92 | /// Returns guid's bytes (matching string representation) in Base64 Url format. 93 | /// 94 | /// 95 | /// See https://cryptii.com/pipes/text-to-base64. 96 | /// 97 | /// 98 | public static string ToBase64UrlString(this Guid guid) 99 | { 100 | return guid.ToByteArrayMatchingStringRepresentation().Base64UrlEncode(); 101 | } 102 | 103 | /// 104 | /// Returns guid from Base64 Url format (matching string representation). 105 | /// 106 | /// 107 | /// See https://cryptii.com/pipes/text-to-base64. 108 | /// 109 | /// 110 | public static Guid FromBase64UrlString(this string guidString) 111 | { 112 | return guidString.Base64UrlDecode().ToGuidMatchingStringRepresentation(); 113 | } 114 | 115 | /// 116 | /// Returns guid's bytes (matching string representation) in Base32 format. 117 | /// 118 | /// 119 | /// See https://cryptii.com/pipes/base32-to-hex. 120 | /// 121 | /// 122 | public static string ToBase32String(this Guid guid) 123 | { 124 | return guid.ToByteArrayMatchingStringRepresentation().Base32Encode(); 125 | } 126 | 127 | /// 128 | /// Returns guid from Base32 format (matching string representation). 129 | /// 130 | /// 131 | /// See https://cryptii.com/pipes/base32-to-hex. 132 | /// 133 | /// 134 | public static Guid FromBase32String(this string guidString) 135 | { 136 | return guidString.Base32Decode().ToGuidMatchingStringRepresentation(); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/rm.Extensions/HashQueue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using rm.Extensions.Deque; 5 | using Ex = rm.Extensions.ExceptionHelper; 6 | 7 | namespace rm.Extensions; 8 | 9 | /// 10 | /// Defines hashqueue methods. 11 | /// 12 | public interface IHashQueue : IEnumerable 13 | { 14 | /// 15 | /// Enqueues into hashqueue. 16 | /// 17 | void Enqueue(T x); 18 | 19 | /// 20 | /// Dequeues head from hashqueue. 21 | /// 22 | T Dequeue(); 23 | 24 | /// 25 | /// Deletes given from hashqueue. 26 | /// Returns true if successful. 27 | /// 28 | bool Delete(T x); 29 | 30 | /// 31 | /// Peeks head of hashqueue. 32 | /// 33 | T Peek(); 34 | 35 | /// 36 | /// Peeks tail of hashqueue. 37 | /// 38 | T PeekTail(); 39 | 40 | /// 41 | /// Returns true if is in hashqueue. 42 | /// 43 | bool Has(T x); 44 | 45 | /// 46 | /// Returns true if hashqueue is empty. 47 | /// 48 | bool IsEmpty(); 49 | 50 | /// 51 | /// Returns count of hashqueue. 52 | /// 53 | int Count(); 54 | 55 | /// 56 | /// Returns long count of hashqueue. 57 | /// 58 | long LongCount(); 59 | 60 | /// 61 | /// Clears hashqueue. 62 | /// 63 | void Clear(); 64 | } 65 | 66 | /// 67 | /// Hashqueue. 68 | /// 69 | /// 70 | /// Uses deque and map as backing store. 71 | /// 72 | /// All methods are O(1) time. 73 | /// 74 | public class HashQueue : IHashQueue 75 | { 76 | #region members 77 | 78 | IDeque dq; 79 | IDictionary>> map; 80 | 81 | #endregion 82 | 83 | #region ctors 84 | 85 | public HashQueue() 86 | { 87 | dq = new Deque(); 88 | map = new Dictionary>>(); 89 | } 90 | 91 | #endregion 92 | 93 | #region IHashQueue methods 94 | 95 | /// 96 | /// Enqueues into hashqueue. 97 | /// 98 | public void Enqueue(T x) 99 | { 100 | x.ThrowIfArgumentNull(nameof(x)); 101 | if (!map.TryGetValue(x, out Deque> nodeq)) 102 | { 103 | nodeq = new Deque>(); 104 | map[x] = nodeq; 105 | } 106 | nodeq.Enqueue(dq.Enqueue(x)); 107 | } 108 | 109 | /// 110 | /// Dequeues head from hashqueue. 111 | /// 112 | public T Dequeue() 113 | { 114 | Ex.ThrowIfEmpty(IsEmpty(), "HashQueue is empty."); 115 | var x = dq.Dequeue(); 116 | DeleteFromMap(x); 117 | return x; 118 | } 119 | 120 | /// 121 | /// Deletes given from hashqueue. 122 | /// Returns true if successful. 123 | /// 124 | public bool Delete(T x) 125 | { 126 | Ex.ThrowIfEmpty(IsEmpty(), "HashQueue is empty."); 127 | x.ThrowIfArgumentNull(nameof(x)); 128 | if (!Has(x)) 129 | { 130 | return false; 131 | } 132 | dq.Delete(DeleteFromMap(x)); 133 | return true; 134 | } 135 | 136 | /// 137 | /// Deletes from and 138 | /// returns the node. 139 | /// 140 | private Node DeleteFromMap(T x) 141 | { 142 | var nodeq = map[x]; 143 | foreach (var node in nodeq.Nodes()) 144 | { 145 | var xnode = node.Value; 146 | if (EqualityComparer.Default.Equals(xnode.Value, x)) 147 | { 148 | nodeq.Delete(node); 149 | if (nodeq.IsEmpty()) 150 | { 151 | map.Remove(x); 152 | } 153 | return node.Value; 154 | } 155 | } 156 | throw new InvalidOperationException( 157 | $"Node not found for item {x} in HashQueue."); 158 | } 159 | 160 | /// 161 | /// Peeks head of hashqueue. 162 | /// 163 | public T Peek() 164 | { 165 | Ex.ThrowIfEmpty(IsEmpty(), "HashQueue is empty."); 166 | return dq.Peek(); 167 | } 168 | 169 | /// 170 | /// Peeks tail of hashqueue. 171 | /// 172 | public T PeekTail() 173 | { 174 | Ex.ThrowIfEmpty(IsEmpty(), "HashQueue is empty."); 175 | return dq.PeekTail(); 176 | } 177 | 178 | /// 179 | /// Returns true if is in hashqueue. 180 | /// 181 | public bool Has(T x) 182 | { 183 | x.ThrowIfArgumentNull(nameof(x)); 184 | return map.ContainsKey(x); 185 | } 186 | 187 | /// 188 | /// Returns true if hashqueue is empty. 189 | /// 190 | public bool IsEmpty() 191 | { 192 | return dq.IsEmpty(); 193 | } 194 | 195 | /// 196 | /// Returns count of hashqueue. 197 | /// 198 | public int Count() 199 | { 200 | return dq.Count(); 201 | } 202 | 203 | /// 204 | /// Returns long count of hashqueue. 205 | /// 206 | public long LongCount() 207 | { 208 | return dq.LongCount(); 209 | } 210 | 211 | /// 212 | /// Clears hashqueue. 213 | /// 214 | public void Clear() 215 | { 216 | dq.Clear(); 217 | map.Clear(); 218 | } 219 | 220 | #endregion 221 | 222 | #region IEnumerable methods 223 | 224 | public IEnumerator GetEnumerator() 225 | { 226 | return dq.GetEnumerator(); 227 | } 228 | 229 | IEnumerator IEnumerable.GetEnumerator() 230 | { 231 | return GetEnumerator(); 232 | } 233 | 234 | #endregion 235 | } 236 | -------------------------------------------------------------------------------- /src/rm.Extensions/Helper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace rm.Extensions; 5 | 6 | /// 7 | /// Helper class. 8 | /// 9 | public static class Helper 10 | { 11 | /// 12 | /// Swaps parameters. 13 | /// 14 | /// Helper.Swap(ref a, ref b); 15 | public static void Swap(ref T t1, ref T t2) 16 | { 17 | var temp = t1; 18 | t1 = t2; 19 | t2 = temp; 20 | } 21 | 22 | /// 23 | /// Swaps array elements for given indices. 24 | /// 25 | /// Helper.Swap(array, i, j); 26 | public static void Swap(T[] a, int i, int j) 27 | { 28 | T t = a[i]; 29 | a[i] = a[j]; 30 | a[j] = t; 31 | } 32 | 33 | /// 34 | /// Concats muliple arrays into an array. 35 | /// 36 | public static T[] Concat(params T[][] arrayOfArrays) 37 | { 38 | arrayOfArrays.ThrowIfNull(nameof(arrayOfArrays)); 39 | if (arrayOfArrays.Length == 0) 40 | { 41 | return Array.Empty(); 42 | } 43 | if (arrayOfArrays.Length == 1) 44 | { 45 | return arrayOfArrays[0]; 46 | } 47 | var array = new T[arrayOfArrays.Sum(arr => arr.Length)]; 48 | int offset = 0; 49 | for (int i = 0; i < arrayOfArrays.Length; i++) 50 | { 51 | arrayOfArrays[i].CopyTo(array, offset); 52 | offset += arrayOfArrays[i].Length; 53 | } 54 | return array; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/rm.Extensions/IntExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Numerics; 3 | 4 | namespace rm.Extensions; 5 | 6 | /// 7 | /// Int extensions. 8 | /// 9 | public static class IntExtension 10 | { 11 | /// 12 | /// Gets n!. 13 | /// 14 | public static BigInteger Factorial(this int n) 15 | { 16 | n.ThrowIfArgumentOutOfRange(nameof(n)); 17 | BigInteger product = 1; 18 | for (int i = 1; i <= n; i++) 19 | { 20 | product *= i; 21 | } 22 | return product; 23 | } 24 | 25 | /// 26 | /// Gets nPr. 27 | /// 28 | public static BigInteger Permutation(this int n, int r) 29 | { 30 | n.ThrowIfArgumentOutOfRange(nameof(n)); 31 | r.ThrowIfArgumentOutOfRange(nameof(r), maxRange: n); 32 | BigInteger result = (n.Factorial() / (n - r).Factorial()); 33 | return result; 34 | } 35 | 36 | /// 37 | /// Gets nCr. 38 | /// 39 | public static BigInteger Combination(this int n, int r) 40 | { 41 | n.ThrowIfArgumentOutOfRange(nameof(n)); 42 | r.ThrowIfArgumentOutOfRange(nameof(r), maxRange: n); 43 | BigInteger result = (n.Factorial() / ((n - r).Factorial() * r.Factorial())); 44 | return result; 45 | } 46 | 47 | /// 48 | /// Gets scrabble count for n. 49 | /// 50 | /// nP1 + nP2 + ... + nPn 51 | public static BigInteger ScrabbleCount(this int n) 52 | { 53 | return ScrabbleCount(n, n); 54 | } 55 | 56 | /// 57 | /// Gets scrabble count for n with limit. 58 | /// 59 | /// nP1 + nP2 + ... + nPlimit, where limit is up to n 60 | public static BigInteger ScrabbleCount(this int n, int limit) 61 | { 62 | BigInteger sum = 0; 63 | for (int i = 1; i <= limit; i++) 64 | { 65 | sum += n.Permutation(i); 66 | } 67 | return sum; 68 | } 69 | 70 | /// 71 | /// Rounds int as "k" for kilo, "m" for mega, "g" for giga. 72 | /// 73 | public static string Round(this int n, uint digits = 0) 74 | { 75 | string s; 76 | var nabs = Math.Abs(n); 77 | if (nabs < 1000) 78 | { 79 | s = n + ""; 80 | } 81 | else if (nabs < 1000000) 82 | { 83 | s = ((decimal)n / 1000).TruncateTo(digits) + "k"; 84 | } 85 | else if (nabs < 1000000000) 86 | { 87 | s = ((decimal)n / 1000000).TruncateTo(digits) + "m"; 88 | } 89 | else 90 | { 91 | s = ((decimal)n / 1000000000).TruncateTo(digits) + "g"; 92 | } 93 | return s; 94 | } 95 | 96 | /// 97 | /// Rounds value down to an integer multiple of a given bin size. 98 | /// 99 | /// 100 | /// See https://docs.microsoft.com/en-us/azure/kusto/query/binfunction 101 | /// 102 | public static int Bin(this int n, int binSize) 103 | { 104 | var result = (n / binSize) * binSize; 105 | if (n < 0) 106 | { 107 | result -= binSize; 108 | } 109 | return result; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/rm.Extensions/ListExtension.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace rm.Extensions; 4 | 5 | /// 6 | /// List extensions. 7 | /// 8 | public static class ListExtension 9 | { 10 | /// 11 | /// Removes the last item(s) in the list. 12 | /// 13 | public static void RemoveLast(this IList source, int n = 1) 14 | { 15 | for (int i = 0; i < n; i++) 16 | { 17 | source.RemoveAt(source.Count - 1); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/rm.Extensions/LruCache.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace rm.Extensions; 4 | 5 | /// 6 | /// Defines LRU cache methods. 7 | /// 8 | interface ILruCache 9 | { 10 | /// 11 | /// Gets value for from cache. 12 | /// 13 | /// 14 | /// Makes item MRU. 15 | /// 16 | TValue Get(TKey key); 17 | 18 | /// 19 | /// Inserts for in cache. 20 | /// 21 | /// 22 | /// Makes item MRU (also if already exists). 23 | /// 24 | void Insert(TKey key, TValue value); 25 | 26 | /// 27 | /// Removes from cache. 28 | /// 29 | bool Remove(TKey key); 30 | 31 | /// 32 | /// Returns true if exists in cache. 33 | /// 34 | bool HasKey(TKey key); 35 | 36 | /// 37 | /// Returns true if cache is empty. 38 | /// 39 | bool IsEmpty(); 40 | 41 | /// 42 | /// Returns true if cache is full. 43 | /// 44 | bool IsFull(); 45 | 46 | /// 47 | /// Returns capacity of cache. 48 | /// 49 | int Capacity(); 50 | 51 | /// 52 | /// Returns count of cache. 53 | /// 54 | int Count(); 55 | 56 | /// 57 | /// Clears cache. 58 | /// 59 | void Clear(); 60 | } 61 | 62 | /// 63 | /// LRU cache. 64 | /// 65 | /// 66 | /// Uses deque and map as backing store. 67 | /// 68 | /// All methods are O(1) time. 69 | /// 70 | public class LruCache : ILruCache 71 | { 72 | #region members 73 | 74 | private readonly int n; 75 | private readonly IDeque dq; 76 | private readonly IDictionary, TValue)> map; 77 | 78 | #endregion 79 | 80 | #region ctors 81 | 82 | public LruCache(int n) 83 | { 84 | n.ThrowIfArgumentOutOfRange(nameof(n)); 85 | this.n = n; 86 | dq = new Deque(); 87 | map = new Dictionary, TValue)>(capacity: n); 88 | } 89 | 90 | #endregion 91 | 92 | #region ILruCache methods 93 | 94 | /// 95 | /// Gets value for from cache. 96 | /// 97 | /// 98 | /// Makes item MRU. 99 | /// 100 | public TValue Get(TKey key) 101 | { 102 | if (!map.ContainsKey(key)) 103 | { 104 | return default(TValue); 105 | } 106 | var (node, value) = map[key]; 107 | dq.MakeTail(node); 108 | return value; 109 | } 110 | 111 | /// 112 | /// Inserts for in cache. 113 | /// 114 | /// 115 | /// Makes item MRU (also if already exists). 116 | /// 117 | public void Insert(TKey key, TValue value) 118 | { 119 | key.ThrowIfArgumentNull(nameof(key)); 120 | if (Capacity() == 0) 121 | { 122 | return; 123 | } 124 | if (map.ContainsKey(key)) 125 | { 126 | var (node, _) = map[key]; 127 | dq.MakeTail(node); 128 | map[key] = (node, value); 129 | return; 130 | } 131 | if (IsFull()) 132 | { 133 | // remove lru 134 | map.Remove(dq.Dequeue()); 135 | } 136 | map[key] = (dq.Enqueue(key), value); 137 | } 138 | 139 | /// 140 | /// Removes from cache. 141 | /// 142 | public bool Remove(TKey key) 143 | { 144 | if (!map.ContainsKey(key)) 145 | { 146 | return false; 147 | } 148 | var (node, _) = map[key]; 149 | dq.Delete(node); 150 | map.Remove(key); 151 | return true; 152 | } 153 | 154 | /// 155 | /// Returns true if exists in cache. 156 | /// 157 | public bool HasKey(TKey key) 158 | { 159 | return map.ContainsKey(key); 160 | } 161 | 162 | /// 163 | /// Returns true if cache is empty. 164 | /// 165 | public bool IsEmpty() 166 | { 167 | return Count() == 0; 168 | } 169 | 170 | /// 171 | /// Returns true if cache is full. 172 | /// 173 | public bool IsFull() 174 | { 175 | return Count() == Capacity(); 176 | } 177 | 178 | /// 179 | /// Returns capacity of cache. 180 | /// 181 | public int Capacity() 182 | { 183 | return n; 184 | } 185 | 186 | /// 187 | /// Returns count of cache. 188 | /// 189 | public int Count() 190 | { 191 | return map.Count; 192 | } 193 | 194 | /// 195 | /// Clears cache. 196 | /// 197 | public void Clear() 198 | { 199 | dq.Clear(); 200 | map.Clear(); 201 | } 202 | 203 | #endregion 204 | } 205 | -------------------------------------------------------------------------------- /src/rm.Extensions/NameValueCollectionExtension.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Specialized; 2 | using System.Text; 3 | using System.Web; 4 | 5 | namespace rm.Extensions; 6 | 7 | /// 8 | /// NameValueCollection extensions. 9 | /// 10 | public static class NameValueCollectionExtension 11 | { 12 | /// 13 | /// Gets query string for name value collection. 14 | /// 15 | public static string ToQueryString(this NameValueCollection collection, 16 | bool prefixQuestionMark = true) 17 | { 18 | collection.ThrowIfArgumentNull(nameof(collection)); 19 | if (collection.Keys.Count == 0) 20 | { 21 | return ""; 22 | } 23 | var buffer = new StringBuilder(); 24 | if (prefixQuestionMark) 25 | { 26 | buffer.Append("?"); 27 | } 28 | var append = false; 29 | for (int i = 0; i < collection.Keys.Count; i++) 30 | { 31 | var key = collection.Keys[i]; 32 | var values = collection.GetValues(key); 33 | key.ThrowIfNull(nameof(key)); 34 | values.ThrowIfNull(nameof(values)); 35 | foreach (var value in values) 36 | { 37 | if (append) 38 | { 39 | buffer.Append("&"); 40 | } 41 | append = true; 42 | buffer.AppendFormat("{0}={1}", key.UrlEncode(), value.UrlEncode()); 43 | } 44 | } 45 | return buffer.ToString(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/rm.Extensions/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("rm.ExtensionsTest")] 4 | -------------------------------------------------------------------------------- /src/rm.Extensions/RandomExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace rm.Extensions; 4 | 5 | /// 6 | /// Random extensions. 7 | /// 8 | public static class RandomExtension 9 | { 10 | /// 11 | /// Returns a random item from . 12 | /// 13 | public static T NextItem(this Random random, T[] source) 14 | { 15 | source.ThrowIfArgumentNull(nameof(source)); 16 | if (source.IsNullOrEmpty()) 17 | { 18 | throw new ArgumentOutOfRangeException(nameof(source.Length), source.Length, null); 19 | } 20 | 21 | return source[random.Next(source.Length)]; 22 | } 23 | 24 | /// 25 | /// source 26 | /// 27 | public static double NextGaussian(this Random random, double mu = 0, double sigma = 1) 28 | { 29 | // uniform(0,1] random doubles 30 | double u1 = 1.0 - random.NextDouble(); 31 | double u2 = 1.0 - random.NextDouble(); 32 | // random normal(0,1) 33 | double randStdNormal = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); 34 | // random normal(mean,stdDev^2) 35 | double randNormal = mu + sigma * randStdNormal; 36 | return randNormal; 37 | } 38 | 39 | /// 40 | /// Generates random string of for . 41 | /// 42 | /// 43 | /// source 44 | /// 45 | public static string NextString(this Random random, int length, string charset) 46 | { 47 | length.ThrowIfArgumentOutOfRange(nameof(length)); 48 | if (charset.IsNullOrEmpty()) 49 | { 50 | throw new ArgumentException(nameof(charset)); 51 | } 52 | var randomString = new char[length]; 53 | for (int i = 0; i < length; i++) 54 | { 55 | randomString[i] = charset[random.Next(charset.Length)]; 56 | } 57 | return new string(randomString); 58 | } 59 | 60 | /// 61 | /// Generates random double between and . 62 | /// 63 | /// Note: This is a scaled implementation: (random.nextDouble() * (max - min)) + min 64 | /// 65 | /// 66 | /// source 67 | /// 68 | public static double NextDouble(this Random random, double minValue = 0d, double maxValue = double.MaxValue) 69 | { 70 | if (minValue > maxValue) 71 | { 72 | throw new ArgumentOutOfRangeException(nameof(minValue), "'minValue' cannot be greater than 'maxValue'"); 73 | } 74 | // scaled 75 | return (random.NextDouble() * (maxValue - minValue)) + minValue; 76 | } 77 | 78 | /// 79 | /// Generates random decimal between and . 80 | /// 81 | /// Note: This is a scaled implementation: (random.nextDouble() * (max - min)) + min 82 | /// 83 | /// 84 | /// source 85 | /// 86 | public static decimal NextDecimal(this Random random, decimal minValue = decimal.Zero, decimal maxValue = decimal.MaxValue) 87 | { 88 | if (minValue > maxValue) 89 | { 90 | throw new ArgumentOutOfRangeException(nameof(minValue), "'minValue' cannot be greater than 'maxValue'"); 91 | } 92 | // scaled 93 | return ((decimal)random.NextDouble() * (maxValue - minValue)) + minValue; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/rm.Extensions/StopwatchExtension.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace rm.Extensions; 4 | 5 | /// 6 | /// Stopwatch extensions. 7 | /// 8 | public static class StopwatchExtension 9 | { 10 | public static long ElapsedTicks(this Stopwatch sw) 11 | { 12 | return sw.ElapsedTicks; 13 | } 14 | public static long ElapsedMilliseconds(this Stopwatch sw) 15 | { 16 | return sw.ElapsedMilliseconds; 17 | } 18 | public static long ElapsedSeconds(this Stopwatch sw) 19 | { 20 | return sw.ElapsedMilliseconds / 1000; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/rm.Extensions/StringBuilderExtension.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace rm.Extensions; 4 | 5 | /// 6 | /// StringBuilder extensions. 7 | /// 8 | public static class StringBuilderExtension 9 | { 10 | /// 11 | /// Appends formatted args followed by newline. 12 | /// 13 | public static StringBuilder AppendLine(this StringBuilder sb, string format, params object[] args) 14 | { 15 | sb.ThrowIfArgumentNull(nameof(sb)); 16 | sb.AppendLine(string.Format(format, args)); 17 | return sb; 18 | } 19 | 20 | /// 21 | /// Reverses this instance in-place. 22 | /// 23 | /// 24 | /// Does not work for string with surrogate pairs as, 25 | /// "Les Misérables" 26 | /// See https://stackoverflow.com/questions/228038/best-way-to-reverse-a-string 27 | /// 28 | public static StringBuilder Reverse(this StringBuilder sb) 29 | { 30 | sb.ThrowIfArgumentNull(nameof(sb)); 31 | var start = 0; 32 | var end = sb.Length - 1; 33 | while (start < end) 34 | { 35 | var temp = sb[start]; 36 | sb[start] = sb[end]; 37 | sb[end] = temp; 38 | start++; 39 | end--; 40 | } 41 | return sb; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/rm.Extensions/ThrowExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Ex = rm.Extensions.ExceptionHelper; 4 | 5 | namespace rm.Extensions; 6 | 7 | /// 8 | /// Throw extensions. 9 | /// 10 | public static class ThrowExtension 11 | { 12 | /// 13 | /// Throws exception if is null. 14 | /// 15 | /// Input. 16 | /// Exception message. 17 | /// Input. 18 | public static T ThrowIfNull(this T t, string exMessage = "") 19 | { 20 | Ex.ThrowIfNull(t == null, exMessage); 21 | return t; 22 | } 23 | 24 | /// 25 | /// Throws exception if argument is null. 26 | /// 27 | /// Input. 28 | /// Exception message. 29 | /// Input. 30 | public static T ThrowIfArgumentNull(this T t, string exMessage = "") 31 | { 32 | Ex.ThrowIfArgumentNull(t == null, exMessage); 33 | return t; 34 | } 35 | 36 | /// 37 | /// Throws exception if any of the is null. 38 | /// 39 | /// Input. 40 | /// Input. 41 | public static IEnumerable ThrowIfAnyNull(this IEnumerable objects) 42 | { 43 | foreach (var o in objects) 44 | { 45 | o.ThrowIfNull(); 46 | } 47 | return objects; 48 | } 49 | 50 | /// 51 | /// Throws exception if any of the arguments is null. 52 | /// 53 | /// Input. 54 | /// Input. 55 | public static IEnumerable ThrowIfAnyArgumentNull(this IEnumerable objects) 56 | { 57 | foreach (var o in objects) 58 | { 59 | o.ThrowIfArgumentNull(); 60 | } 61 | return objects; 62 | } 63 | 64 | /// 65 | /// Throws exception if the string is null or empty. 66 | /// 67 | /// Input. 68 | /// Exception message. 69 | /// Input. 70 | public static string ThrowIfNullOrEmpty(this string s, string exMessage = "") 71 | { 72 | Ex.ThrowIfNull(s == null, exMessage); 73 | Ex.ThrowIfEmpty(s.Length == 0, exMessage); 74 | return s; 75 | } 76 | 77 | /// 78 | /// Throws exception if the string argument is null or empty. 79 | /// 80 | /// Input. 81 | /// Exception message. 82 | /// Input. 83 | public static string ThrowIfNullOrEmptyArgument(this string s, string exMessage = "") 84 | { 85 | Ex.ThrowIfArgumentNull(s == null, exMessage); 86 | Ex.ThrowIfEmpty(s.Length == 0, exMessage); 87 | return s; 88 | } 89 | 90 | /// 91 | /// Throws exception if any of the strings is null or empty. 92 | /// 93 | /// Input. 94 | /// Input. 95 | public static IEnumerable ThrowIfNullOrEmpty(this IEnumerable strings) 96 | { 97 | foreach (var s in strings) 98 | { 99 | s.ThrowIfNullOrEmpty(); 100 | } 101 | return strings; 102 | } 103 | 104 | /// 105 | /// Throws exception if any of the string arguments is null or empty. 106 | /// 107 | /// Input. 108 | /// Input. 109 | public static IEnumerable ThrowIfNullOrEmptyArgument(this IEnumerable strings) 110 | { 111 | foreach (var s in strings) 112 | { 113 | s.ThrowIfNullOrEmptyArgument(); 114 | } 115 | return strings; 116 | } 117 | 118 | /// 119 | /// Throws exception if the string is null or whitespace. 120 | /// 121 | /// Input. 122 | /// Exception message. 123 | /// Input. 124 | public static string ThrowIfNullOrWhiteSpace(this string s, string exMessage = "") 125 | { 126 | Ex.ThrowIfNull(s == null, exMessage); 127 | Ex.ThrowIfEmpty(s.Trim().Length == 0, exMessage); 128 | return s; 129 | } 130 | 131 | /// 132 | /// Throws exception if the string argument is null or whitespace. 133 | /// 134 | /// Input. 135 | /// Exception message. 136 | /// Input. 137 | public static string ThrowIfNullOrWhiteSpaceArgument(this string s, string exMessage = "") 138 | { 139 | Ex.ThrowIfArgumentNull(s == null, exMessage); 140 | Ex.ThrowIfEmpty(s.Trim().Length == 0, exMessage); 141 | return s; 142 | } 143 | 144 | /// 145 | /// Throws exception if is out of range. 146 | /// 147 | /// Input. 148 | /// Exception message. 149 | /// Min range value. 150 | /// Max range value. 151 | /// Input. 152 | public static int ThrowIfArgumentOutOfRange(this int x, string exMessage = "", 153 | int minRange = 0, int maxRange = int.MaxValue) 154 | { 155 | Ex.ThrowIfArgumentOutOfRange(x < minRange || x > maxRange, 156 | exMessage); 157 | return x; 158 | } 159 | 160 | /// 161 | /// Throws exception if is out of range (uint). 162 | /// 163 | /// Input. 164 | /// Exception message. 165 | /// Min range value. 166 | /// Max range value. 167 | /// Input. 168 | public static uint ThrowIfArgumentOutOfRange(this uint x, string exMessage = "", 169 | uint minRange = 0, uint maxRange = uint.MaxValue) 170 | { 171 | Ex.ThrowIfArgumentOutOfRange(x < minRange || x > maxRange, 172 | exMessage); 173 | return x; 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/rm.Extensions/TimeSpanExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace rm.Extensions; 4 | 5 | /// 6 | /// TimeSpan extensions. 7 | /// 8 | public static class TimespanExtension 9 | { 10 | /// 11 | /// Rounds timespan. 12 | /// 13 | /// Ex: ms, s, h, d, wk, mth, y. 14 | /// 15 | /// 16 | public static string Round(this TimeSpan ts) 17 | { 18 | if (ts.Days >= 365) 19 | { 20 | return "{0}y".Format(ts.Days / 365); 21 | } 22 | if (ts.Days >= 30) 23 | { 24 | return "{0}mth".Format(ts.Days / 30); 25 | } 26 | if (ts.Days >= 7) 27 | { 28 | return "{0}wk".Format(ts.Days / 7); 29 | } 30 | if (ts.Days > 0) 31 | { 32 | return "{0}d".Format(ts.Days); 33 | } 34 | if (ts.Hours > 0) 35 | { 36 | return "{0}h".Format(ts.Hours); 37 | } 38 | if (ts.Minutes > 0) 39 | { 40 | return "{0}m".Format(ts.Minutes); 41 | } 42 | if (ts.Seconds > 0) 43 | { 44 | return "{0}s".Format(ts.Seconds); 45 | } 46 | return "{0}ms".Format(ts.Milliseconds); 47 | } 48 | 49 | /// 50 | /// Gets timespan with days. 51 | /// 52 | public static TimeSpan Days(this int n) 53 | { 54 | return TimeSpan.FromDays(n); 55 | } 56 | 57 | /// 58 | /// Gets timespan with hours. 59 | /// 60 | public static TimeSpan Hours(this int n) 61 | { 62 | return TimeSpan.FromHours(n); 63 | } 64 | 65 | /// 66 | /// Gets timespan with minutes. 67 | /// 68 | public static TimeSpan Minutes(this int n) 69 | { 70 | return TimeSpan.FromMinutes(n); 71 | } 72 | 73 | /// 74 | /// Gets timespan with seconds. 75 | /// 76 | public static TimeSpan Seconds(this int n) 77 | { 78 | return TimeSpan.FromSeconds(n); 79 | } 80 | 81 | /// 82 | /// Gets timespan with milliseconds. 83 | /// 84 | public static TimeSpan Milliseconds(this int n) 85 | { 86 | return TimeSpan.FromMilliseconds(n); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/rm.Extensions/UnsupportedEnumValueException.TEnum.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.Serialization; 4 | using System.Text; 5 | 6 | namespace rm.Extensions; 7 | 8 | /// 9 | /// The exception that is thrown when the enum value is not supported. 10 | /// 11 | public class UnsupportedEnumValueException : Exception 12 | { 13 | public UnsupportedEnumValueException(string message) 14 | : base(message) { } 15 | public UnsupportedEnumValueException(string message, Exception inner) 16 | : base(message, inner) { } 17 | protected UnsupportedEnumValueException(SerializationInfo info, StreamingContext context) 18 | : base(info, context) { } 19 | 20 | public UnsupportedEnumValueException(TEnum enumValue) 21 | : base($"Value {enumValue} of enum {typeof(TEnum).Name} is not supported.") 22 | { } 23 | } 24 | -------------------------------------------------------------------------------- /src/rm.Extensions/UriExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Security.Cryptography; 4 | 5 | namespace rm.Extensions; 6 | 7 | /// 8 | /// Hash algorithm type. 9 | /// 10 | public enum Hasher 11 | { 12 | sha1 = 1, 13 | md5, 14 | } 15 | 16 | /// 17 | /// Uri extensions. 18 | /// 19 | public static class UriExtension 20 | { 21 | /// 22 | /// Calculates checksum / hash (sha1 or md5) for uri (url or local file). 23 | /// 24 | /// hash: sha1 20 bytes, md5 hash 16 bytes 25 | /// 26 | /// 27 | /// uri (url or local file). 28 | /// Hasher.sha1 or Hasher.md5. 29 | /// hash 30 | /// http://hash.online-convert.com/ 31 | public static string Checksum(this Uri uri, Hasher type = Hasher.sha1) 32 | { 33 | uri.ThrowIfArgumentNull(nameof(uri)); 34 | switch (type) 35 | { 36 | case Hasher.sha1: 37 | using (var hasher = SHA1.Create()) 38 | { 39 | return Checksum(hasher, uri); 40 | } 41 | case Hasher.md5: 42 | using (var hasher = MD5.Create()) 43 | { 44 | return Checksum(hasher, uri); 45 | } 46 | default: 47 | throw new ArgumentOutOfRangeException(nameof(type), type, "Unknown hash algorithm type."); 48 | } 49 | } 50 | 51 | private static string Checksum(HashAlgorithm hasher, Uri uri) 52 | { 53 | // webclient works for uris and local files 54 | using (var webclient = new WebClient()) 55 | { 56 | using (var stream = webclient.OpenRead(uri)) 57 | { 58 | stream.ThrowIfNull(nameof(stream)); 59 | var hash = BitConverter.ToString(hasher.ComputeHash(stream)).Replace("-", "").ToLower(); 60 | return hash; 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/rm.Extensions/Wrapped.cs: -------------------------------------------------------------------------------- 1 | namespace rm.Extensions; 2 | 3 | /// 4 | /// Wrapped type. 5 | /// 6 | /// Useful to avoid pass by reference parameters. 7 | public class Wrapped 8 | { 9 | public T Value { get; set; } 10 | 11 | public Wrapped(T value) 12 | { 13 | Value = value; 14 | } 15 | 16 | ///// 17 | ///// Converts T to Wrapped{T}. 18 | ///// 19 | ///// 20 | ///// This does NOT work as a new instance is returned. 21 | ///// And cannot overload the assignment "=" operator in C#. 22 | ///// 23 | ///// It WOULD allow this: 24 | ///// // same as wrappedT.Value = value; 25 | ///// wrappedT = value; 26 | ///// 27 | //public static implicit operator Wrapped(T value) 28 | //{ 29 | // return new Wrapped(value); 30 | //} 31 | 32 | ///// 33 | ///// Converts Wrapped{T} to T. 34 | ///// 35 | ///// 36 | ///// This works. But not allowing due to inconsistency (see above conversion operator limitation). 37 | ///// 38 | ///// It allows this: 39 | ///// // same as value = wrappedT.Value; 40 | ///// value = wrappedT; 41 | ///// 42 | //public static implicit operator T(Wrapped wrappedT) 43 | //{ 44 | // return wrappedT.Value; 45 | //} 46 | } 47 | -------------------------------------------------------------------------------- /src/rm.Extensions/WrappedExtension.cs: -------------------------------------------------------------------------------- 1 | namespace rm.Extensions; 2 | 3 | /// 4 | /// Wrapped extensions. 5 | /// 6 | public static class WrappedExtension 7 | { 8 | /// 9 | /// Returns a Wrapped{T} instance for . 10 | /// 11 | public static Wrapped Wrap(this T t) 12 | { 13 | return new Wrapped(t); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/rm.Extensions/rm.Extensions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0;netstandard2.1;netstandard2.0 5 | false 6 | $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb 7 | false 8 | true 9 | 1591;NU1507 10 | latest 11 | rmandvikar.Extensions 12 | A collection of utility C# extension methods. 13 | utility extensions 14 | https://github.com/rmandvikar/csharp-extensions 15 | https://github.com/rmandvikar/csharp-extensions 16 | README.md 17 | LICENSE 18 | hippy 19 | hippy 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/ArrayExtensionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using rm.Extensions; 4 | 5 | namespace rm.ExtensionsTest; 6 | 7 | [TestFixture] 8 | public class ArrayExtensionTest 9 | { 10 | [Test] 11 | public void EmptyTest01() 12 | { 13 | var empty = Array.Empty; 14 | var expected = Array.Empty(); 15 | Assert.AreSame(expected, empty); 16 | Assert.AreSame(Array.Empty, Array.Empty); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/Base16ExtensionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | using System.Text; 5 | using NUnit.Framework; 6 | using rm.Extensions; 7 | 8 | namespace rm.ExtensionsTest; 9 | 10 | public class Base16ExtensionTest 11 | { 12 | private const int iterations = 1_000_000; 13 | 14 | [Test] 15 | [TestCase("Man", "4D616E")] 16 | [TestCase("Woman", "576F6D616E")] 17 | [TestCase("The quick brown fox jumps over the lazy dog.", "54686520717569636B2062726F776E20666F78206A756D7073206F76657220746865206C617A7920646F672E")] 18 | public void Base16Encode_01(string s, string base16) 19 | { 20 | Assert.AreEqual(base16, s.ToUtf8Bytes().Base16Encode()); 21 | } 22 | 23 | [Test] 24 | [TestCase("4D616E", "Man")] 25 | [TestCase("576F6D616E", "Woman")] 26 | [TestCase("54686520717569636B2062726F776E20666F78206A756D7073206F76657220746865206C617A7920646F672E", "The quick brown fox jumps over the lazy dog.")] 27 | public void Base16Decode_01(string base16, string s) 28 | { 29 | Assert.AreEqual(s, base16.Base16Decode().ToUtf8String()); 30 | } 31 | 32 | [Test] 33 | [TestCase("54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f672e", "The quick brown fox jumps over the lazy dog.")] 34 | public void Base16Decode_Lowercase_01(string base16, string s) 35 | { 36 | Assert.AreEqual(s, base16.Base16Decode().ToUtf8String()); 37 | } 38 | 39 | [Test] 40 | [TestCase("F")] 41 | [TestCase("f")] 42 | public void Base16Decode_Invalid_01(string base16) 43 | { 44 | var ex = Assert.Throws(() => base16.Base16Decode()); 45 | } 46 | 47 | [Test] 48 | [TestCase("FU")] 49 | [TestCase("fu")] 50 | public void Base16Decode_Invalid_02(string base16) 51 | { 52 | var ex = Assert.Throws(() => base16.Base16Decode()); 53 | } 54 | 55 | [Test] 56 | [TestCase("base16")] 57 | public void Base16_Roundtrip_01(string s) 58 | { 59 | var base16 = s.ToUtf8Bytes().Base16Encode(); 60 | var roundtrip = base16.Base16Decode().ToUtf8String(); 61 | Assert.AreEqual(s, roundtrip); 62 | } 63 | 64 | [Explicit] 65 | [Test] 66 | [Category("slow")] 67 | public void Perf_Base16Encode() 68 | { 69 | var bytes = "The quick brown fox jumps over the lazy dog.".ToUtf8Bytes(); 70 | var sw = Stopwatch.StartNew(); 71 | for (int i = 0; i < iterations; i++) 72 | { 73 | var base16 = bytes.Base16Encode(); 74 | } 75 | sw.Stop(); 76 | Console.WriteLine(sw.ElapsedMilliseconds); 77 | } 78 | 79 | [Explicit] 80 | [Test] 81 | [Category("slow")] 82 | public void Perf_Base16EncodeUppercase() 83 | { 84 | var bytes = "The quick brown fox jumps over the lazy dog.".ToUtf8Bytes(); 85 | var sw = Stopwatch.StartNew(); 86 | for (int i = 0; i < iterations; i++) 87 | { 88 | var base16 = bytes.Base16EncodeUppercase(); 89 | } 90 | sw.Stop(); 91 | Console.WriteLine(sw.ElapsedMilliseconds); 92 | } 93 | 94 | [Explicit] 95 | [Test] 96 | [Category("slow")] 97 | public void Perf_Base16EncodeLowercase() 98 | { 99 | var bytes = "The quick brown fox jumps over the lazy dog.".ToUtf8Bytes(); 100 | var sw = Stopwatch.StartNew(); 101 | for (int i = 0; i < iterations; i++) 102 | { 103 | var base16 = bytes.Base16EncodeLowercase(); 104 | } 105 | sw.Stop(); 106 | Console.WriteLine(sw.ElapsedMilliseconds); 107 | } 108 | 109 | [Explicit] 110 | [Test] 111 | [Category("slow")] 112 | public void Perf_Base16Encode_SampleImpl() 113 | { 114 | var bytes = "The quick brown fox jumps over the lazy dog.".ToUtf8Bytes(); 115 | var sw = Stopwatch.StartNew(); 116 | for (int i = 0; i < iterations; i++) 117 | { 118 | var hex = Base16Encode_SampleImpl(bytes); 119 | } 120 | sw.Stop(); 121 | Console.WriteLine(sw.ElapsedMilliseconds); 122 | } 123 | 124 | public static string Base16Encode_SampleImpl(byte[] bytes) 125 | { 126 | #if NET5_0_OR_GREATER 127 | return Convert.ToHexString(bytes); 128 | #else 129 | // slower 130 | //StringBuilder hex = new StringBuilder(bytes.Length * 2); 131 | //foreach (byte b in bytes) 132 | //{ 133 | // hex.AppendFormat("{0:x2}", b); 134 | //} 135 | //return hex.ToString(); 136 | 137 | return BitConverter.ToString(bytes).Replace("-", ""); 138 | #endif 139 | } 140 | 141 | [Explicit] 142 | [Test] 143 | [Category("slow")] 144 | public void Perf_Base16Decode() 145 | { 146 | var base16 = "54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f672e"; 147 | var sw = Stopwatch.StartNew(); 148 | for (int i = 0; i < iterations; i++) 149 | { 150 | var bytes = base16.Base16Decode(); 151 | } 152 | sw.Stop(); 153 | Console.WriteLine(sw.ElapsedMilliseconds); 154 | } 155 | 156 | [Explicit] 157 | [Test] 158 | [Category("slow")] 159 | public void Perf_Base16Decode_SampleImpl() 160 | { 161 | var base16 = "54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f672e"; 162 | var sw = Stopwatch.StartNew(); 163 | for (int i = 0; i < iterations; i++) 164 | { 165 | var bytes = Base16Decode_SampleImpl(base16); 166 | } 167 | sw.Stop(); 168 | Console.WriteLine(sw.ElapsedMilliseconds); 169 | } 170 | 171 | ///net5.0+ has Convert.FromHexString(string) 172 | public static byte[] Base16Decode_SampleImpl(string hex) 173 | { 174 | #if NET5_0_OR_GREATER 175 | return Convert.FromHexString(hex); 176 | #else 177 | // skip validations 178 | var bytes = new byte[hex.Length >> 1]; 179 | for (int i = 0; i < hex.Length; i += 2) 180 | { 181 | bytes[i >> 1] = Convert.ToByte(hex.Substring(i, 2), 16); 182 | } 183 | return bytes; 184 | #endif 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/Base32ExtensionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using NUnit.Framework; 4 | using rm.Extensions; 5 | 6 | namespace rm.ExtensionsTest; 7 | 8 | // See https://cryptii.com/pipes/crockford-base32 9 | public class Base32ExtensionTest 10 | { 11 | private const int iterations = 1_000_000; 12 | 13 | [Test] 14 | [TestCase("Man", "9NGPW")] 15 | [TestCase("Woman", "AXQPTRBE")] 16 | [TestCase("The quick brown fox jumps over the lazy dog.", "AHM6A83HENMP6TS0C9S6YXVE41K6YY10D9TPTW3K41QQCSBJ41T6GS90DHGQMY90CHQPEBG")] 17 | public void Base32Encode_01(string s, string base32) 18 | { 19 | Assert.AreEqual(base32, s.ToUtf8Bytes().Base32Encode()); 20 | } 21 | 22 | [Test] 23 | [TestCase("9NGPW", "Man")] 24 | [TestCase("AXQPTRBE", "Woman")] 25 | [TestCase("AHM6A83HENMP6TS0C9S6YXVE41K6YY10D9TPTW3K41QQCSBJ41T6GS90DHGQMY90CHQPEBG", "The quick brown fox jumps over the lazy dog.")] 26 | public void Base32Decode_01(string base32, string s) 27 | { 28 | Assert.AreEqual(s, base32.Base32Decode().ToUtf8String()); 29 | } 30 | 31 | [Test] 32 | [TestCase("ahm6a83henmp6ts0c9s6yxve41k6yy10d9tptw3k41qqcsbj41t6gs90dhgqmy90chqpebg", "The quick brown fox jumps over the lazy dog.")] // lowercase 33 | [TestCase("ahm6a83henmp6tsOc9s6yxve41k6yy1Od9tptw3k41qqcsbj41t6gs9Odhgqmy9Ochqpebg", "The quick brown fox jumps over the lazy dog.")] // O 34 | [TestCase("ahm6a83henmp6tsoc9s6yxve41k6yy1od9tptw3k41qqcsbj41t6gs9odhgqmy9ochqpebg", "The quick brown fox jumps over the lazy dog.")] // o 35 | [TestCase("ahm6a83henmp6ts0c9s6yxve4Ik6yyI0d9tptw3k4Iqqcsbj4It6gs90dhgqmy90chqpebg", "The quick brown fox jumps over the lazy dog.")] // I 36 | [TestCase("ahm6a83henmp6ts0c9s6yxve4ik6yyi0d9tptw3k4iqqcsbj4it6gs90dhgqmy90chqpebg", "The quick brown fox jumps over the lazy dog.")] // i 37 | [TestCase("ahm6a83henmp6ts0c9s6yxve4Lk6yyL0d9tptw3k4Lqqcsbj4Lt6gs90dhgqmy90chqpebg", "The quick brown fox jumps over the lazy dog.")] // L 38 | [TestCase("ahm6a83henmp6ts0c9s6yxve4lk6yyl0d9tptw3k4lqqcsbj4lt6gs90dhgqmy90chqpebg", "The quick brown fox jumps over the lazy dog.")] // l 39 | [TestCase("ahm6a83henmp6ts0c9s6yxve4lk6yyl0-d9tptw3k4lqqcsbj-4lt6gs90dhgqmy90chqpebg", "The quick brown fox jumps over the lazy dog.")] // - 40 | public void Base32Decode_Variations_01(string base32, string s) 41 | { 42 | Assert.AreEqual(s, base32.Base32Decode().ToUtf8String()); 43 | } 44 | 45 | [Test] 46 | [TestCase("base3")] 47 | [TestCase("base32")] 48 | [TestCase("base32!")] 49 | [TestCase("base32!!")] 50 | [TestCase("base32!!!")] 51 | [TestCase("base32!!!!")] 52 | [TestCase("base32!!!!!")] 53 | [TestCase("base32!!!!!!")] 54 | [TestCase("base32!!!!!!!")] 55 | public void Base32_Roundtrip_01(string s) 56 | { 57 | var base32 = s.ToUtf8Bytes().Base32Encode(); 58 | var roundtrip = base32.Base32Decode().ToUtf8String(); 59 | Assert.AreEqual(s, roundtrip); 60 | } 61 | 62 | [Test] 63 | [TestCase("FU")] 64 | [TestCase("F®")] 65 | public void Base32Decode_Invalid_02(string base32) 66 | { 67 | Assert.Throws(() => 68 | base32.Base32Decode().ToUtf8String()); 69 | } 70 | 71 | [Test] 72 | [TestCase("a", "")] 73 | [TestCase("ah", "T")] 74 | [TestCase("ahm", "T")] 75 | [TestCase("ahm6", "Th")] 76 | [TestCase("ahm6a", "The")] 77 | [TestCase("ahm6a8", "The")] 78 | [TestCase("ahm6a83", "The ")] 79 | [TestCase("ahm6a83h", "The q")] 80 | public void Base32Decode_Surplus_Chars_Do_Not_Throw_01(string base32, string s) 81 | { 82 | Assert.AreEqual(s, base32.Base32Decode().ToUtf8String()); 83 | } 84 | 85 | [Test] 86 | [TestCase("8843EB1845D94528A4E862E277A26629", "H11YP625V52JH978CBH7F8K654")] 87 | public void Base32Encode_Hex_01(string guid, string base32) 88 | { 89 | Assert.AreEqual(base32, guid.Base16Decode().Base32Encode()); 90 | } 91 | 92 | [Test] 93 | [TestCase("H11YP625V52JH978CBH7F8K654", "8843EB1845D94528A4E862E277A26629")] 94 | public void Base32Decode_Hex_01(string base32, string guid) 95 | { 96 | Assert.AreEqual(guid, base32.Base32Decode().Base16Encode()); 97 | } 98 | 99 | [Explicit] 100 | [Test] 101 | [Category("slow")] 102 | [TestCase("The quick brown fox jumps over the lazy dog.")] 103 | [TestCase("The quick brown fox jumps over the lazy dog. ")] 104 | [TestCase("The quick brown fox jumps over the lazy ")] 105 | public void Perf_Base32Encode(string s) 106 | { 107 | var bytes = s.ToUtf8Bytes(); 108 | var sw = Stopwatch.StartNew(); 109 | for (int i = 0; i < iterations; i++) 110 | { 111 | var base32 = bytes.Base32Encode(); 112 | } 113 | sw.Stop(); 114 | Console.WriteLine(sw.ElapsedMilliseconds); 115 | } 116 | 117 | [Explicit] 118 | [Test] 119 | [Category("slow")] 120 | [TestCase("ahm6a83henmp6ts0c9s6yxve41k6yy10d9tptw3k41qqcsbj41t6gs90dhgqmy90chqpebg")] 121 | [TestCase("ahm6a83henmp6ts0c9s6yxve41k6yy10d9tptw3k41qqcsbj41t6gs90dhgqmy90chqpebh0")] 122 | [TestCase("ahm6a83henmp6ts0c9s6yxve41k6yy10d9tptw3k41qqcsbj41t6gs90dhgqmy90")] 123 | [TestCase("ahm6a83henmp6ts0c9s6yxve4lk6yyl0-d9tptw3k4lqqcsbj-4lt6gs90dhgqmy90chqpebg")] 124 | public void Perf_Base32Decode(string base32) 125 | { 126 | var sw = Stopwatch.StartNew(); 127 | for (int i = 0; i < iterations; i++) 128 | { 129 | var bytes = base32.Base32Decode(); 130 | } 131 | sw.Stop(); 132 | Console.WriteLine(sw.ElapsedMilliseconds); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/Base64ExtensionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using NUnit.Framework; 4 | using rm.Extensions; 5 | 6 | namespace rm.ExtensionsTest; 7 | 8 | [TestFixture] 9 | public class Base64ExtensionTest 10 | { 11 | private const int iterations = 1_000_000; 12 | 13 | [Test] 14 | [TestCase("Man", "TWFu")] 15 | [TestCase("Woman", "V29tYW4=")] 16 | [TestCase("light work.", "bGlnaHQgd29yay4=")] 17 | [TestCase("light work", "bGlnaHQgd29yaw==")] 18 | public void Base64Encode_01(string s, string base64) 19 | { 20 | Assert.AreEqual(base64, s.ToUtf8Bytes().Base64Encode()); 21 | } 22 | 23 | [Test] 24 | [TestCase("TWFu", "Man")] 25 | [TestCase("V29tYW4=", "Woman")] 26 | [TestCase("bGlnaHQgd29yay4=", "light work.")] 27 | [TestCase("bGlnaHQgd29yaw==", "light work")] 28 | public void Base64Decode_01(string base64, string s) 29 | { 30 | Assert.AreEqual(s, base64.Base64Decode().ToUtf8String()); 31 | } 32 | 33 | [Test] 34 | [TestCase("Man", "TWFu")] 35 | [TestCase("Woman", "V29tYW4")] 36 | [TestCase("light work.", "bGlnaHQgd29yay4")] 37 | [TestCase("light work", "bGlnaHQgd29yaw")] 38 | public void Base64UrlEncode_01(string s, string base64Url) 39 | { 40 | Assert.AreEqual(base64Url, s.ToUtf8Bytes().Base64UrlEncode()); 41 | } 42 | 43 | [Test] 44 | [TestCase("TWFu", "Man")] 45 | [TestCase("V29tYW4", "Woman")] 46 | [TestCase("bGlnaHQgd29yay4", "light work.")] 47 | [TestCase("bGlnaHQgd29yaw", "light work")] 48 | public void Base64UrlDecode_01(string base64Url, string s) 49 | { 50 | Assert.AreEqual(s, base64Url.Base64UrlDecode().ToUtf8String()); 51 | } 52 | 53 | [Explicit] 54 | [Test] 55 | [Category("slow")] 56 | public void Perf_Base64Encode() 57 | { 58 | var bytes = "The quick brown fox jumps over the lazy dog.".ToUtf8Bytes(); 59 | var sw = Stopwatch.StartNew(); 60 | for (int i = 0; i < iterations; i++) 61 | { 62 | var base64 = bytes.Base64Encode(); 63 | } 64 | sw.Stop(); 65 | Console.WriteLine(sw.ElapsedMilliseconds); 66 | } 67 | 68 | [Explicit] 69 | [Test] 70 | [Category("slow")] 71 | public void Perf_Base64Decode() 72 | { 73 | var base64 = "ahm6a83henmp6ts0c9s6yxve41k6yy10d9tptw3k41qqcsbj41t6gs90dhgqmy90chqpebg="; 74 | var sw = Stopwatch.StartNew(); 75 | for (int i = 0; i < iterations; i++) 76 | { 77 | var bytes = base64.Base64Decode(); 78 | } 79 | sw.Stop(); 80 | Console.WriteLine(sw.ElapsedMilliseconds); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/BitSetTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using rm.Extensions; 4 | 5 | namespace rm.ExtensionsTest; 6 | 7 | [TestFixture] 8 | public class BitSetTest 9 | { 10 | [Test] 11 | public void Add01() 12 | { 13 | var bitset = new BitSet(1023); 14 | 15 | Assert.IsFalse(bitset.Has(250)); 16 | bitset.Add(250); 17 | Assert.IsTrue(bitset.Has(250)); 18 | 19 | Assert.IsFalse(bitset.Has(0)); 20 | bitset.Add(0); 21 | Assert.IsTrue(bitset.Has(0)); 22 | 23 | Assert.IsFalse(bitset.Has(1023)); 24 | bitset.Add(1023); 25 | Assert.IsTrue(bitset.Has(1023)); 26 | 27 | bitset.Add(250); 28 | Assert.IsTrue(bitset.Has(250)); 29 | } 30 | 31 | [Test] 32 | public void Add01u() 33 | { 34 | var bitset = new BitSet((uint)1023); 35 | 36 | Assert.IsFalse(bitset.Has((uint)250)); 37 | bitset.Add((uint)250); 38 | Assert.IsTrue(bitset.Has((uint)250)); 39 | 40 | Assert.IsFalse(bitset.Has((uint)0)); 41 | bitset.Add((uint)0); 42 | Assert.IsTrue(bitset.Has((uint)0)); 43 | 44 | Assert.IsFalse(bitset.Has((uint)1023)); 45 | bitset.Add((uint)1023); 46 | Assert.IsTrue(bitset.Has((uint)1023)); 47 | 48 | bitset.Add((uint)250); 49 | Assert.IsTrue(bitset.Has((uint)250)); 50 | } 51 | 52 | [Test] 53 | public void Remove01() 54 | { 55 | var bitset = new BitSet(2000); 56 | 57 | bitset.Remove(750); 58 | Assert.IsFalse(bitset.Has(750)); 59 | bitset.Add(750); 60 | Assert.IsTrue(bitset.Has(750)); 61 | bitset.Remove(750); 62 | Assert.IsFalse(bitset.Has(750)); 63 | 64 | bitset.Remove(0); 65 | Assert.IsFalse(bitset.Has(0)); 66 | bitset.Add(0); 67 | Assert.IsTrue(bitset.Has(0)); 68 | bitset.Remove(0); 69 | Assert.IsFalse(bitset.Has(0)); 70 | 71 | bitset.Remove(1023); 72 | Assert.IsFalse(bitset.Has(1023)); 73 | bitset.Add(1023); 74 | Assert.IsTrue(bitset.Has(1023)); 75 | bitset.Remove(1023); 76 | Assert.IsFalse(bitset.Has(1023)); 77 | 78 | bitset.Remove(750); 79 | Assert.IsFalse(bitset.Has(750)); 80 | } 81 | 82 | [Test] 83 | public void Remove01u() 84 | { 85 | var bitset = new BitSet((uint)2000); 86 | 87 | bitset.Remove((uint)750); 88 | Assert.IsFalse(bitset.Has((uint)750)); 89 | bitset.Add((uint)750); 90 | Assert.IsTrue(bitset.Has((uint)750)); 91 | bitset.Remove((uint)750); 92 | Assert.IsFalse(bitset.Has((uint)750)); 93 | 94 | bitset.Remove((uint)0); 95 | Assert.IsFalse(bitset.Has((uint)0)); 96 | bitset.Add((uint)0); 97 | Assert.IsTrue(bitset.Has((uint)0)); 98 | bitset.Remove((uint)0); 99 | Assert.IsFalse(bitset.Has((uint)0)); 100 | 101 | bitset.Remove((uint)1023); 102 | Assert.IsFalse(bitset.Has((uint)1023)); 103 | bitset.Add((uint)1023); 104 | Assert.IsTrue(bitset.Has((uint)1023)); 105 | bitset.Remove((uint)1023); 106 | Assert.IsFalse(bitset.Has((uint)1023)); 107 | 108 | bitset.Remove((uint)750); 109 | Assert.IsFalse(bitset.Has((uint)750)); 110 | } 111 | 112 | [Test] 113 | public void Toggle01() 114 | { 115 | var bitset = new BitSet(1500); 116 | 117 | Assert.IsFalse(bitset.Has(500)); 118 | bitset.Toggle(500); 119 | Assert.IsTrue(bitset.Has(500)); 120 | bitset.Toggle(500); 121 | Assert.IsFalse(bitset.Has(500)); 122 | 123 | Assert.IsFalse(bitset.Has(0)); 124 | bitset.Toggle(0); 125 | Assert.IsTrue(bitset.Has(0)); 126 | bitset.Toggle(0); 127 | Assert.IsFalse(bitset.Has(0)); 128 | 129 | Assert.IsFalse(bitset.Has(1023)); 130 | bitset.Toggle(1023); 131 | Assert.IsTrue(bitset.Has(1023)); 132 | bitset.Toggle(1023); 133 | Assert.IsFalse(bitset.Has(1023)); 134 | } 135 | 136 | [Test] 137 | public void Toggle01u() 138 | { 139 | var bitset = new BitSet((uint)1500); 140 | 141 | Assert.IsFalse(bitset.Has((uint)500)); 142 | bitset.Toggle((uint)500); 143 | Assert.IsTrue(bitset.Has((uint)500)); 144 | bitset.Toggle((uint)500); 145 | Assert.IsFalse(bitset.Has((uint)500)); 146 | 147 | Assert.IsFalse(bitset.Has((uint)0)); 148 | bitset.Toggle((uint)0); 149 | Assert.IsTrue(bitset.Has((uint)0)); 150 | bitset.Toggle((uint)0); 151 | Assert.IsFalse(bitset.Has((uint)0)); 152 | 153 | Assert.IsFalse(bitset.Has((uint)1023)); 154 | bitset.Toggle((uint)1023); 155 | Assert.IsTrue(bitset.Has((uint)1023)); 156 | bitset.Toggle((uint)1023); 157 | Assert.IsFalse(bitset.Has((uint)1023)); 158 | } 159 | 160 | [Test] 161 | public void IEnumerable01() 162 | { 163 | var bitset = new BitSet(255); 164 | var inputs = new[] { 0, 30, 31, 32, 33, 100, 200, 255 }; 165 | foreach (var item in inputs) 166 | { 167 | bitset.Add(item); 168 | } 169 | var size = 0; 170 | foreach (int item in bitset) 171 | { 172 | size++; 173 | } 174 | Assert.AreEqual(inputs.Length, size); 175 | } 176 | 177 | [Test] 178 | public void IEnumerable01u() 179 | { 180 | var bitset = new BitSet((uint)255); 181 | var inputs = new uint[] { 0, 30, 31, 32, 33, 100, 200, 255 }; 182 | foreach (var item in inputs) 183 | { 184 | bitset.Add(item); 185 | } 186 | var size = 0; 187 | foreach (var item in bitset) 188 | { 189 | size++; 190 | } 191 | Assert.AreEqual(inputs.Length, size); 192 | } 193 | 194 | [Test] 195 | public void Ctor01() 196 | { 197 | Assert.DoesNotThrow(() => { var bitset = new BitSet(0); }); 198 | Assert.Throws(() => { var bitset = new BitSet(-1); }); 199 | } 200 | 201 | [Test] 202 | public void Ctor01u() 203 | { 204 | Assert.DoesNotThrow(() => { var bitset = new BitSet((uint)0); }); 205 | } 206 | 207 | [Test] 208 | public void Count01() 209 | { 210 | var bitset = new BitSet(15); 211 | var inputs = new[] { 0, 1, 2, 3, 4 }; 212 | foreach (var item in inputs) 213 | { 214 | bitset.Add(item); 215 | bitset.Add(item); 216 | } 217 | Assert.AreEqual(bitset.Count, 5); 218 | for (int i = 0; i < 2; i++) 219 | { 220 | bitset.Remove(inputs[i]); 221 | bitset.Remove(inputs[i]); 222 | } 223 | Assert.AreEqual(bitset.Count, 3); 224 | foreach (var item in inputs) 225 | { 226 | bitset.Toggle(item); 227 | } 228 | Assert.AreEqual(bitset.Count, 2); 229 | } 230 | 231 | [Test] 232 | public void Count01u() 233 | { 234 | var bitset = new BitSet(15); 235 | var inputs = new uint[] { 0, 1, 2, 3, 4 }; 236 | foreach (var item in inputs) 237 | { 238 | bitset.Add(item); 239 | bitset.Add(item); 240 | } 241 | Assert.AreEqual(bitset.Count, 5); 242 | for (int i = 0; i < 2; i++) 243 | { 244 | bitset.Remove(inputs[i]); 245 | bitset.Remove(inputs[i]); 246 | } 247 | Assert.AreEqual(bitset.Count, 3); 248 | foreach (var item in inputs) 249 | { 250 | bitset.Toggle(item); 251 | } 252 | Assert.AreEqual(bitset.Count, 2); 253 | } 254 | 255 | [Test] 256 | public void Clear01() 257 | { 258 | var bitset = new BitSet(20); 259 | foreach (var item in new[] { 0, 1, 2, 3, 4 }) 260 | { 261 | bitset.Add(item); 262 | } 263 | Assert.AreNotEqual(0, bitset.Count); 264 | bitset.Clear(); 265 | Assert.AreEqual(0, bitset.Count); 266 | } 267 | 268 | [Test] 269 | public void FullCapacity01() 270 | { 271 | int max = 50; 272 | var bitset = new BitSet(max); 273 | for (int i = 0; i <= max; i++) 274 | { 275 | bitset.Add(i); 276 | } 277 | Assert.AreEqual(max + 1, bitset.Count); 278 | for (int i = 0; i <= max; i++) 279 | { 280 | Assert.IsTrue(bitset.Has(i)); 281 | } 282 | } 283 | 284 | [Test] 285 | public void FullCapacity01u() 286 | { 287 | uint max = 50; 288 | var bitset = new BitSet(max); 289 | for (uint i = 0; i <= max; i++) 290 | { 291 | bitset.Add(i); 292 | } 293 | Assert.AreEqual(max + 1, bitset.Count); 294 | for (uint i = 0; i <= max; i++) 295 | { 296 | Assert.IsTrue(bitset.Has(i)); 297 | } 298 | } 299 | 300 | [Test] 301 | public void Capacity01() 302 | { 303 | Assert.AreEqual(1, new BitSet(30).flags.Length); 304 | Assert.AreEqual(1, new BitSet(31).flags.Length); 305 | Assert.AreEqual(2, new BitSet(32).flags.Length); 306 | Assert.AreEqual(2, new BitSet(62).flags.Length); 307 | Assert.AreEqual(2, new BitSet(63).flags.Length); 308 | Assert.AreEqual(3, new BitSet(64).flags.Length); 309 | } 310 | 311 | [Test] 312 | public void Capacity01u() 313 | { 314 | Assert.AreEqual(1, new BitSet((uint)30).flags.Length); 315 | Assert.AreEqual(1, new BitSet((uint)31).flags.Length); 316 | Assert.AreEqual(2, new BitSet((uint)32).flags.Length); 317 | Assert.AreEqual(2, new BitSet((uint)62).flags.Length); 318 | Assert.AreEqual(2, new BitSet((uint)63).flags.Length); 319 | Assert.AreEqual(3, new BitSet((uint)64).flags.Length); 320 | } 321 | 322 | [Test] 323 | public void Capacity02() 324 | { 325 | Assert.AreEqual((int.MaxValue / 32) + 1, new BitSet(int.MaxValue).flags.Length); 326 | } 327 | 328 | [Test] 329 | public void Capacity02u() 330 | { 331 | Assert.AreEqual((uint.MaxValue / 32) + 1, new BitSet(uint.MaxValue).flags.Length); 332 | } 333 | 334 | [Test] 335 | public void OutOfRange01() 336 | { 337 | var bitset = new BitSet(1023); 338 | Assert.Throws(() => bitset.Has(-1)); 339 | Assert.Throws(() => bitset.Has(1024)); 340 | Assert.Throws(() => bitset.Add(-1)); 341 | Assert.Throws(() => bitset.Add(1024)); 342 | Assert.Throws(() => bitset.Remove(-1)); 343 | Assert.Throws(() => bitset.Remove(1024)); 344 | Assert.Throws(() => bitset.Toggle(-1)); 345 | Assert.Throws(() => bitset.Toggle(1024)); 346 | } 347 | 348 | [Test] 349 | public void OutOfRange01u() 350 | { 351 | var bitset = new BitSet(1023); 352 | Assert.Throws(() => bitset.Has((uint)1024)); 353 | Assert.Throws(() => bitset.Add((uint)1024)); 354 | Assert.Throws(() => bitset.Remove((uint)1024)); 355 | Assert.Throws(() => bitset.Toggle((uint)1024)); 356 | } 357 | } 358 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/CircularQueueTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using rm.Extensions; 4 | 5 | namespace rm.ExtensionsTest; 6 | 7 | [TestFixture] 8 | public class CircularQueueTest 9 | { 10 | [Test] 11 | public void Enqueue01() 12 | { 13 | var capacity = 4; 14 | var cq = new CircularQueue(capacity); 15 | for (int i = 0; i < 6; i++) 16 | { cq.Enqueue(i); } 17 | Assert.AreEqual(capacity, cq.Count()); 18 | } 19 | 20 | [Test] 21 | public void Dequeue01() 22 | { 23 | var capacity = 4; 24 | var cq = new CircularQueue(capacity); 25 | for (int i = 0; i < capacity; i++) 26 | { cq.Enqueue(i); } 27 | for (int i = 0; i < capacity; i++) 28 | { 29 | Assert.AreEqual(i, cq.Dequeue()); 30 | Assert.AreEqual(capacity - (i + 1), cq.Count()); 31 | } 32 | Assert.Throws(() => cq.Dequeue()); 33 | } 34 | 35 | [Test] 36 | public void EnqueueDequeue01() 37 | { 38 | var capacity = 4; 39 | var cq = new CircularQueue(capacity); 40 | cq.Enqueue(0); 41 | cq.Enqueue(1); 42 | cq.Enqueue(2); 43 | cq.Enqueue(3); 44 | cq.Dequeue(); 45 | cq.Dequeue(); 46 | Assert.AreEqual(2, cq.Peek()); 47 | Assert.AreEqual(3, cq.PeekTail()); 48 | cq.Enqueue(4); 49 | cq.Enqueue(5); 50 | Assert.AreEqual(2, cq.Peek()); 51 | Assert.AreEqual(5, cq.PeekTail()); 52 | cq.Enqueue(6); 53 | cq.Enqueue(7); 54 | Assert.AreEqual(4, cq.Peek()); 55 | Assert.AreEqual(7, cq.PeekTail()); 56 | } 57 | 58 | [Test] 59 | public void PeekPeekTail01() 60 | { 61 | var capacity = 4; 62 | var cq = new CircularQueue(capacity); 63 | for (int i = 0; i < 6; i++) 64 | { cq.Enqueue(i); } 65 | Assert.AreEqual(capacity, cq.Count()); 66 | Assert.AreEqual(2, cq.Peek()); 67 | Assert.AreEqual(5, cq.PeekTail()); 68 | } 69 | 70 | [Test] 71 | public void IsEmpty01() 72 | { 73 | var cq = new CircularQueue(1); 74 | Assert.IsTrue(cq.IsEmpty()); 75 | cq.Enqueue(1); 76 | Assert.IsFalse(cq.IsEmpty()); 77 | cq.Dequeue(); 78 | Assert.IsTrue(cq.IsEmpty()); 79 | } 80 | 81 | [Test] 82 | public void Count01() 83 | { 84 | var cq = new CircularQueue(4); 85 | Assert.AreEqual(0, cq.Count()); 86 | cq.Enqueue(1); 87 | cq.Enqueue(1); 88 | Assert.AreEqual(2, cq.Count()); 89 | cq.Dequeue(); 90 | Assert.AreEqual(1, cq.Count()); 91 | cq.Dequeue(); 92 | Assert.AreEqual(0, cq.Count()); 93 | } 94 | 95 | [Test] 96 | public void Capacity01() 97 | { 98 | var capacity = 4; 99 | var cq = new CircularQueue(capacity); 100 | Assert.AreEqual(capacity, cq.Capacity()); 101 | for (int i = 0; i < 6; i++) 102 | { cq.Enqueue(i); } 103 | Assert.AreEqual(capacity, cq.Capacity()); 104 | } 105 | 106 | [Test] 107 | public void Clear01() 108 | { 109 | var capacity = 4; 110 | var cq = new CircularQueue(capacity); 111 | Assert.AreEqual(capacity, cq.Capacity()); 112 | for (int i = 0; i < 6; i++) 113 | { cq.Enqueue(i); } 114 | Assert.AreEqual(capacity, cq.Count()); 115 | cq.Clear(); 116 | Assert.AreEqual(0, cq.Count()); 117 | for (int i = 0; i < 6; i++) 118 | { cq.Enqueue(i); } 119 | Assert.AreEqual(capacity, cq.Count()); 120 | } 121 | 122 | [Test] 123 | public void ArgumentOutOfRange01() 124 | { 125 | Assert.Throws(() => new CircularQueue(0)); 126 | Assert.Throws(() => new CircularQueue(-1)); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/CircularStackTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using rm.Extensions; 4 | 5 | namespace rm.ExtensionsTest; 6 | 7 | [TestFixture] 8 | public class CircularStackTest 9 | { 10 | [Test] 11 | public void Push01() 12 | { 13 | var capacity = 4; 14 | var cq = new CircularStack(capacity); 15 | for (int i = 0; i < 6; i++) 16 | { cq.Push(i); } 17 | Assert.AreEqual(capacity, cq.Count()); 18 | } 19 | 20 | [Test] 21 | public void Pop01() 22 | { 23 | var capacity = 4; 24 | var cq = new CircularStack(capacity); 25 | for (int i = 0; i < capacity; i++) 26 | { cq.Push(i); } 27 | for (int i = 0; i < capacity; i++) 28 | { 29 | Assert.AreEqual(capacity - (i + 1), cq.Pop()); 30 | Assert.AreEqual(capacity - (i + 1), cq.Count()); 31 | } 32 | Assert.Throws(() => cq.Pop()); 33 | } 34 | 35 | [Test] 36 | public void PushPop01() 37 | { 38 | var capacity = 4; 39 | var cq = new CircularStack(capacity); 40 | cq.Push(0); 41 | cq.Push(1); 42 | cq.Push(2); 43 | cq.Push(3); 44 | cq.Pop(); 45 | cq.Pop(); 46 | Assert.AreEqual(1, cq.Peek()); 47 | Assert.AreEqual(0, cq.PeekBottom()); 48 | cq.Push(4); 49 | cq.Push(5); 50 | Assert.AreEqual(5, cq.Peek()); 51 | Assert.AreEqual(0, cq.PeekBottom()); 52 | cq.Push(6); 53 | cq.Push(7); 54 | Assert.AreEqual(7, cq.Peek()); 55 | Assert.AreEqual(4, cq.PeekBottom()); 56 | } 57 | 58 | [Test] 59 | public void PeekPeekBottom01() 60 | { 61 | var capacity = 4; 62 | var cq = new CircularStack(capacity); 63 | for (int i = 0; i < 6; i++) 64 | { cq.Push(i); } 65 | Assert.AreEqual(capacity, cq.Count()); 66 | Assert.AreEqual(5, cq.Peek()); 67 | Assert.AreEqual(2, cq.PeekBottom()); 68 | } 69 | 70 | [Test] 71 | public void IsEmpty01() 72 | { 73 | var cq = new CircularStack(1); 74 | Assert.IsTrue(cq.IsEmpty()); 75 | cq.Push(1); 76 | Assert.IsFalse(cq.IsEmpty()); 77 | cq.Pop(); 78 | Assert.IsTrue(cq.IsEmpty()); 79 | } 80 | 81 | [Test] 82 | public void Count01() 83 | { 84 | var cq = new CircularStack(4); 85 | Assert.AreEqual(0, cq.Count()); 86 | cq.Push(1); 87 | cq.Push(1); 88 | Assert.AreEqual(2, cq.Count()); 89 | cq.Pop(); 90 | Assert.AreEqual(1, cq.Count()); 91 | cq.Pop(); 92 | Assert.AreEqual(0, cq.Count()); 93 | } 94 | 95 | [Test] 96 | public void Capacity01() 97 | { 98 | var capacity = 4; 99 | var cq = new CircularStack(capacity); 100 | Assert.AreEqual(capacity, cq.Capacity()); 101 | for (int i = 0; i < 6; i++) 102 | { cq.Push(i); } 103 | Assert.AreEqual(capacity, cq.Capacity()); 104 | } 105 | 106 | [Test] 107 | public void Clear01() 108 | { 109 | var capacity = 4; 110 | var cq = new CircularStack(capacity); 111 | Assert.AreEqual(capacity, cq.Capacity()); 112 | for (int i = 0; i < 6; i++) 113 | { cq.Push(i); } 114 | Assert.AreEqual(capacity, cq.Count()); 115 | cq.Clear(); 116 | Assert.AreEqual(0, cq.Count()); 117 | for (int i = 0; i < 6; i++) 118 | { cq.Push(i); } 119 | Assert.AreEqual(capacity, cq.Count()); 120 | } 121 | 122 | [Test] 123 | public void ArgumentOutOfRange01() 124 | { 125 | Assert.Throws(() => new CircularStack(0)); 126 | Assert.Throws(() => new CircularStack(-1)); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/DateTimeExtensionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using NUnit.Framework; 4 | using rm.Extensions; 5 | 6 | namespace rm.ExtensionsTest; 7 | 8 | [TestFixture] 9 | public class DateTimeExtensionTest 10 | { 11 | [Test] 12 | public void ToUtcFormatString01() 13 | { 14 | var date = new DateTime(1994, 11, 05, 13, 15, 30, DateTimeKind.Utc); 15 | Assert.AreEqual("1994-11-05T13:15:30.000Z", date.ToUtcFormatString()); 16 | } 17 | 18 | [Test] 19 | public void ToSqlDateTimeMinUtc01() 20 | { 21 | var date = new DateTime().ToSqlDateTimeMinUtc(); 22 | var expectedDate = new DateTime(1753, 1, 1, 0, 0, 0, DateTimeKind.Utc); 23 | Assert.AreEqual(expectedDate, date); 24 | Assert.AreEqual(expectedDate.Kind, date.Kind); 25 | } 26 | 27 | [Test] 28 | public void AsUtcKind01() 29 | { 30 | var date = DateTime.ParseExact( 31 | "4/1/2014 12:00:00 AM", "M/d/yyyy hh:mm:ss tt", CultureInfo.CurrentCulture); 32 | Assert.AreEqual(DateTimeKind.Unspecified, date.Kind); 33 | date = date.AsUtcKind(); 34 | Assert.AreEqual(DateTimeKind.Utc, date.Kind); 35 | var expectedDate = new DateTime(2014, 4, 1, 0, 0, 0, DateTimeKind.Utc); 36 | Assert.AreEqual(expectedDate, date); 37 | Assert.AreEqual(expectedDate.Kind, date.Kind); 38 | Assert.AreEqual(expectedDate, date.ToUniversalTime()); 39 | Assert.AreEqual(expectedDate.Kind, date.ToUniversalTime().Kind); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/DecimalExtensionTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using rm.Extensions; 3 | 4 | namespace rm.ExtensionsTest; 5 | 6 | [TestFixture] 7 | public class DecimalExtensionTest 8 | { 9 | [Test] 10 | public void TruncateTo01() 11 | { 12 | Assert.AreEqual(12.34m, 12.349m.TruncateTo(2)); 13 | Assert.AreEqual(12.33m, 12.339m.TruncateTo(2)); 14 | Assert.AreEqual(12m, 12.999m.TruncateTo(0)); 15 | Assert.AreEqual(11m, 11.999m.TruncateTo(0)); 16 | 17 | Assert.AreEqual(-12.34m, (-12.349m).TruncateTo(2)); 18 | Assert.AreEqual(-12.33m, (-12.339m).TruncateTo(2)); 19 | Assert.AreEqual(-12m, (-12.999m).TruncateTo(0)); 20 | Assert.AreEqual(-11m, (-11.999m).TruncateTo(0)); 21 | 22 | Assert.AreEqual(12.1234567890123456789012345678m, 12.1234567890123456789012345678m.TruncateTo(28)); 23 | Assert.AreEqual(-12.1234567890123456789012345678m, (-12.1234567890123456789012345678m).TruncateTo(28)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/DictionaryExtensionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | using rm.Extensions; 5 | 6 | namespace rm.ExtensionsTest; 7 | 8 | [TestFixture] 9 | public class DictionaryExtensionTest 10 | { 11 | [Test] 12 | [TestCase(new[] { 1, 2 }, 3, 0)] 13 | [TestCase(new[] { 1, 2 }, 1, 1)] 14 | [TestCase(new[] { 0, 1, 2 }, 0, 0)] 15 | public void GetValueOrDefault01(int[] a, int key, int expected) 16 | { 17 | var dictionary = a.ToDictionary(x => x); 18 | Assert.AreEqual(expected, dictionary.GetValueOrDefault(key)); 19 | } 20 | 21 | [Test] 22 | [TestCase(new[] { 1, 2 }, 3, null)] 23 | [TestCase(new[] { 1, 2 }, 1, "1")] 24 | [TestCase(new[] { 0, 1, 2 }, 0, "0")] 25 | public void GetValueOrDefault02(int[] a, int key, string expected) 26 | { 27 | var dictionary = a.ToDictionary(x => x, y => y.ToString()); 28 | Assert.AreEqual(expected, dictionary.GetValueOrDefault(key)); 29 | } 30 | 31 | [Test] 32 | public void AsReadOnly01() 33 | { 34 | var dictionary = new[] { 0, 1, 2 }.ToDictionary(x => x, y => y.ToString()).AsReadOnly(); 35 | Assert.Throws(() => dictionary[5] = "5"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/EnumExtensionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using NUnit.Framework; 5 | using rm.Extensions; 6 | using Sc = System.ComponentModel; 7 | 8 | namespace rm.ExtensionsTest; 9 | 10 | public enum Temperature 11 | { 12 | C = 1, 13 | F 14 | } 15 | 16 | public enum Color 17 | { 18 | [Sc.Description("Red color")] 19 | Red = 1, 20 | Green, 21 | [Sc.Description("Blue color")] 22 | Blue, 23 | [Sc.Description("")] 24 | Black, 25 | [Sc.Description()] 26 | White, 27 | } 28 | 29 | public enum Grade 30 | { 31 | Toddler = 1, 32 | [Sc.Description("Pre-K")] 33 | PreK, 34 | Kindergarten, 35 | [Sc.Description("1")] 36 | One, 37 | [Sc.Description("2")] 38 | Two, 39 | [Sc.Description("3")] 40 | Three, 41 | [Sc.Description("4")] 42 | Four, 43 | [Sc.Description("5")] 44 | Five, 45 | [Sc.Description("6")] 46 | Six, 47 | [Sc.Description("7")] 48 | Seven, 49 | [Sc.Description("8")] 50 | Eight, 51 | [Sc.Description("9")] 52 | Nine, 53 | [Sc.Description("10")] 54 | Ten, 55 | [Sc.Description("11")] 56 | Eleven, 57 | [Sc.Description("12")] 58 | Twelve, 59 | College 60 | } 61 | 62 | public enum EmptyEnum { } 63 | 64 | [TestFixture] 65 | public class EnumExtensionTest 66 | { 67 | [Test] 68 | public void Parse01() 69 | { 70 | Assert.AreEqual(Color.Red, "Red".Parse()); 71 | } 72 | 73 | [Test] 74 | public void Parse02() 75 | { 76 | Assert.Throws(() => { "Red".Parse(); }); 77 | } 78 | 79 | [Test] 80 | public void TryParse01() 81 | { 82 | Color color; 83 | Assert.IsTrue("Red".TryParse(out color)); 84 | Assert.AreEqual(Color.Red, color); 85 | } 86 | 87 | [Test] 88 | public void TryParse02() 89 | { 90 | Temperature t; 91 | Assert.IsFalse("Red".TryParse(out t)); 92 | Assert.AreNotEqual(Color.Red, t); 93 | Assert.AreEqual(0, (int)t); 94 | } 95 | 96 | [Test] 97 | public void GetDescription01() 98 | { 99 | Assert.AreEqual("Red color", Color.Red.GetDescription()); 100 | Assert.AreEqual(Color.Green.ToString(), Color.Green.GetDescription()); 101 | Assert.AreEqual(Color.Black.ToString(), Color.Black.GetDescription()); 102 | Assert.AreEqual(Color.White.ToString(), Color.White.GetDescription()); 103 | } 104 | 105 | [Test] 106 | public void UnsupportedEnumValueException01() 107 | { 108 | var enumValue = (Color)0; 109 | var TEnum = enumValue.GetType(); 110 | var ex = Assert.Throws>(() => 111 | { 112 | enumValue.GetEnumName(); 113 | }); 114 | Assert.AreEqual($"Value {enumValue} of enum {TEnum.Name} is not supported.", ex.Message); 115 | } 116 | 117 | [Test] 118 | public void GetDescription02() 119 | { 120 | Assert.Throws>(() => 121 | { 122 | ((Color)0).GetDescription(); 123 | }); 124 | } 125 | 126 | [Test] 127 | public void GetEnumValue01() 128 | { 129 | Assert.AreEqual(Color.Red, "Red".GetEnumValue()); 130 | Assert.Throws>(() => 131 | { 132 | "UnsupportedEnumValue".GetEnumValue(); 133 | }); 134 | } 135 | 136 | [Test] 137 | public void GetEnumValues01() 138 | { 139 | var colors = EnumExtension.GetEnumValues(); 140 | Assert.AreEqual(5, colors.Count()); 141 | Assert.IsTrue(colors.Contains(Color.Red)); 142 | Assert.IsTrue(colors.Contains(Color.Green)); 143 | Assert.IsTrue(colors.Contains(Color.Blue)); 144 | } 145 | 146 | [Test(Description = "GetEnumValue() v/s Enum.ToString() speed test.")] 147 | [Category("slow")] 148 | public void GetEnumValue02() 149 | { 150 | Action, string> speedTest = (testPredicate, testName) => 151 | { 152 | var iterations = 1000000; 153 | DateTime datetime; 154 | TimeSpan timespan; 155 | datetime = DateTime.Now; 156 | foreach (var item in Enumerable.Range(0, iterations)) 157 | { 158 | if (testPredicate()) 159 | { } 160 | } 161 | timespan = DateTime.Now - datetime; 162 | Console.WriteLine("{0}: \t{1}", testName, timespan.TotalSeconds); 163 | }; 164 | // 15x slow 165 | speedTest(() => { return Color.Red.ToString() == "Red"; }, "Enum.ToString()"); 166 | // 10x slow 167 | speedTest(() => { return "Red".Parse() == Color.Red; }, "Enum.Parse()"); 168 | // 1.5x slow 169 | speedTest(() => { return "Red".GetEnumValue() == Color.Red; }, "GetEnumValue()"); 170 | // fastest 171 | speedTest(() => { return Color.Red.GetEnumName() == "Red"; }, "GetEnumName()"); 172 | } 173 | 174 | [Test] 175 | public void GetEnumNames01() 176 | { 177 | var colors = EnumExtension.GetEnumNames(); 178 | Assert.AreEqual(5, colors.Count()); 179 | Assert.IsTrue(colors.Contains(Color.Red.ToString())); 180 | Assert.IsTrue(colors.Contains(Color.Green.ToString())); 181 | Assert.IsTrue(colors.Contains(Color.Blue.ToString())); 182 | } 183 | 184 | [Test] 185 | public void GetEnumNameToDescriptionMap01() 186 | { 187 | var colorsMap = EnumExtension.GetEnumNameToDescriptionMap(); 188 | Assert.AreEqual(5, colorsMap.Count()); 189 | Assert.IsTrue(colorsMap.Contains( 190 | new KeyValuePair(Color.Red.ToString(), Color.Red.GetDescription()) 191 | )); 192 | Assert.IsTrue(colorsMap.Contains( 193 | new KeyValuePair(Color.Green.ToString(), Color.Green.GetDescription()) 194 | )); 195 | Assert.IsTrue(colorsMap.Contains( 196 | new KeyValuePair(Color.Blue.ToString(), Color.Blue.GetDescription()) 197 | )); 198 | } 199 | 200 | [Test] 201 | public void GetEnumNameFromDescription01() 202 | { 203 | Assert.AreEqual(Color.Red.ToString(), "Red color".GetEnumNameFromDescription()); 204 | Assert.Throws>(() => 205 | { 206 | "UnsupportedEnumValue".GetEnumNameFromDescription(); 207 | }); 208 | } 209 | 210 | [Test] 211 | public void Sorting01() 212 | { 213 | var gradesUnsorted = new[] { "Pre-K", "1", "College", "2", "Toddler" }; 214 | var grades = gradesUnsorted 215 | .Select(x => x.GetEnumValueFromDescription()).ToArray(); 216 | Array.Sort(grades); 217 | Console.WriteLine("orig: {0}", string.Join(", ", gradesUnsorted)); 218 | var gradesSorted = grades.Select(x => x.GetDescription()); 219 | var gradesSortedFlat = string.Join(", ", gradesSorted); 220 | Assert.AreEqual("Toddler, Pre-K, 1, 2, College", gradesSortedFlat); 221 | Console.WriteLine("sort: {0}", gradesSortedFlat); 222 | } 223 | 224 | [Test] 225 | public void IsDefined01() 226 | { 227 | Assert.IsFalse(0.IsDefined()); 228 | Assert.IsFalse(100.IsDefined()); 229 | Assert.IsTrue(((int)Color.Red).IsDefined()); 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/GraphExtensionTest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using NUnit.Framework; 3 | using rm.Extensions; 4 | 5 | namespace rm.ExtensionsTest; 6 | 7 | [TestFixture] 8 | public class GraphExtensionTest 9 | { 10 | [Test(Description = " 1->2->3 ")] 11 | public void IsCyclic_False_01() 12 | { 13 | var n1 = new GraphNode("1"); 14 | var n2 = new GraphNode("2"); 15 | var n3 = new GraphNode("3"); 16 | n1.Neighbors = new[] { n2 }; 17 | n2.Neighbors = new[] { n3 }; 18 | var graph = new Graph(new[] { n1, n2, n3 }); 19 | 20 | Assert.IsFalse(graph.IsCyclic()); 21 | } 22 | 23 | [Test(Description = " 1<-2<-3 ")] 24 | public void IsCyclic_False_02() 25 | { 26 | var n1 = new GraphNode("1"); 27 | var n2 = new GraphNode("2"); 28 | var n3 = new GraphNode("3"); 29 | n3.Neighbors = new[] { n2 }; 30 | n2.Neighbors = new[] { n1 }; 31 | var graph = new Graph(new[] { n1, n2, n3 }); 32 | 33 | Assert.IsFalse(graph.IsCyclic()); 34 | } 35 | 36 | [Test(Description = " 1->2->3, 1->3 ")] 37 | public void IsCyclic_False_03() 38 | { 39 | var n1 = new GraphNode("1"); 40 | var n2 = new GraphNode("2"); 41 | var n3 = new GraphNode("3"); 42 | n1.Neighbors = new[] { n2, n3 }; 43 | n2.Neighbors = new[] { n3 }; 44 | var graph = new Graph(new[] { n1, n2, n3 }); 45 | 46 | Assert.IsFalse(graph.IsCyclic()); 47 | } 48 | 49 | [Test(Description = " 1<->2 ")] 50 | public void IsCyclic_True_01() 51 | { 52 | var n1 = new GraphNode("1"); 53 | var n2 = new GraphNode("2"); 54 | n1.Neighbors = new[] { n2 }; 55 | n2.Neighbors = new[] { n1 }; 56 | var graph = new Graph(new[] { n1, n2 }); 57 | 58 | Assert.IsTrue(graph.IsCyclic()); 59 | } 60 | 61 | [Test(Description = " 1->2->3, 3->1 ")] 62 | public void IsCyclic_True_02() 63 | { 64 | var n1 = new GraphNode("1"); 65 | var n2 = new GraphNode("2"); 66 | var n3 = new GraphNode("3"); 67 | n1.Neighbors = new[] { n2 }; 68 | n2.Neighbors = new[] { n3 }; 69 | n3.Neighbors = new[] { n1 }; 70 | var graph = new Graph(new[] { n1, n2, n3 }); 71 | 72 | Assert.IsTrue(graph.IsCyclic()); 73 | } 74 | 75 | [Test(Description = " 1->2<->3, 2->4, 4->3 ")] 76 | public void IsCyclic_True_03() 77 | { 78 | var n1 = new GraphNode("1"); 79 | var n2 = new GraphNode("2"); 80 | var n3 = new GraphNode("3"); 81 | var n4 = new GraphNode("4"); 82 | n1.Neighbors = new[] { n2 }; 83 | n2.Neighbors = new[] { n3, n4 }; 84 | n3.Neighbors = new[] { n2 }; 85 | n4.Neighbors = new[] { n3 }; 86 | var graph = new Graph(new[] { n1, n2, n3, n4 }); 87 | 88 | Assert.IsTrue(graph.IsCyclic()); 89 | } 90 | 91 | [Test(Description = " 1->2, 3->4, 4->3 ")] 92 | public void IsCyclic_True_04() 93 | { 94 | var n1 = new GraphNode("1"); 95 | var n2 = new GraphNode("2"); 96 | var n3 = new GraphNode("3"); 97 | var n4 = new GraphNode("4"); 98 | n1.Neighbors = new[] { n2 }; 99 | n3.Neighbors = new[] { n4 }; 100 | n4.Neighbors = new[] { n3 }; 101 | var graph = new Graph(new[] { n1, n2, n3, n4 }); 102 | 103 | Assert.IsTrue(graph.IsCyclic()); 104 | } 105 | } 106 | 107 | #region Graph interfaces 108 | 109 | /// 110 | /// Graph. 111 | /// 112 | public class Graph : IGraph 113 | { 114 | #region IGraph methods 115 | public IEnumerable Nodes { get; private set; } 116 | #endregion 117 | 118 | /// 119 | /// ctor. 120 | /// 121 | public Graph(IEnumerable nodes) 122 | { 123 | this.Nodes = nodes; 124 | } 125 | } 126 | 127 | /// 128 | /// Graph node. 129 | /// 130 | public class GraphNode : IGraphNode 131 | { 132 | #region IGraphNode methods 133 | 134 | public string Id { get; private set; } 135 | 136 | public IEnumerable Neighbors { get; set; } 137 | 138 | #endregion 139 | 140 | private static readonly IEnumerable emptyGraphNodes = new GraphNode[0]; 141 | 142 | /// 143 | /// ctor. 144 | /// 145 | public GraphNode(string id, IEnumerable neighbors = null) 146 | { 147 | this.Id = id; 148 | this.Neighbors = neighbors ?? emptyGraphNodes; 149 | } 150 | 151 | public override string ToString() 152 | { 153 | return Id; 154 | } 155 | } 156 | 157 | #endregion 158 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/GuidExtensionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | using NUnit.Framework; 5 | using rm.Extensions; 6 | 7 | namespace rm.ExtensionsTest; 8 | 9 | [TestFixture] 10 | public class GuidExtensionTest 11 | { 12 | private const string guidStringSample = "8843eb18-45d9-4528-a4e8-62e277a26629"; 13 | private const int iterations = 1_000_000; 14 | 15 | [Test] 16 | public void ToByteArrayMatchingStringRepresentation_01() 17 | { 18 | Console.WriteLine($"BitConverter.IsLittleEndian: {BitConverter.IsLittleEndian}"); 19 | var guidString = guidStringSample; 20 | var guid = Guid.Parse(guidString); 21 | 22 | var bytes = guid.ToByteArrayMatchingStringRepresentation(); 23 | 24 | var guidStringRoundtrip = 25 | Guid.Parse( 26 | BitConverter.ToString(bytes) 27 | .Replace("-", "") 28 | .ToLower()) 29 | .ToString("D"); 30 | Assert.AreEqual(guidString, guidStringRoundtrip); 31 | } 32 | 33 | [Test] 34 | public void ToGuidMatchingStringRepresentations_01() 35 | { 36 | Console.WriteLine($"BitConverter.IsLittleEndian: {BitConverter.IsLittleEndian}"); 37 | var guidString = guidStringSample.Replace("-", ""); 38 | var bytes = 39 | Enumerable.Range(0, guidString.Length / 2) 40 | .Select(i => Convert.ToByte(guidString.Substring(i * 2, 2), 16)) 41 | .ToArray(); 42 | 43 | var guidRoundtrip = bytes.ToGuidMatchingStringRepresentation(); 44 | 45 | var guid = Guid.Parse(guidString); 46 | Assert.AreEqual(guid, guidRoundtrip); 47 | } 48 | 49 | [Test] 50 | public void ToByteArrayMatchingStringRepresentation_ToGuidMatchingStringRepresentation_Roundtrip_01() 51 | { 52 | Console.WriteLine($"BitConverter.IsLittleEndian: {BitConverter.IsLittleEndian}"); 53 | var count = 1_000; 54 | for (int i = 0; i < count; i++) 55 | { 56 | var guid = Guid.NewGuid(); 57 | var guidRoundtrip = guid.ToByteArrayMatchingStringRepresentation().ToGuidMatchingStringRepresentation(); 58 | Assert.AreEqual(guid, guidRoundtrip); 59 | } 60 | } 61 | 62 | [Test] 63 | public void ToBase64String_01() 64 | { 65 | var guidString = "856a990d3aee4bca825f7e8f3c613e9f"; 66 | var guidBase64urlString = "hWqZDTruS8qCX36PPGE+nw=="; 67 | Assert.AreEqual(guidBase64urlString, Guid.Parse(guidString).ToBase64String()); 68 | } 69 | 70 | [Test] 71 | public void FromBase64String_01() 72 | { 73 | var guidString = "856a990d3aee4bca825f7e8f3c613e9f"; 74 | var guidBase64urlString = "hWqZDTruS8qCX36PPGE+nw=="; 75 | Assert.AreEqual(guidString, guidBase64urlString.FromBase64String().ToString("N")); 76 | } 77 | 78 | [Test] 79 | public void FromBase64String_ToBase64String_Roundtrip_01() 80 | { 81 | var count = 1_000; 82 | for (int i = 0; i < count; i++) 83 | { 84 | var guid = Guid.NewGuid(); 85 | var guidRoundtrip = guid.ToBase64String().FromBase64String(); 86 | Assert.AreEqual(guid, guidRoundtrip); 87 | } 88 | } 89 | 90 | [Test] 91 | public void ToBase64UrlString_01() 92 | { 93 | var guidString = "856a990d3aee4bca825f7e8f3c613e9f"; 94 | var guidBase64urlString = "hWqZDTruS8qCX36PPGE-nw"; 95 | Assert.AreEqual(guidBase64urlString, Guid.Parse(guidString).ToBase64UrlString()); 96 | } 97 | 98 | [Test] 99 | public void FromBase64UrlString_01() 100 | { 101 | var guidString = "856a990d3aee4bca825f7e8f3c613e9f"; 102 | var guidBase64urlString = "hWqZDTruS8qCX36PPGE-nw"; 103 | Assert.AreEqual(guidString, guidBase64urlString.FromBase64UrlString().ToString("N")); 104 | } 105 | 106 | [Test] 107 | public void FromBase64UrlString_ToBase64UrlString_Roundtrip_01() 108 | { 109 | var count = 1_000; 110 | for (int i = 0; i < count; i++) 111 | { 112 | var guid = Guid.NewGuid(); 113 | var guidRoundtrip = guid.ToBase64UrlString().FromBase64UrlString(); 114 | Assert.AreEqual(guid, guidRoundtrip); 115 | } 116 | } 117 | 118 | [Explicit] 119 | [Test] 120 | public void ToBase64UrlString_Sample() 121 | { 122 | var guid = Guid.NewGuid(); 123 | Console.WriteLine(guid.ToString("N")); 124 | Console.WriteLine(guid.ToBase64UrlString()); 125 | } 126 | 127 | [Test] 128 | public void ToBase32String_01() 129 | { 130 | var guidString = "856a990d3aee4bca825f7e8f3c613e9f"; 131 | var guidBase32String = "GNN9J39TXS5WN0JZFT7KRR9YKW"; 132 | Assert.AreEqual(guidBase32String, Guid.Parse(guidString).ToBase32String()); 133 | } 134 | 135 | [Test] 136 | public void FromBase32String_01() 137 | { 138 | var guidString = "856a990d3aee4bca825f7e8f3c613e9f"; 139 | var guidBase32String = "GNN9J39TXS5WN0JZFT7KRR9YKW"; 140 | Assert.AreEqual(guidString, guidBase32String.FromBase32String().ToString("N")); 141 | } 142 | 143 | [Test] 144 | public void FromBase32String_ToBase32String_Roundtrip_01() 145 | { 146 | var count = 1_000; 147 | for (int i = 0; i < count; i++) 148 | { 149 | var guid = Guid.NewGuid(); 150 | var guidRoundtrip = guid.ToBase32String().FromBase32String(); 151 | Assert.AreEqual(guid, guidRoundtrip); 152 | } 153 | } 154 | 155 | [Explicit] 156 | [Test] 157 | public void ToBase32String_Sample() 158 | { 159 | var guid = Guid.NewGuid(); 160 | Console.WriteLine(guid.ToString("N")); 161 | Console.WriteLine(guid.ToBase32String()); 162 | } 163 | 164 | public class Perf 165 | { 166 | [Explicit] 167 | [Test] 168 | public void Perf_ToByteArray_01() 169 | { 170 | Console.WriteLine($"BitConverter.IsLittleEndian: {BitConverter.IsLittleEndian}"); 171 | var stopwatch = Stopwatch.StartNew(); 172 | var g = Guid.NewGuid(); 173 | for (int i = 0; i < iterations; i++) 174 | { 175 | var _ = g.ToByteArray(); 176 | } 177 | stopwatch.Stop(); 178 | Console.WriteLine(stopwatch.ElapsedMilliseconds); 179 | } 180 | 181 | [Explicit] 182 | [Test] 183 | public void Perf_ToByteArrayMatchingStringRepresentation_01() 184 | { 185 | Console.WriteLine($"BitConverter.IsLittleEndian: {BitConverter.IsLittleEndian}"); 186 | var stopwatch = Stopwatch.StartNew(); 187 | var g = Guid.NewGuid(); 188 | for (int i = 0; i < iterations; i++) 189 | { 190 | var _ = g.ToByteArrayMatchingStringRepresentation(); 191 | } 192 | stopwatch.Stop(); 193 | Console.WriteLine(stopwatch.ElapsedMilliseconds); 194 | } 195 | 196 | [Explicit] 197 | [Test] 198 | public void Perf_ToString_01() 199 | { 200 | Console.WriteLine($"BitConverter.IsLittleEndian: {BitConverter.IsLittleEndian}"); 201 | var stopwatch = Stopwatch.StartNew(); 202 | var g = Guid.NewGuid(); 203 | for (int i = 0; i < iterations; i++) 204 | { 205 | var _ = g.ToString(); 206 | } 207 | stopwatch.Stop(); 208 | Console.WriteLine(stopwatch.ElapsedMilliseconds); 209 | } 210 | 211 | [Explicit] 212 | [Test] 213 | public void Perf_ToBase64String_01() 214 | { 215 | Console.WriteLine($"BitConverter.IsLittleEndian: {BitConverter.IsLittleEndian}"); 216 | var stopwatch = Stopwatch.StartNew(); 217 | var g = Guid.NewGuid(); 218 | for (int i = 0; i < iterations; i++) 219 | { 220 | var _ = g.ToBase64String(); 221 | } 222 | stopwatch.Stop(); 223 | Console.WriteLine(stopwatch.ElapsedMilliseconds); 224 | } 225 | 226 | [Explicit] 227 | [Test] 228 | public void Perf_FromBase64String_01() 229 | { 230 | Console.WriteLine($"BitConverter.IsLittleEndian: {BitConverter.IsLittleEndian}"); 231 | var stopwatch = Stopwatch.StartNew(); 232 | var g = Guid.NewGuid().ToBase64String(); 233 | for (int i = 0; i < iterations; i++) 234 | { 235 | var _ = g.FromBase64String(); 236 | } 237 | stopwatch.Stop(); 238 | Console.WriteLine(stopwatch.ElapsedMilliseconds); 239 | } 240 | 241 | [Explicit] 242 | [Test] 243 | public void Perf_ToBase64UrlString_01() 244 | { 245 | Console.WriteLine($"BitConverter.IsLittleEndian: {BitConverter.IsLittleEndian}"); 246 | var stopwatch = Stopwatch.StartNew(); 247 | var g = Guid.NewGuid(); 248 | for (int i = 0; i < iterations; i++) 249 | { 250 | var _ = g.ToBase64UrlString(); 251 | } 252 | stopwatch.Stop(); 253 | Console.WriteLine(stopwatch.ElapsedMilliseconds); 254 | } 255 | 256 | [Explicit] 257 | [Test] 258 | public void Perf_FromBase64UrlString_01() 259 | { 260 | Console.WriteLine($"BitConverter.IsLittleEndian: {BitConverter.IsLittleEndian}"); 261 | var stopwatch = Stopwatch.StartNew(); 262 | var g = Guid.NewGuid().ToBase64UrlString(); 263 | for (int i = 0; i < iterations; i++) 264 | { 265 | var _ = g.FromBase64UrlString(); 266 | } 267 | stopwatch.Stop(); 268 | Console.WriteLine(stopwatch.ElapsedMilliseconds); 269 | } 270 | 271 | [Explicit] 272 | [Test] 273 | public void Perf_ToBase32String_01() 274 | { 275 | Console.WriteLine($"BitConverter.IsLittleEndian: {BitConverter.IsLittleEndian}"); 276 | var stopwatch = Stopwatch.StartNew(); 277 | var g = Guid.NewGuid(); 278 | for (int i = 0; i < iterations; i++) 279 | { 280 | var _ = g.ToBase32String(); 281 | } 282 | stopwatch.Stop(); 283 | Console.WriteLine(stopwatch.ElapsedMilliseconds); 284 | } 285 | 286 | [Explicit] 287 | [Test] 288 | public void Perf_FromBase32String_01() 289 | { 290 | Console.WriteLine($"BitConverter.IsLittleEndian: {BitConverter.IsLittleEndian}"); 291 | var stopwatch = Stopwatch.StartNew(); 292 | var g = Guid.NewGuid().ToBase32String(); 293 | for (int i = 0; i < iterations; i++) 294 | { 295 | var _ = g.FromBase32String(); 296 | } 297 | stopwatch.Stop(); 298 | Console.WriteLine(stopwatch.ElapsedMilliseconds); 299 | } 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/HashQueueTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using NUnit.Framework; 6 | using rm.Extensions; 7 | 8 | namespace rm.ExtensionsTest; 9 | 10 | [TestFixture] 11 | public class HashQueueTest 12 | { 13 | [Test] 14 | public void Enqueue01() 15 | { 16 | var hashq = new HashQueue(); 17 | hashq.Enqueue(1); 18 | hashq.Enqueue(1); 19 | Assert.AreEqual(2, hashq.Count()); 20 | } 21 | 22 | [Test] 23 | public void Dequeue01() 24 | { 25 | var hashq = new HashQueue(); 26 | hashq.Enqueue(1); 27 | hashq.Enqueue(1); 28 | Assert.AreEqual(2, hashq.Count()); 29 | Assert.AreEqual(1, hashq.Dequeue()); 30 | Assert.AreEqual(1, hashq.Count()); 31 | Assert.AreEqual(1, hashq.Dequeue()); 32 | Assert.AreEqual(0, hashq.Count()); 33 | } 34 | 35 | class Person 36 | { 37 | public string Name; 38 | public int Age; 39 | public override int GetHashCode() 40 | { 41 | // only Name 42 | return Name.GetHashCode(); 43 | } 44 | public override bool Equals(object obj) 45 | { 46 | return obj is Person that 47 | // Name and Age 48 | && (this.Name == that.Name 49 | && this.Age == that.Age); 50 | } 51 | } 52 | 53 | [Test] 54 | public void EnqueueDequeue_StableCheck_01() 55 | { 56 | var person1 = new Person() { Name = "p1", Age = 1 }; 57 | var person2 = new Person() { Name = "p1", Age = 2 }; 58 | Assert.AreEqual(person1.GetHashCode(), person2.GetHashCode()); 59 | Assert.AreNotEqual(person1, person2); 60 | var hashq = new HashQueue(); 61 | hashq.Enqueue(person1); 62 | hashq.Enqueue(person2); 63 | Assert.AreEqual(2, hashq.Count()); 64 | Person temp; 65 | temp = hashq.Dequeue(); 66 | Assert.AreSame(person1, temp); 67 | Assert.AreNotSame(person2, temp); 68 | Assert.AreEqual(1, hashq.Count()); 69 | temp = hashq.Dequeue(); 70 | Assert.AreSame(person2, temp); 71 | Assert.AreNotSame(person1, temp); 72 | Assert.AreEqual(0, hashq.Count()); 73 | } 74 | 75 | [Test] 76 | public void EnqueueDequeue_StableCheck_02() 77 | { 78 | var hashq = new HashQueue(); 79 | var person1 = new Person() { Name = "p1", Age = 1 }; 80 | hashq.Enqueue(person1); 81 | hashq.Enqueue(person1); 82 | Assert.AreEqual(2, hashq.Count()); 83 | Assert.AreSame(person1, hashq.Dequeue()); 84 | Assert.AreEqual(1, hashq.Count()); 85 | Assert.AreSame(person1, hashq.Dequeue()); 86 | Assert.AreEqual(0, hashq.Count()); 87 | } 88 | 89 | [Test] 90 | public void PeekPeekTail01() 91 | { 92 | var hashq = new HashQueue(); 93 | hashq.Enqueue(1); 94 | hashq.Enqueue(2); 95 | hashq.Enqueue(3); 96 | Assert.AreEqual(1, hashq.Peek()); 97 | Assert.AreEqual(3, hashq.PeekTail()); 98 | } 99 | 100 | [Test] 101 | public void IsEmpty01() 102 | { 103 | var hashq = new HashQueue(); 104 | Assert.IsTrue(hashq.IsEmpty()); 105 | hashq.Enqueue(1); 106 | Assert.IsFalse(hashq.IsEmpty()); 107 | hashq.Dequeue(); 108 | Assert.IsTrue(hashq.IsEmpty()); 109 | Assert.Throws(() => hashq.Dequeue()); 110 | } 111 | 112 | [Test] 113 | public void Clear01() 114 | { 115 | var hashq = new HashQueue(); 116 | hashq.Enqueue(1); 117 | hashq.Enqueue(2); 118 | Assert.IsFalse(hashq.IsEmpty()); 119 | hashq.Clear(); 120 | Assert.IsTrue(hashq.IsEmpty()); 121 | hashq.Enqueue(1); 122 | hashq.Enqueue(2); 123 | Assert.IsFalse(hashq.IsEmpty()); 124 | } 125 | 126 | [Test] 127 | public void Delete_Between_01() 128 | { 129 | var hashq = new HashQueue(); 130 | Assert.AreEqual(0, hashq.Count()); 131 | hashq.Enqueue(1); 132 | hashq.Enqueue(2); 133 | hashq.Enqueue(3); 134 | Assert.AreEqual(1, hashq.Peek()); 135 | Assert.AreEqual(3, hashq.PeekTail()); 136 | hashq.Delete(2); 137 | Assert.AreEqual(1, hashq.Peek()); 138 | Assert.AreEqual(3, hashq.PeekTail()); 139 | } 140 | 141 | [Test] 142 | public void Delete_HeadTail_02() 143 | { 144 | var hashq = new HashQueue(); 145 | Assert.AreEqual(0, hashq.Count()); 146 | hashq.Enqueue(1); 147 | Assert.AreEqual(1, hashq.Peek()); 148 | Assert.AreEqual(1, hashq.PeekTail()); 149 | hashq.Delete(1); 150 | Assert.IsTrue(hashq.IsEmpty()); 151 | } 152 | 153 | [Test] 154 | public void Delete_ReEnqueue_03() 155 | { 156 | var hashq = new HashQueue(); 157 | Assert.AreEqual(0, hashq.Count()); 158 | hashq.Enqueue(1); 159 | Assert.AreEqual(1, hashq.Count()); 160 | hashq.Delete(1); 161 | Assert.AreEqual(0, hashq.Count()); 162 | hashq.Enqueue(1); 163 | Assert.AreEqual(1, hashq.Count()); 164 | } 165 | 166 | [Test] 167 | public void Delete_DuplicateHashCode_04() 168 | { 169 | var person1 = new Person() { Name = "p1", Age = 1 }; 170 | var person2 = new Person() { Name = "p1", Age = 2 }; 171 | Assert.AreEqual(person1.GetHashCode(), person2.GetHashCode()); 172 | Assert.AreNotEqual(person1, person2); 173 | var hashq = new HashQueue(); 174 | Assert.AreEqual(0, hashq.Count()); 175 | hashq.Enqueue(person1); 176 | hashq.Enqueue(person2); 177 | Assert.AreEqual(2, hashq.Count()); 178 | Assert.IsTrue(hashq.Delete(person2)); 179 | Assert.AreEqual(1, hashq.Count()); 180 | Assert.AreSame(person1, hashq.Peek()); 181 | } 182 | 183 | [Test(Description = "HashQueue v/s Queue find speed test.")] 184 | [Category("slow")] 185 | public void Find01() 186 | { 187 | var count = 1000000; 188 | var sw = new Stopwatch(); 189 | 190 | var hashq = new HashQueue(); 191 | for (int i = 0; i < count; i++) 192 | { hashq.Enqueue(i); } 193 | sw.Start(); 194 | Assert.IsTrue(hashq.Delete(count - 1)); 195 | sw.Stop(); 196 | var hashqTime = sw.ElapsedMilliseconds; 197 | Console.WriteLine($"hashqTime: {hashqTime}"); 198 | 199 | sw.Reset(); 200 | var q = new Queue(); 201 | for (int i = 0; i < count; i++) 202 | { q.Enqueue(i); } 203 | sw.Start(); 204 | // Queue.Remove(x) and Where(x) are O(n). 205 | q.Where(x => x == count - 1).ToList(); 206 | sw.Stop(); 207 | var qTime = sw.ElapsedMilliseconds; 208 | Console.WriteLine($"qTime: {qTime}"); 209 | 210 | Assert.Less(hashqTime, qTime); 211 | Assert.Less(hashqTime, 10); 212 | } 213 | 214 | [Explicit] 215 | [Test(Description = "HashQueue v/s Queue enumerate speed test.")] 216 | [Category("slow")] 217 | public void Enumerate01() 218 | { 219 | var count = 1000000; 220 | var sw = new Stopwatch(); 221 | 222 | var hashq = new HashQueue(); 223 | for (int i = 0; i < count; i++) 224 | { hashq.Enqueue(i); } 225 | sw.Start(); 226 | foreach (var item in hashq) { } 227 | sw.Stop(); 228 | var hashqTime = sw.ElapsedMilliseconds; 229 | Console.WriteLine($"hashqTime: {hashqTime}"); 230 | 231 | sw.Reset(); 232 | var q = new Queue(); 233 | for (int i = 0; i < count; i++) 234 | { q.Enqueue(i); } 235 | sw.Start(); 236 | foreach (var item in q) { } 237 | sw.Stop(); 238 | var qTime = sw.ElapsedMilliseconds; 239 | Console.WriteLine($"qTime: {qTime}"); 240 | 241 | Assert.Less(hashqTime, qTime * 10); 242 | Assert.Less(hashqTime, 50); 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/HeapTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using rm.Extensions; 4 | 5 | namespace rm.ExtensionsTest; 6 | 7 | [TestFixture] 8 | public class HeapTest 9 | { 10 | class Person 11 | { 12 | public int Age { get; set; } 13 | } 14 | 15 | // tests Siftdown() internally. 16 | [Test] 17 | public void Append01() 18 | { 19 | var person1 = new Person { Age = 1 }; 20 | var person2 = new Person { Age = 1 }; 21 | var minheap = new MinHeap(2, x => x.Age); 22 | minheap.Append(person1); 23 | minheap.Append(person2); 24 | foreach (var x in minheap) { break; } 25 | Assert.AreEqual(person1, minheap.Peek()); 26 | Assert.AreNotEqual(person2, minheap.Peek()); 27 | } 28 | 29 | // tests SiftUp() internally. 30 | [Test] 31 | public void Insert01() 32 | { 33 | var person1 = new Person { Age = 1 }; 34 | var person2 = new Person { Age = 1 }; 35 | var maxheap = new MaxHeap(2, x => x.Age); 36 | maxheap.Insert(person1); 37 | maxheap.Insert(person2); 38 | Assert.AreEqual(person1, maxheap.Peek()); 39 | Assert.AreNotEqual(person2, maxheap.Peek()); 40 | } 41 | 42 | [Test] 43 | public void Insert02() 44 | { 45 | var minheap = new MinHeap(3); 46 | Assert.AreEqual(0, minheap.Count()); 47 | minheap.Insert(0); 48 | Assert.AreEqual(1, minheap.Count()); 49 | minheap.Insert(0); 50 | Assert.AreEqual(2, minheap.Count()); 51 | minheap.Insert(2); 52 | Assert.AreEqual(3, minheap.Count()); 53 | 54 | var maxheap = new MaxHeap(3); 55 | Assert.AreEqual(0, maxheap.Count()); 56 | maxheap.Insert(0); 57 | Assert.AreEqual(1, maxheap.Count()); 58 | maxheap.Insert(0); 59 | Assert.AreEqual(2, maxheap.Count()); 60 | maxheap.Insert(1); 61 | Assert.AreEqual(3, maxheap.Count()); 62 | } 63 | 64 | [Test] 65 | public void Delete01() 66 | { 67 | var minheap = new MinHeap(2); 68 | Assert.AreEqual(0, minheap.Count()); 69 | minheap.Insert(1); 70 | minheap.Insert(0); 71 | Assert.Throws(() => { minheap.Insert(-1); }); 72 | Assert.AreEqual(0, minheap.Delete()); 73 | Assert.AreEqual(1, minheap.Count()); 74 | 75 | var maxheap = new MaxHeap(2); 76 | Assert.AreEqual(0, maxheap.Count()); 77 | maxheap.Insert(0); 78 | maxheap.Insert(1); 79 | Assert.Throws(() => { maxheap.Insert(-1); }); 80 | Assert.AreEqual(1, maxheap.Delete()); 81 | Assert.AreEqual(1, maxheap.Count()); 82 | } 83 | 84 | [Test] 85 | public void Displace01() 86 | { 87 | var minheap = new MinHeap(2); 88 | Assert.AreEqual(0, minheap.Count()); 89 | minheap.Insert(5); 90 | minheap.Insert(4); 91 | Assert.AreEqual(4, minheap.Displace(6)); 92 | Assert.AreEqual(2, minheap.Count()); 93 | Assert.AreEqual(5, minheap.Peek()); 94 | 95 | var maxheap = new MaxHeap(2); 96 | Assert.AreEqual(0, maxheap.Count()); 97 | maxheap.Insert(3); 98 | maxheap.Insert(4); 99 | Assert.AreEqual(4, maxheap.Displace(2)); 100 | Assert.AreEqual(2, maxheap.Count()); 101 | Assert.AreEqual(3, maxheap.Peek()); 102 | } 103 | 104 | [Test] 105 | public void Peek01() 106 | { 107 | var minheap = new MinHeap(2); 108 | Assert.Throws(() => { minheap.Peek(); }); 109 | minheap.Insert(0); 110 | Assert.AreEqual(0, minheap.Peek()); 111 | 112 | var maxheap = new MaxHeap(2); 113 | Assert.Throws(() => { maxheap.Peek(); }); 114 | maxheap.Insert(0); 115 | Assert.AreEqual(0, maxheap.Peek()); 116 | } 117 | 118 | [Test] 119 | public void Ineritance01() 120 | { 121 | IHeap minheap1 = new MinHeap(2); 122 | IHeap maxheap1 = new MaxHeap(2); 123 | IHeap minheap2 = new MinHeap(2, x => x); 124 | IHeap maxheap2 = new MaxHeap(2, x => x); 125 | // these should not compile 126 | //IHeap maxheap3 = new MaxHeap(2, x => x); 127 | //IHeap minheap3 = new MinHeap(2, x => x); 128 | //IHeap maxheap3 = new MaxHeap(2); 129 | //IHeap minheap3 = new MinHeap(2); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/HelperTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using rm.Extensions; 3 | 4 | namespace rm.ExtensionsTest; 5 | 6 | [TestFixture] 7 | public class HelperTest 8 | { 9 | [Test] 10 | public void Swap01() 11 | { 12 | var t1 = 1; 13 | var t2 = 2; 14 | Assert.AreNotEqual(t1, t2); 15 | Helper.Swap(ref t1, ref t2); 16 | Assert.AreNotEqual(1, t1); 17 | Assert.AreNotEqual(2, t2); 18 | Assert.AreEqual(2, t1); 19 | Assert.AreEqual(1, t2); 20 | } 21 | 22 | [Test] 23 | public void Swap02() 24 | { 25 | var c1 = new object(); var c1copy = c1; 26 | var c2 = new object(); var c2copy = c2; 27 | Assert.AreNotEqual(c1, c2); 28 | Helper.Swap(ref c1, ref c2); 29 | Assert.AreNotEqual(c1copy, c1); 30 | Assert.AreNotEqual(c2copy, c2); 31 | Assert.AreEqual(c2copy, c1); 32 | Assert.AreEqual(c1copy, c2); 33 | } 34 | 35 | [Test] 36 | public void Swap03() 37 | { 38 | object c1 = null; var c1copy = c1; 39 | var c2 = new object(); var c2copy = c2; 40 | Assert.AreNotEqual(c1, c2); 41 | Helper.Swap(ref c1, ref c2); 42 | Assert.AreNotEqual(c1copy, c1); 43 | Assert.AreNotEqual(c2copy, c2); 44 | Assert.AreEqual(c2copy, c1); 45 | Assert.AreEqual(c1copy, c2); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/IntExtensionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using rm.Extensions; 4 | 5 | namespace rm.ExtensionsTest; 6 | 7 | [TestFixture] 8 | public class IntExtensionTest 9 | { 10 | [Test] 11 | [TestCase(3, 6)] 12 | [TestCase(0, 1)] 13 | [TestCase(10, 3628800)] 14 | [TestCase(12, 479001600)] 15 | public void Factorial01(int n, int result) 16 | { 17 | Assert.AreEqual(result, (int)n.Factorial()); 18 | } 19 | 20 | [Test] 21 | public void Factorial02() 22 | { 23 | //Assert.Throws(() => 24 | //{ 25 | // //13.Factorial(); // int 26 | // 21.Factorial(); // long 27 | //}); 28 | Assert.DoesNotThrow(() => 29 | { 30 | 100.Factorial(); 31 | }); 32 | } 33 | 34 | [Test] 35 | [TestCase(10, 4, 5040)] 36 | [TestCase(3, 3, 6)] 37 | [TestCase(0, 0, 1)] 38 | public void Permutation01(int n, int r, int result) 39 | { 40 | Assert.AreEqual(result, (int)n.Permutation(r)); 41 | } 42 | 43 | [Test] 44 | [TestCase(10, 4, 210)] 45 | [TestCase(3, 3, 1)] 46 | [TestCase(0, 0, 1)] 47 | public void Combination01(int n, int r, int result) 48 | { 49 | Assert.AreEqual(result, (int)n.Combination(r)); 50 | } 51 | 52 | [Test] 53 | [TestCase(2, 4)] 54 | [TestCase(4, 64)] 55 | [TestCase(0, 0)] 56 | public void ScrabbleCount01(int n, int result) 57 | { 58 | Assert.AreEqual(result, (int)n.ScrabbleCount()); 59 | } 60 | 61 | [Test] 62 | [TestCase(2, 0, "2")] 63 | [TestCase(1000, 0, "1k")] 64 | [TestCase(1000000, 0, "1m")] 65 | [TestCase(1000000000, 0, "1g")] 66 | [TestCase(1500, 0, "1k")] 67 | [TestCase(1900, 0, "1k")] 68 | [TestCase(2000, 0, "2k")] 69 | [TestCase(int.MaxValue, 0, "2g")] 70 | [TestCase(int.MinValue, 0, null)] // OverflowException due to abs(n) 71 | [TestCase(int.MinValue + 1, 0, "-2g")] 72 | [TestCase(999, 0, "999")] 73 | [TestCase(-999, 0, "-999")] 74 | [TestCase(1001, 0, "1k")] 75 | [TestCase(-1001, 0, "-1k")] 76 | [TestCase(1099, 1, "1k")] 77 | [TestCase(1299, 1, "1.2k")] 78 | [TestCase(1599, 1, "1.5k")] 79 | [TestCase(1999, 1, "1.9k")] 80 | public void Round01(int n, int digits, string result) 81 | { 82 | if (result.IsNullOrEmpty()) 83 | { 84 | Assert.Throws(() => n.Round((uint)digits)); 85 | } 86 | else 87 | { 88 | Assert.AreEqual(result, n.Round((uint)digits)); 89 | } 90 | } 91 | 92 | [Test] 93 | [TestCase(0, 5, 0)] 94 | [TestCase(1, 5, 0)] 95 | [TestCase(4, 5, 0)] 96 | [TestCase(5, 5, 5)] 97 | [TestCase(6, 5, 5)] 98 | [TestCase(9, 5, 5)] 99 | [TestCase(10, 5, 10)] 100 | [TestCase(-1, 5, -5)] 101 | [TestCase(-4, 5, -5)] 102 | [TestCase(-5, 5, -10)] 103 | [TestCase(-6, 5, -10)] 104 | [TestCase(-9, 5, -10)] 105 | [TestCase(-10, 5, -15)] 106 | public void Bin01(int n, int binSize, int result) 107 | { 108 | Assert.AreEqual(result, n.Bin(binSize)); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/ListExtensionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | using rm.Extensions; 5 | 6 | namespace rm.ExtensionsTest; 7 | 8 | [TestFixture] 9 | public class ListExtensionTest 10 | { 11 | [Test] 12 | public void RemoveLast01() 13 | { 14 | var list = new int[] { 1, 2 }.ToList(); 15 | list.RemoveLast(); 16 | Assert.AreEqual(1, list.Count); 17 | list.RemoveLast(); 18 | Assert.AreEqual(0, list.Count); 19 | Assert.Throws(() => list.RemoveLast()); 20 | } 21 | 22 | [Test] 23 | public void RemoveLast02() 24 | { 25 | var list = new int[] { 1, 2 }.ToList(); 26 | list.RemoveLast(2); 27 | Assert.AreEqual(0, list.Count); 28 | Assert.Throws(() => list.RemoveLast()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/LruCacheTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using rm.Extensions; 3 | 4 | namespace rm.ExtensionsTest; 5 | 6 | [TestFixture] 7 | public class LruCacheTest 8 | { 9 | private const int capacity = 5; 10 | ILruCache cache; 11 | 12 | [SetUp] 13 | public void Setup() 14 | { 15 | cache = new LruCache(capacity); 16 | } 17 | 18 | [Test] 19 | public void Get_01() 20 | { 21 | cache.Insert(-1, -1); 22 | for (int i = 0; i < capacity; i++) 23 | { 24 | cache.Insert(i, i); 25 | } 26 | Assert.IsFalse(cache.HasKey(-1)); 27 | } 28 | 29 | [Test] 30 | public void Get_02() 31 | { 32 | Assert.AreEqual(default(int), cache.Get(0)); 33 | } 34 | 35 | [Test] 36 | public void Get_03() 37 | { 38 | Assert.DoesNotThrow(() => cache.Get(0)); 39 | } 40 | 41 | [Test] 42 | public void Insert_01() 43 | { 44 | for (int i = 0; i < capacity; i++) 45 | { 46 | cache.Insert(i, i); 47 | Assert.AreEqual(i + 1, cache.Count()); 48 | } 49 | } 50 | 51 | [Test] 52 | public void Lru_01() 53 | { 54 | int key = 0; 55 | for (int i = 0; i < capacity; i++, key++) 56 | { 57 | cache.Insert(key, key); 58 | } 59 | for (int i = 0; i < capacity; i++, key++) 60 | { 61 | cache.Insert(key, key); 62 | } 63 | for (int i = 0; i < capacity; i++) 64 | { 65 | Assert.IsFalse(cache.HasKey(i)); 66 | } 67 | for (int i = 0; i < capacity; i++) 68 | { 69 | Assert.IsTrue(cache.HasKey(capacity + i)); 70 | } 71 | } 72 | 73 | [Test] 74 | public void Lru_02() 75 | { 76 | int key = 0; 77 | for (int i = 0; i < capacity; i++, key++) 78 | { 79 | cache.Insert(key, key); 80 | } 81 | cache.Get(2); 82 | for (int i = 0; i < capacity - 1; i++, key++) 83 | { 84 | cache.Insert(key, key); 85 | } 86 | Assert.IsTrue(cache.HasKey(2)); 87 | cache.Insert(key, key); 88 | Assert.IsFalse(cache.HasKey(2)); 89 | } 90 | 91 | [Test] 92 | public void Remove_01() 93 | { 94 | for (int i = 0; i < capacity; i++) 95 | { 96 | cache.Insert(i, i); 97 | } 98 | for (int i = 0; i < capacity; i++) 99 | { 100 | cache.Remove(i); 101 | Assert.AreEqual(capacity - i - 1, cache.Count()); 102 | } 103 | } 104 | 105 | [Test] 106 | public void HasKey_01() 107 | { 108 | for (int i = 0; i < capacity; i++) 109 | { 110 | cache.Insert(i, i); 111 | } 112 | cache.Remove(0); 113 | Assert.IsFalse(cache.HasKey(0)); 114 | } 115 | 116 | [Test] 117 | public void IsEmpty_01() 118 | { 119 | Assert.IsTrue(cache.IsEmpty()); 120 | cache.Insert(0, 0); 121 | Assert.IsFalse(cache.IsEmpty()); 122 | } 123 | 124 | [Test] 125 | public void IsFull_01() 126 | { 127 | for (int i = 0; i < capacity; i++) 128 | { 129 | Assert.IsFalse(cache.IsFull()); 130 | cache.Insert(i, i); 131 | } 132 | Assert.IsTrue(cache.IsFull()); 133 | } 134 | 135 | [Test] 136 | public void IsFull_02() 137 | { 138 | for (int i = 0; i < capacity; i++) 139 | { 140 | cache.Insert(i, i); 141 | } 142 | Assert.IsTrue(cache.IsFull()); 143 | cache.Remove(0); 144 | Assert.IsFalse(cache.IsFull()); 145 | } 146 | 147 | [Test] 148 | public void Capacity_01() 149 | { 150 | Assert.AreEqual(capacity, cache.Capacity()); 151 | } 152 | 153 | [Test] 154 | public void Count_01() 155 | { 156 | Assert.AreEqual(0, cache.Count()); 157 | cache.Insert(0, 0); 158 | Assert.AreEqual(1, cache.Count()); 159 | } 160 | 161 | [Test] 162 | public void Count_02() 163 | { 164 | for (int i = 0; i < capacity; i++) 165 | { 166 | cache.Insert(i, i); 167 | } 168 | Assert.AreEqual(capacity, cache.Count()); 169 | cache.Insert(10, 10); 170 | Assert.AreEqual(capacity, cache.Count()); 171 | } 172 | 173 | [Test] 174 | public void Clear_01() 175 | { 176 | for (int i = 0; i < capacity; i++) 177 | { 178 | cache.Insert(i, i); 179 | } 180 | cache.Clear(); 181 | Assert.IsTrue(cache.IsEmpty()); 182 | } 183 | 184 | [Test] 185 | public void Ctor_01() 186 | { 187 | Assert.DoesNotThrow(() => 188 | { 189 | cache = new LruCache(0); 190 | cache.Insert(0, 0); 191 | Assert.IsFalse(cache.HasKey(0)); 192 | }); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/NameValueCollectionExtensionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Specialized; 4 | using System.Linq; 5 | using NUnit.Framework; 6 | using rm.Extensions; 7 | 8 | namespace rm.ExtensionsTest; 9 | 10 | [TestFixture] 11 | public class NameValueCollectionExtensionTest 12 | { 13 | private NameValueCollection GetCollection(string[] args) 14 | { 15 | if (args == null || args.Length == 0) 16 | { 17 | return new NameValueCollection(); 18 | } 19 | if (args.Length % 2 != 0) 20 | { 21 | throw new ApplicationException("args length should be even."); 22 | } 23 | var collection = new NameValueCollection(); 24 | for (int i = 0; i < args.Length; i += 2) 25 | { 26 | collection.Add(args[i], args[i + 1]); 27 | } 28 | return collection; 29 | } 30 | 31 | [Test] 32 | [TestCase("?k1=v1&k2=v2", "k1", "v1", "k2", "v2")] 33 | [TestCase("?k1=v1&k1=v1", "k1", "v1", "k1", "v1")] 34 | [TestCase("?k1%2C=v1%2C", "k1,", "v1,")] 35 | [TestCase("")] 36 | public void ToQueryString01(string expected, params string[] args) 37 | { 38 | var nvc = GetCollection(args); 39 | Assert.AreEqual(expected, nvc.ToQueryString()); 40 | Assert.AreEqual(expected.Replace("?", ""), nvc.ToQueryString(false)); 41 | } 42 | 43 | [Test] 44 | [TestCase(null, "v")] 45 | [TestCase("k", null)] 46 | public void ToQueryString02(params string[] args) 47 | { 48 | var nvc = GetCollection(args); 49 | Assert.Throws(() => 50 | { 51 | var query = nvc.ToQueryString(); 52 | }); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | [assembly: Parallelizable(ParallelScope.None)] 4 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/RandomExtensionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using NUnit.Framework; 5 | using rm.Extensions; 6 | using rm.Random2; 7 | 8 | namespace rm.ExtensionsTest; 9 | 10 | [TestFixture] 11 | public class RandomExtensionTest 12 | { 13 | private readonly Random random = RandomFactory.GetThreadStaticRandom(); 14 | 15 | private const int iterations = 1_000_000; 16 | 17 | [Explicit] 18 | [Test] 19 | [TestCase(10)] 20 | public void Verify_NextItem(int size) 21 | { 22 | var source = Enumerable.Range(0, size).ToArray(); 23 | var nextItem = random.NextItem(source); 24 | Console.WriteLine(nextItem); 25 | } 26 | 27 | [Test] 28 | public void Verify_NextItem_Throws() 29 | { 30 | var source = Array.Empty(); 31 | Assert.Throws(() => random.NextItem(source)); 32 | } 33 | 34 | [Explicit] 35 | [Test] 36 | [TestCase(3500, 350)] 37 | public void Verify_NextGaussian(double mu, double sigma) 38 | { 39 | var binSize = (int)(mu / 10); 40 | var delays = new List(iterations); 41 | var bins = CreateBins(binSize); 42 | 43 | for (int i = 0; i < iterations; i++) 44 | { 45 | var delay = random.NextGaussian(mu, sigma); 46 | delays.Add((int)delay); 47 | Bucketize((int)delay, binSize, bins); 48 | } 49 | 50 | PrintBins(bins); 51 | PrintStats(mu, sigma, delays); 52 | } 53 | 54 | [Explicit] 55 | [Test] 56 | [TestCase(3500, 350)] 57 | public void Verify_NextGaussian_Positive(double mu, double sigma) 58 | { 59 | var binSize = (int)(mu / 10); 60 | var delays = new List(iterations); 61 | var bins = CreateBins(binSize); 62 | 63 | for (int i = 0; i < iterations; i++) 64 | { 65 | var delay = random.NextGaussian(mu, sigma); 66 | if (delay < 0) 67 | { 68 | delay = mu; 69 | } 70 | delays.Add((int)delay); 71 | Bucketize((int)delay, binSize, bins); 72 | } 73 | 74 | PrintBins(bins); 75 | PrintStats(mu, sigma, delays); 76 | } 77 | 78 | [Explicit] 79 | [Test] 80 | [TestCase(10, "0123456789")] 81 | [TestCase(10, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_")] 82 | public void Verify_NextString(int length, string charset) 83 | { 84 | var unique = new HashSet(); 85 | for (int i = 0; i < iterations; i++) 86 | { 87 | var nextString = random.NextString(length, charset); 88 | unique.Add(nextString); 89 | } 90 | Console.WriteLine(unique.Count); 91 | } 92 | 93 | [Explicit] 94 | [Test] 95 | [TestCase(0.0d, 1.0d)] 96 | [TestCase(1.0d, 10.0d)] 97 | [TestCase(-1.0d, 1.0d)] 98 | [TestCase(0.99d, 1.00d)] 99 | [TestCase(1.0d, 1.0d)] 100 | public void Verify_NextDouble(double minValue, double maxValue) 101 | { 102 | for (int i = 0; i < 1_000; i++) 103 | { 104 | var nextDouble = random.NextDouble(minValue, maxValue); 105 | Console.WriteLine(nextDouble); 106 | } 107 | } 108 | 109 | [Test] 110 | [TestCase(2.0d, 1.0d)] 111 | public void Verify_NextDouble_Throws(double minValue, double maxValue) 112 | { 113 | Assert.Throws(() => 114 | random.NextDouble(minValue, maxValue)); 115 | } 116 | 117 | [Explicit] 118 | [Test] 119 | [TestCase(0.0d, 1.0d)] 120 | [TestCase(1.0d, 10.0d)] 121 | [TestCase(-1.0d, 1.0d)] 122 | [TestCase(0.99d, 1.00d)] 123 | [TestCase(1.0d, 1.0d)] 124 | public void Verify_NextDecimal(double minValue, double maxValue) 125 | { 126 | for (int i = 0; i < 1_000; i++) 127 | { 128 | var nextDecimal = random.NextDecimal((decimal)minValue, (decimal)maxValue); 129 | Console.WriteLine(nextDecimal); 130 | } 131 | } 132 | 133 | [Test] 134 | [TestCase(2.0d, 1.0d)] 135 | public void Verify_NextDecimal_Throws(double minValue, double maxValue) 136 | { 137 | Assert.Throws(() => 138 | random.NextDecimal((decimal)minValue, (decimal)maxValue)); 139 | } 140 | 141 | private Dictionary CreateBins(int binSize) 142 | { 143 | var bins = new Dictionary(); 144 | for (int i = 0; i < 50; i++) 145 | { 146 | var bucket = binSize * i; 147 | bins.Add(bucket, 0); 148 | } 149 | 150 | return bins; 151 | } 152 | 153 | private void PrintBins(Dictionary bins) 154 | { 155 | var padding = bins.Max(x => x.Key).ToString().Length; 156 | foreach (var item in bins.OrderBy(x => x.Key)) 157 | { 158 | Console.WriteLine($"{item.Key.ToString().PadLeft(padding)}: {item.Value}"); 159 | } 160 | } 161 | 162 | private void PrintStats(double mu, double sigma, List delays) 163 | { 164 | Console.WriteLine($"mu: {mu}, sigma: {sigma}"); 165 | var avg = Average(delays); 166 | var p95 = Percentile(delays, 0.95); 167 | var p99 = Percentile(delays, 0.99); 168 | Console.WriteLine($"avg: {avg}"); 169 | Console.WriteLine($"p95: {p95}"); 170 | Console.WriteLine($"p99: {p99}"); 171 | } 172 | 173 | private void Bucketize(int n, int binSize, IDictionary bins) 174 | { 175 | var bin = Bin(n, binSize); 176 | bins[bin]++; 177 | } 178 | 179 | private int Bin(int n, int binSize) 180 | { 181 | return n / binSize * binSize; 182 | } 183 | 184 | public double Percentile(IEnumerable source, double percentile) 185 | { 186 | var elements = source.ToArray(); 187 | Array.Sort(elements); 188 | double realIndex = percentile * (elements.Length - 1); 189 | int index = (int)realIndex; 190 | double indexDelta = realIndex - index; 191 | if (index + 1 < elements.Length) 192 | return elements[index] * (1 - indexDelta) + elements[index + 1] * indexDelta; 193 | else 194 | return elements[index]; 195 | } 196 | 197 | private double Average(IEnumerable source) 198 | { 199 | return source.Average(); 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/Sample/ComparableClass.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace rm.ExtensionsTest.Sample; 4 | 5 | public class ComparableClass : IComparable 6 | { 7 | public int CompareTo(ComparableClass other) 8 | { 9 | throw new NotImplementedException(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/Sample/ComparableClass2.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace rm.ExtensionsTest.Sample; 4 | 5 | public class ComparableClass2 : IComparable, IEquatable 6 | { 7 | public int Value { get; set; } 8 | 9 | public int CompareTo(ComparableClass2 other) 10 | { 11 | if (other == null) 12 | { 13 | return 1; 14 | } 15 | return Value.CompareTo(other.Value); 16 | } 17 | 18 | public override string ToString() 19 | { 20 | return Value.ToString(); 21 | } 22 | 23 | public bool Equals(ComparableClass2 other) 24 | { 25 | if (other == null) 26 | { 27 | return false; 28 | } 29 | return Value.Equals(other.Value); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/Sample/ComparableClass2Comparer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace rm.ExtensionsTest.Sample; 4 | 5 | public class ComparableClass2Comparer : IComparer 6 | { 7 | public int Compare(ComparableClass2 x, ComparableClass2 y) 8 | { 9 | return x.Value.CompareTo(y.Value); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/Sample/SampleClass.cs: -------------------------------------------------------------------------------- 1 | namespace rm.ExtensionsTest.Sample; 2 | 3 | public class SampleClass 4 | { 5 | public ComparableClass2 ComparableClass2 { get; set; } 6 | } 7 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/StopwatchExtensionTest.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using NUnit.Framework; 3 | using rm.Extensions; 4 | 5 | namespace rm.ExtensionsTest; 6 | 7 | [TestFixture] 8 | public class StopwatchExtensionTest 9 | { 10 | [Test] 11 | public void Elaspsed_Compile01() 12 | { 13 | // flaky tests as dependent on time so just put calls for compile safety. 14 | var sw = Stopwatch.StartNew(); 15 | //Assert.AreEqual(sw.ElapsedTicks, sw.ElapsedTicks()); 16 | sw.ElapsedTicks(); 17 | //Assert.AreEqual(sw.ElapsedMilliseconds, sw.ElapsedMilliseconds()); 18 | sw.ElapsedMilliseconds(); 19 | //Assert.AreEqual(sw.ElapsedMilliseconds() / 1000, sw.ElapsedSeconds()); 20 | sw.ElapsedSeconds(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/StringBuilderExtensionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | using NUnit.Framework; 5 | using rm.Extensions; 6 | 7 | namespace rm.ExtensionsTest; 8 | 9 | [TestFixture] 10 | public class StringBuilderExtensionTest 11 | { 12 | private static readonly string CRLF = Environment.NewLine; 13 | private static readonly string LFCR = new string(Environment.NewLine.ToCharArray().Reverse().ToArray()); 14 | 15 | [Test] 16 | [TestCase("this {0} {1} test.", "is", "a")] 17 | [TestCase("this test.")] 18 | [TestCase("this ->{0}<- test.", null, null)] 19 | public void AppendLine01(string format, params object[] args) 20 | { 21 | var result = new StringBuilder().AppendLine(format, args).ToString(); 22 | Console.WriteLine(result); 23 | Assert.AreEqual(string.Format(format, args) + Environment.NewLine, result); 24 | } 25 | 26 | [Test] 27 | [TestCase("this", "siht")] 28 | [TestCase("this test", "tset siht")] 29 | [TestCase("0123456789", "9876543210")] 30 | [TestCase("t", "t")] 31 | [TestCase("", "")] 32 | [TestCase("this{CRLF}test", "tset{LFCR}siht")] 33 | [TestCase("Les Misérables", "selbarésiM seL")] 34 | [TestCase("Les Mise\u0301rables", "selbaŕesiM seL")] 35 | public void Reverse01(string input, string expected) 36 | { 37 | var actual = new StringBuilder(input.Replace("{CRLF}", CRLF)).Reverse().ToString(); 38 | Assert.AreEqual(expected.Replace("{LFCR}", LFCR), actual); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/ThrowExtensionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using rm.Extensions; 4 | 5 | namespace rm.ExtensionsTest; 6 | 7 | [TestFixture] 8 | public class ThrowExtensionTest 9 | { 10 | [Test] 11 | [TestCase((object)null)] 12 | public void ThrowIfNull01(object o) 13 | { 14 | Assert.Throws(() => { o.ThrowIfNull(); }); 15 | } 16 | 17 | [Test] 18 | public void ThrowIfNull02() 19 | { 20 | Assert.DoesNotThrow(() => { new object().ThrowIfNull(); }); 21 | } 22 | 23 | [Test] 24 | [TestCase((object)null, "ex message")] 25 | public void ThrowIfNull03(object o, string m) 26 | { 27 | try 28 | { 29 | o.ThrowIfNull(m); 30 | Assert.Fail(); 31 | } 32 | catch (NullReferenceException ex) 33 | { 34 | Assert.IsTrue(ex.Message.Contains(m)); 35 | } 36 | catch (Exception) 37 | { 38 | Assert.Fail(); 39 | } 40 | } 41 | 42 | [Test] 43 | public void ThrowIfNull04() 44 | { 45 | Assert.Throws(() => 46 | { 47 | new[] { new object(), null }.ThrowIfAnyNull(); 48 | }); 49 | } 50 | 51 | [Test] 52 | public void ThrowIfNull05() 53 | { 54 | Assert.DoesNotThrow(() => 55 | { 56 | new[] { new object(), new object() }.ThrowIfAnyNull(); 57 | }); 58 | } 59 | 60 | [Test] 61 | [TestCase((object)null)] 62 | public void ThrowIfArgumentNull01(object o) 63 | { 64 | Assert.Throws(() => { o.ThrowIfArgumentNull(); }); 65 | } 66 | 67 | [Test] 68 | public void ThrowIfArgumentNull02() 69 | { 70 | Assert.DoesNotThrow(() => { new object().ThrowIfArgumentNull(); }); 71 | } 72 | 73 | [TestCase((object)null, "ex message")] 74 | public void ThrowIfArgumentNull03(object o, string m) 75 | { 76 | try 77 | { 78 | o.ThrowIfArgumentNull(m); 79 | Assert.Fail(); 80 | } 81 | catch (ArgumentNullException ex) 82 | { 83 | Assert.IsTrue(ex.Message.Contains(m)); 84 | } 85 | catch (Exception) 86 | { 87 | Assert.Fail(); 88 | } 89 | } 90 | 91 | [Test] 92 | public void ThrowIfArgumentNull04() 93 | { 94 | Assert.Throws(() => 95 | { 96 | new[] { new object(), null }.ThrowIfAnyArgumentNull(); 97 | }); 98 | } 99 | 100 | [Test] 101 | public void ThrowIfArgumentNull05() 102 | { 103 | Assert.DoesNotThrow(() => 104 | { 105 | new[] { new object(), new object() }.ThrowIfAnyArgumentNull(); 106 | }); 107 | } 108 | 109 | [Test] 110 | [TestCase((string)null)] 111 | public void ThrowIfNullOrEmpty01a(string s) 112 | { 113 | Assert.Throws(() => { s.ThrowIfNullOrEmpty(); }); 114 | } 115 | 116 | [Test] 117 | [TestCase("")] 118 | public void ThrowIfNullOrEmpty01b(string s) 119 | { 120 | Assert.Throws(() => { s.ThrowIfNullOrEmpty(); }); 121 | } 122 | 123 | [Test] 124 | [TestCase("s")] 125 | [TestCase(" ")] 126 | public void ThrowIfNullOrEmpty02(string s) 127 | { 128 | Assert.DoesNotThrow(() => { s.ThrowIfNullOrEmpty(); }); 129 | } 130 | 131 | [Test] 132 | public void ThrowIfNullOrEmpty03() 133 | { 134 | Assert.Throws(() => 135 | { 136 | new[] { "s1", null }.ThrowIfNullOrEmpty(); 137 | }); 138 | } 139 | 140 | [Test] 141 | [TestCase((string)null)] 142 | public void ThrowIfNullOrEmptyArgument01a(string s) 143 | { 144 | Assert.Throws(() => { s.ThrowIfNullOrEmptyArgument(); }); 145 | } 146 | 147 | [Test] 148 | [TestCase("")] 149 | public void ThrowIfNullOrEmptyArgument01b(string s) 150 | { 151 | Assert.Throws(() => { s.ThrowIfNullOrEmptyArgument(); }); 152 | } 153 | 154 | [Test] 155 | [TestCase("s")] 156 | [TestCase(" ")] 157 | public void ThrowIfNullOrEmptyArgument02(string s) 158 | { 159 | Assert.DoesNotThrow(() => { s.ThrowIfNullOrEmptyArgument(); }); 160 | } 161 | 162 | [Test] 163 | public void ThrowIfNullOrEmptyArgument03() 164 | { 165 | Assert.Throws(() => 166 | { 167 | new[] { "s1", null }.ThrowIfNullOrEmptyArgument(); 168 | }); 169 | } 170 | 171 | [Test] 172 | [TestCase((string)null)] 173 | public void ThrowIfNullOrWhiteSpace01a(string s) 174 | { 175 | Assert.Throws(() => { s.ThrowIfNullOrWhiteSpace(); }); 176 | } 177 | 178 | [Test] 179 | [TestCase("")] 180 | [TestCase(" ")] 181 | public void ThrowIfNullOrWhiteSpace01b(string s) 182 | { 183 | Assert.Throws(() => { s.ThrowIfNullOrWhiteSpace(); }); 184 | } 185 | 186 | [Test] 187 | [TestCase("s")] 188 | public void ThrowIfNullOrWhiteSpace02(string s) 189 | { 190 | Assert.DoesNotThrow(() => { s.ThrowIfNullOrWhiteSpace(); }); 191 | } 192 | 193 | [Test] 194 | [TestCase((string)null)] 195 | public void ThrowIfNullOrWhiteSpaceArgument01a(string s) 196 | { 197 | Assert.Throws(() => { s.ThrowIfNullOrWhiteSpaceArgument(); }); 198 | } 199 | 200 | [Test] 201 | [TestCase("")] 202 | [TestCase(" ")] 203 | public void ThrowIfNullOrWhiteSpaceArgument01b(string s) 204 | { 205 | Assert.Throws(() => { s.ThrowIfNullOrWhiteSpaceArgument(); }); 206 | } 207 | 208 | [Test] 209 | [TestCase("s")] 210 | public void ThrowIfNullOrWhiteSpaceArgument02(string s) 211 | { 212 | Assert.DoesNotThrow(() => { s.ThrowIfNullOrWhiteSpaceArgument(); }); 213 | } 214 | 215 | [Test] 216 | public void IsNotVoidReturn() 217 | { 218 | Assert.IsNotNull(new object().ThrowIfNull()); 219 | Assert.IsNotNull(new object().ThrowIfArgumentNull()); 220 | Assert.IsNotNull(new[] { new object() }.ThrowIfAnyNull()); 221 | Assert.IsNotNull(new[] { new object() }.ThrowIfAnyArgumentNull()); 222 | Assert.IsNotNull("s".ThrowIfNullOrEmpty()); 223 | Assert.IsNotNull("s".ThrowIfNullOrEmptyArgument()); 224 | Assert.IsNotNull(new[] { "s", "t" }.ThrowIfNullOrEmpty()); 225 | Assert.IsNotNull(new[] { "s", "t" }.ThrowIfNullOrEmptyArgument()); 226 | Assert.IsNotNull("s".ThrowIfNullOrWhiteSpace()); 227 | Assert.IsNotNull("s".ThrowIfNullOrWhiteSpaceArgument()); 228 | Assert.IsNotNull(0.ThrowIfArgumentOutOfRange()); 229 | Assert.IsNotNull(((uint)0).ThrowIfArgumentOutOfRange()); 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/TimeSpanExtensionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using rm.Extensions; 4 | 5 | namespace rm.ExtensionsTest; 6 | 7 | [TestFixture] 8 | public class TimeSpanExtensionTest 9 | { 10 | [Test] 11 | [TestCase(0, 0, 0, 0, 10, "10ms")] 12 | [TestCase(0, 0, 0, 10, 10, "10s")] 13 | [TestCase(0, 0, 10, 10, 10, "10m")] 14 | [TestCase(0, 10, 10, 10, 10, "10h")] 15 | [TestCase(6, 10, 10, 10, 10, "6d")] 16 | [TestCase(10, 10, 10, 10, 10, "1wk")] 17 | [TestCase(30, 10, 10, 10, 10, "1mth")] 18 | [TestCase(365, 120, 0, 0, 0, "1y")] 19 | [TestCase(0, 0, 0, 0, 999, "999ms")] 20 | [TestCase(0, 0, 0, 0, 1000, "1s")] 21 | [TestCase(20, 10, 10, 10, 10, "2wk")] 22 | public void Round01(int d, int h, int m, int s, int ms, string result) 23 | { 24 | var ts = new TimeSpan(d, h, m, s, ms); 25 | Assert.AreEqual(result, ts.Round()); 26 | } 27 | 28 | [Test] 29 | [TestCase(1.9d, "1s")] 30 | [TestCase(1.009d, "1s")] 31 | public void Round02(double s, string result) 32 | { 33 | var ts = TimeSpan.FromSeconds(s); 34 | Assert.AreEqual(result, ts.Round()); 35 | } 36 | 37 | [Test] 38 | [TestCase(10)] 39 | [TestCase(100)] 40 | [TestCase(1000)] 41 | public void Days01(int n) 42 | { 43 | Assert.AreEqual(n, n.Days().TotalDays); 44 | } 45 | 46 | [Test] 47 | [TestCase(10)] 48 | [TestCase(100)] 49 | [TestCase(1000)] 50 | public void Hours01(int n) 51 | { 52 | Assert.AreEqual(n, n.Hours().TotalHours); 53 | } 54 | 55 | [Test] 56 | [TestCase(10)] 57 | [TestCase(100)] 58 | [TestCase(1000)] 59 | public void Minutes01(int n) 60 | { 61 | Assert.AreEqual(n, n.Minutes().TotalMinutes); 62 | } 63 | 64 | [Test] 65 | [TestCase(10)] 66 | [TestCase(100)] 67 | [TestCase(1000)] 68 | public void Seconds01(int n) 69 | { 70 | Assert.AreEqual(n, n.Seconds().TotalSeconds); 71 | } 72 | 73 | [Test] 74 | [TestCase(10)] 75 | [TestCase(100)] 76 | [TestCase(1000)] 77 | public void Milliseconds01(int n) 78 | { 79 | Assert.AreEqual(n, n.Milliseconds().TotalMilliseconds); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/UriExtensionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using rm.Extensions; 4 | 5 | namespace rm.ExtensionsTest; 6 | 7 | [TestFixture] 8 | public class UriExtensionTest 9 | { 10 | [Test] 11 | [TestCase("http://rmandvikar.github.io/images/ds.jpg", Hasher.sha1, "9ca6c1c83f07a42dc5549bc2ba0b2079686d0e12")] 12 | [TestCase("http://rmandvikar.github.io/images/ds.jpg", Hasher.md5, "131b39ae30d4d55db34f5a03cd6ce1f8")] 13 | [TestCase(@"./images/d s .jpg", Hasher.sha1, "9ca6c1c83f07a42dc5549bc2ba0b2079686d0e12")] 14 | [TestCase(@"./images/d s .jpg", Hasher.md5, "131b39ae30d4d55db34f5a03cd6ce1f8")] 15 | public void Checksum01(string url_or_path, Hasher type, string hash) 16 | { 17 | Uri uri; 18 | if (url_or_path.StartsWith(".")) 19 | { 20 | var path = url_or_path; 21 | uri = new Uri(new Uri(System.Reflection.Assembly.GetExecutingAssembly().Location), path); 22 | } 23 | else 24 | { 25 | var url = url_or_path; 26 | uri = new Uri(url); 27 | } 28 | var h = uri.Checksum(type); 29 | Console.WriteLine(h); 30 | Assert.AreEqual(hash, h); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/WrappedExtensionTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using rm.Extensions; 3 | 4 | namespace rm.ExtensionsTest; 5 | 6 | [TestFixture] 7 | public class WrappedExtensionTest 8 | { 9 | [Test] 10 | public void WrapInt01() 11 | { 12 | var wi = 1.Wrap(); 13 | var wiReference = wi; 14 | Assert.AreEqual(1, wi.Value); 15 | Assert.IsTrue(ReferenceEquals(wi, wiReference)); 16 | wi.Value = 5; 17 | Assert.AreEqual(5, wi.Value); 18 | Assert.IsTrue(ReferenceEquals(wi, wiReference)); 19 | Change(wi); 20 | Assert.AreEqual(9, wi.Value); 21 | Assert.IsTrue(ReferenceEquals(wi, wiReference)); 22 | } 23 | 24 | private void Change(Wrapped wi) 25 | { 26 | wi.Value = 9; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/images/d s .jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmandvikar/csharp-extensions/a23a4b4bc64f5ab22ade49d048788f5a509326f0/tests/rm.ExtensionsTest/images/d s .jpg -------------------------------------------------------------------------------- /tests/rm.ExtensionsTest/rm.ExtensionsTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0;net8.0;net6.0;net7.0;netcoreapp3.1;net48 5 | latest 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | PreserveNewest 22 | 23 | 24 | 25 | 26 | --------------------------------------------------------------------------------