├── .gitattributes ├── .gitignore ├── LICENSE.md ├── README.md ├── azure-pipelines.yml └── src ├── CouponCodeBuilder.sln ├── Powells.CouponCode ├── CouponCodeBuilder.cs ├── Options.cs ├── Powell.CouponCode.csproj ├── Powell.CouponCode.nuspec └── SecureRandom.cs └── Powells.CouponCodeTests ├── CouponTests.cs └── Powell.CouponCodeTests.csproj /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.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 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | License: http://opensource.org/licenses/MIT -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Logo](https://repository-images.githubusercontent.com/44112601/e7646680-3302-11eb-91df-9962cfb40da5) 2 | 3 | # csharp-algorithm-coupon-code 4 | Implementation of Perl's Algorithm::CouponCode in C# 5 | 6 | ``` 7 | Install-Package Powell.CouponCode -Version 1.0.3 8 | ``` 9 | 10 | Supports net35, net40, net45 and netstandard20. netstandard<20 not supported due to System.Security.Cryptography deficit. 11 | 12 | Port of http://search.cpan.org/dist/Algorithm-CouponCode/ 13 | 14 | Copyright 2011 Grant McLean grantm@cpan.org 15 | 16 | With nods to the various other language ports for guidance: 17 | - https://github.com/baxang/coupon-code/blob/master/lib/coupon_code.rb 18 | - https://github.com/chilts/node-coupon-code/blob/master/coupon-code.js 19 | 20 | ## Synopsis 21 | A 'Coupon Code' is made up of letters and numbers grouped into 4 character 22 | 'parts'. For example, a 3-part code might look like this: 23 | 24 | 1K7Q-CTFM-LMTC 25 | 26 | Coupon Codes are random codes which are easy for the recipient to type 27 | accurately into a web form. An example application might be to print a code on 28 | a letter to a customer who would then enter the code as part of the 29 | registration process for web access to their account. 30 | 31 | Features of the codes that make them well suited to manual transcription: 32 | - The codes are not case sensitive. 33 | - Not all letters and numbers are used, so if a person enters the letter 'O' we 34 | can automatically correct it to the digit '0' (similarly for I => 1, S => 5, Z => 2). 35 | - The 4th character of each part is a checkdigit, so client-side scripting can 36 | be used to highlight parts which have been mis-typed, before the code is even 37 | submitted to the application's back-end validation. 38 | - The checkdigit algorithm takes into account the position of the part being 39 | keyed. So for example '1K7Q' might be valid in the first part but not in the 40 | second so if a user typed the parts in the wrong boxes then their error could 41 | be highlighted. 42 | - The code generation algorithm avoids 'undesirable' codes. For example any code 43 | in which transposed characters happen to result in a valid checkdigit will be 44 | skipped. Any generated part which happens to spell an 'inappropriate' 4-letter 45 | word (e.g.: 'P00P') will also be skipped. 46 | - The code returned by Generate() is random, but not necessarily unique. 47 | If your application requires unique codes, it is your responsibility to 48 | avoid duplicates (for example by using a unique index on your database column). 49 | - The codes are generated using a SHA1 cryptographic hash of a plaintext. If you 50 | do not supply a plaintext, one will be generated for you (using RNGCryptoServiceProvider). 51 | In the event that an 'inappropriate' code is created, the generated hash will be 52 | used as a plaintext input for generating a new hash and the process will be repeated. 53 | - Each 4-character part encodes 15 bits of random data, so a 3-part code will 54 | incorporate 45 bits making a total of 2^45 (approximately 35 trillion) unique 55 | codes. 56 | 57 | ## string Generate(Options opts) method: 58 | Returns a coupon code as a string of 4-character parts separated by '-' 59 | characters. The following optional named parameters may be supplied: 60 | 61 | Example: 62 | ``` 63 | var opts = new Options(); 64 | var ccb = new CouponCodeBuilder(); 65 | var badWords = ccb.BadWordsList; 66 | var code = ccb.Generate(opts); 67 | // code example EED9-128L-BJUQ 68 | ``` 69 | 70 | ### Parts 71 | The number of parts desired. Must be a number in the range 1 - 6. Default is 3. 72 | 73 | ### Plaintext 74 | A byte string which will be hashed using Digest::SHA to produce the code. 75 | If you do not supply your own plaintext then a random one will be generated for you. 76 | 77 | ## string Validate(string code, Options opts) 78 | 79 | Takes a code, cleans it up and validates the checkdigits. Returns the 80 | normalised (and untainted) version of the code on success or undef on error. 81 | The following named parameters may be supplied: 82 | 83 | ### Code 84 | The code to be validated. The parameter is mandatory. 85 | 86 | ### Parts 87 | The number of parts you expect the code to contain. Default is 3. 88 | 89 | 90 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # ASP.NET Core (.NET Framework) 2 | # Build and test ASP.NET Core projects targeting the full .NET Framework. 3 | # Add steps that publish symbols, save build artifacts, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core 5 | 6 | trigger: 7 | - master 8 | 9 | pool: 10 | vmImage: 'windows-latest' 11 | 12 | variables: 13 | solution: '**/*.sln' 14 | buildPlatform: 'Any CPU' 15 | buildConfiguration: 'Release' 16 | 17 | steps: 18 | - task: NuGetToolInstaller@1 19 | 20 | - task: NuGetCommand@2 21 | inputs: 22 | restoreSolution: '$(solution)' 23 | 24 | - task: VSBuild@1 25 | inputs: 26 | solution: '$(solution)' 27 | msbuildArgs: '/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:DesktopBuildPackageLocation="$(build.artifactStagingDirectory)\WebApp.zip" /p:DeployIisAppPath="Default Web Site"' 28 | platform: '$(buildPlatform)' 29 | configuration: '$(buildConfiguration)' 30 | 31 | - task: VSTest@2 32 | inputs: 33 | platform: '$(buildPlatform)' 34 | configuration: '$(buildConfiguration)' 35 | -------------------------------------------------------------------------------- /src/CouponCodeBuilder.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.271 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{91E149F2-0089-4CD9-85AD-7E150F836D7F}" 7 | ProjectSection(SolutionItems) = preProject 8 | LICENSE.md = LICENSE.md 9 | README.md = README.md 10 | EndProjectSection 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Powell.CouponCode", "Powells.CouponCode\Powell.CouponCode.csproj", "{2B876823-AC3A-45CE-A39F-D2E1A78B8A72}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Powell.CouponCodeTests", "Powells.CouponCodeTests\Powell.CouponCodeTests.csproj", "{CEBEF9EA-AC41-464C-95A9-C3B9BAEAB6C8}" 15 | EndProject 16 | Global 17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 18 | Debug|Any CPU = Debug|Any CPU 19 | Release|Any CPU = Release|Any CPU 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {2B876823-AC3A-45CE-A39F-D2E1A78B8A72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {2B876823-AC3A-45CE-A39F-D2E1A78B8A72}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {2B876823-AC3A-45CE-A39F-D2E1A78B8A72}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {2B876823-AC3A-45CE-A39F-D2E1A78B8A72}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {CEBEF9EA-AC41-464C-95A9-C3B9BAEAB6C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {CEBEF9EA-AC41-464C-95A9-C3B9BAEAB6C8}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {CEBEF9EA-AC41-464C-95A9-C3B9BAEAB6C8}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {CEBEF9EA-AC41-464C-95A9-C3B9BAEAB6C8}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {41604EEA-CF21-4305-B45C-14A19DDB80B9} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /src/Powells.CouponCode/CouponCodeBuilder.cs: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------------------------------------------------------------- 2 | // 3 | // Overview : An implementation of Perl's Algorithm::CouponCode for C#. 4 | // Copyright : (c) 2015 Rebecca Powell. 5 | // License : http://opensource.org/licenses/MIT 6 | // 7 | // Ported from CPAN Algorithm-CouponCode-1.004 8 | // Copyright 2011 Grant McLean grantm@cpan.org 9 | // http://cpansearch.perl.org/src/GRANTM/Algorithm-CouponCode-1.004/lib/Algorithm/CouponCode.pm 10 | // This program is free software; you can redistribute it and/or modify it 11 | // under the terms of either: the GNU General Public License as published 12 | // by the Free Software Foundation; or the Artistic License. 13 | // 14 | // 15 | // A 'Coupon Code' is made up of letters and numbers grouped into 4 character 16 | // 'parts'. For example, a 3-part code might look like this: 17 | // 18 | // 1K7Q-CTFM-LMTC 19 | // 20 | // Coupon Codes are random codes which are easy for the recipient to type 21 | // accurately into a web form. An example application might be to print a code on 22 | // a letter to a customer who would then enter the code as part of the 23 | // registration process for web access to their account. 24 | // 25 | // Features of the codes that make them well suited to manual transcription: 26 | // - The codes are not case sensitive. 27 | // - Not all letters and numbers are used, so if a person enters the letter 'O' we 28 | // can automatically correct it to the digit '0' (similarly for I => 1, S => 5, Z => 2). 29 | // - The 4th character of each part is a checkdigit, so client-side scripting can 30 | // be used to highlight parts which have been mis-typed, before the code is even 31 | // submitted to the application's back-end validation. 32 | // - The checkdigit algorithm takes into account the position of the part being 33 | // keyed. So for example '1K7Q' might be valid in the first part but not in the 34 | // second so if a user typed the parts in the wrong boxes then their error could 35 | // be highlighted. 36 | // - The code generation algorithm avoids 'undesirable' codes. For example any code 37 | // in which transposed characters happen to result in a valid checkdigit will be 38 | // skipped. Any generated part which happens to spell an 'inappropriate' 4-letter 39 | // word (e.g.: 'P00P') will also be skipped. 40 | // - The code returned by Generate() is random, but not necessarily unique. 41 | // If your application requires unique codes, it is your responsibility to 42 | // avoid duplicates (for example by using a unique index on your database column). 43 | // - The codes are generated using a SHA1 cryptographic hash of a plaintext. If you 44 | // do not supply a plaintext, one will be generated for you (using RNGCryptoServiceProvider). 45 | // In the event that an 'inappropriate' code is created, the generated hash will be 46 | // used as a plaintext input for generating a new hash and the process will be repeated. 47 | // - Each 4-character part encodes 15 bits of random data, so a 3-part code will 48 | // incorporate 45 bits making a total of 2^45 (approximately 35 trillion) unique 49 | // codes. 50 | // 51 | // string Generate(Options opts) method: 52 | // Returns a coupon code as a string of 4-character parts separated by '-' 53 | // characters. The following optional named parameters may be supplied: 54 | // 55 | // Parts 56 | // The number of parts desired. Must be a number in the range 1 - 6. Default is 3. 57 | // 58 | // Plaintext 59 | // A byte string which will be hashed using Digest::SHA to produce the code. 60 | // If you do not supply your own plaintext then a random one will be generated for you. 61 | // 62 | // string Validate(string code, Options opts) 63 | // 64 | // Takes a code, cleans it up and validates the checkdigits. Returns the 65 | // normalised (and untainted) version of the code on success or undef on error. 66 | // The following named parameters may be supplied: 67 | // 68 | // Code 69 | // The code to be validated. The parameter is mandatory. 70 | // 71 | // Parts 72 | // The number of parts you expect the code to contain. Default is 3. 73 | // 74 | // 75 | // Rebecca Powell 76 | // https://rebecca-powell.com 77 | // https://twitter.com/junto 78 | // 79 | // -------------------------------------------------------------------------------------------------------------------- 80 | 81 | using System; 82 | using System.Collections.Generic; 83 | using System.Linq; 84 | using System.Security.Cryptography; 85 | using System.Text; 86 | 87 | namespace Powell.CouponCode 88 | { 89 | /// 90 | /// The coupon code builder. 91 | /// 92 | public class CouponCodeBuilder 93 | { 94 | /// 95 | /// The symbols dictionary. 96 | /// 97 | private readonly Dictionary symbolsDictionary = new Dictionary(); 98 | 99 | /// 100 | /// The random number generator 101 | /// 102 | private readonly RandomNumberGenerator randomNumberGenerator; 103 | 104 | /// 105 | /// The symbols array. 106 | /// 107 | private char[] symbols; 108 | 109 | /// 110 | /// Initializes a new instance of the class. 111 | /// 112 | public CouponCodeBuilder() 113 | { 114 | this.BadWordsList = new List("SHPX PHAG JNAX JNAT CVFF PBPX FUVG GJNG GVGF SNEG URYY ZHSS QVPX XABO NEFR FUNT GBFF FYHG GHEQ FYNT PENC CBBC OHGG SRPX OBBO WVFZ WVMM CUNG'".Split(' ')); 115 | this.SetupSymbolsDictionary(); 116 | this.randomNumberGenerator = new SecureRandom(); 117 | } 118 | 119 | /// 120 | /// Gets or sets the bad words list. 121 | /// 122 | public List BadWordsList { get; set; } 123 | 124 | /// 125 | /// Define a delegate for your bad words list 126 | /// 127 | public Func> SetBadWordsList { get; set; } 128 | 129 | /// 130 | /// The generate. 131 | /// 132 | /// 133 | /// The opts. 134 | /// 135 | /// 136 | /// The . 137 | /// 138 | public string Generate(Options opts) 139 | { 140 | var parts = new List(); 141 | 142 | // populate the bad words list with this delegate if it was set; 143 | if (this.SetBadWordsList != null) 144 | { 145 | this.BadWordsList = this.SetBadWordsList.Invoke(); 146 | } 147 | 148 | // remove empty strings from list 149 | this.BadWordsList = this.BadWordsList.Except(new List {string.Empty}).ToList(); 150 | 151 | // if plaintext wasn't set then override 152 | if (string.IsNullOrEmpty(opts.Plaintext)) 153 | { 154 | // not yet implemented 155 | opts.Plaintext = this.GetRandomPlaintext(8); 156 | } 157 | 158 | // generate parts and combine 159 | do 160 | { 161 | for (var i = 0; i < opts.Parts; i++) 162 | { 163 | var sb = new StringBuilder(); 164 | for (var j = 0; j < opts.PartLength - 1; j++) 165 | { 166 | sb.Append(this.GetRandomSymbol()); 167 | } 168 | 169 | var part = sb.ToString(); 170 | sb.Append(this.CheckDigitAlg1(part, i + 1)); 171 | parts.Add(sb.ToString()); 172 | } 173 | } 174 | while (this.ContainsBadWord(string.Join(string.Empty, parts.ToArray()))); 175 | 176 | return string.Join("-", parts.ToArray()); 177 | } 178 | 179 | /// 180 | /// The validate. 181 | /// 182 | /// The code. 183 | /// The opts. 184 | /// 185 | /// The . 186 | /// 187 | /// Provide a code to be validated 188 | /// 189 | public string Validate(string code, Options opts) 190 | { 191 | if (string.IsNullOrEmpty(code)) 192 | { 193 | throw new Exception("Provide a code to be validated"); 194 | } 195 | 196 | // uppercase the code, replace OIZS with 0125 197 | code = new string(Array.FindAll(code.ToCharArray(), char.IsLetterOrDigit)) 198 | .ToUpper() 199 | .Replace("O", "0") 200 | .Replace("I", "1") 201 | .Replace("Z", "2") 202 | .Replace("S", "5"); 203 | 204 | // split in the different parts 205 | var parts = new List(); 206 | var tmp = code; 207 | while (tmp.Length > 0) 208 | { 209 | parts.Add(tmp.Substring(0, opts.PartLength)); 210 | tmp = tmp.Substring(opts.PartLength); 211 | } 212 | 213 | // make sure we have been given the same number of parts as we are expecting 214 | if (parts.Count != opts.Parts) 215 | { 216 | return string.Empty; 217 | } 218 | 219 | // validate each part 220 | for (var i = 0; i < parts.Count; i++) 221 | { 222 | var part = parts[i]; 223 | 224 | // check this part has 4 chars 225 | if (part.Length != opts.PartLength) 226 | { 227 | return string.Empty; 228 | } 229 | 230 | // split out the data and the check 231 | var data = part.Substring(0, opts.PartLength - 1); 232 | var check = part.Substring(opts.PartLength - 1, 1); 233 | 234 | if (Convert.ToChar(check) != this.CheckDigitAlg1(data, i + 1)) 235 | { 236 | return string.Empty; 237 | } 238 | } 239 | 240 | // everything looked ok with this code 241 | return string.Join("-", parts.ToArray()); 242 | } 243 | 244 | /// 245 | /// The get random plaintext. 246 | /// 247 | /// 248 | /// The max Size. 249 | /// 250 | /// 251 | /// The . 252 | /// 253 | private string GetRandomPlaintext(int maxSize) 254 | { 255 | var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray(); 256 | var data = new byte[1]; 257 | this.randomNumberGenerator.GetNonZeroBytes(data); 258 | 259 | data = new byte[maxSize]; 260 | this.randomNumberGenerator.GetNonZeroBytes(data); 261 | 262 | var result = new StringBuilder(maxSize); 263 | foreach (var b in data) 264 | { 265 | result.Append(chars[b % chars.Length]); 266 | } 267 | 268 | return result.ToString(); 269 | } 270 | 271 | /// 272 | /// The get random symbol. 273 | /// 274 | /// 275 | /// The . 276 | /// 277 | private char GetRandomSymbol() 278 | { 279 | var rng = new SecureRandom(); 280 | var pos = rng.Next(this.symbols.Length); 281 | return this.symbols[pos]; 282 | } 283 | 284 | /// 285 | /// The check digit algorithm 1. 286 | /// 287 | /// 288 | /// The data. 289 | /// 290 | /// 291 | /// The check. 292 | /// 293 | /// 294 | /// The . 295 | /// 296 | private char CheckDigitAlg1(string data, long check) 297 | { 298 | // check's initial value is the part number (e.g. 3 or above) 299 | // loop through the data chars 300 | Array.ForEach( 301 | data.ToCharArray(), 302 | v => 303 | { 304 | var k = this.symbolsDictionary[v]; 305 | check = (check * 19) + k; 306 | }); 307 | 308 | return this.symbols[check % (this.symbols.Length - 1)]; 309 | } 310 | 311 | /// 312 | /// The contains bad word. 313 | /// 314 | /// 315 | /// The code. 316 | /// 317 | /// 318 | /// The . 319 | /// 320 | private bool ContainsBadWord(string code) 321 | { 322 | return this.BadWordsList 323 | .Except(new List { string.Empty }) 324 | .Any(t => code.ToUpper().IndexOf(t, StringComparison.Ordinal) > -1); 325 | } 326 | 327 | /// 328 | /// The setup of the symbols dictionary. 329 | /// 330 | private void SetupSymbolsDictionary() 331 | { 332 | const string AvailableSymbols = "0123456789ABCDEFGHJKLMNPQRTUVWXY"; 333 | this.symbols = AvailableSymbols.ToCharArray(); 334 | for (var i = 0; i < this.symbols.Length; i++) 335 | { 336 | this.symbolsDictionary.Add(this.symbols[i], i); 337 | } 338 | } 339 | } 340 | } 341 | -------------------------------------------------------------------------------- /src/Powells.CouponCode/Options.cs: -------------------------------------------------------------------------------- 1 | namespace Powell.CouponCode 2 | { 3 | public class Options 4 | { 5 | public int Parts { get; set; } 6 | public int PartLength { get; set; } 7 | public string Plaintext { get; set; } 8 | 9 | public Options() 10 | { 11 | this.Parts = 3; 12 | this.PartLength = 4; 13 | this.Plaintext = "test"; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Powells.CouponCode/Powell.CouponCode.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Powell.CouponCode 5 | Coupon Code Generator 6 | net35;net40;net45;netstandard2.0 7 | Powell.CouponCode 8 | true 9 | 1.0.5 10 | Rebecca Powell 11 | Rebecca PowellRebecca Powell 12 | This is a C# port of the Perl CPAN Algorithm::CouponCode to generate and validate coupon codes or license keys. 13 | Copyright &#x00A9; Rebecca Powell. All rights reserved. 14 | MIT 15 | https://github.com/rebeccapowell/csharp-algorithm-coupon-code/ 16 | https://github.com/rebeccapowell/csharp-algorithm-coupon-code/ 17 | coupon code algorithm checksum license 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/Powells.CouponCode/Powell.CouponCode.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Powell.CouponCode 5 | 1.0.5 6 | Coupon Code Algorithm 7 | Rebecca Powell 8 | Rebecca Powell 9 | MIT 10 | https://github.com/rebeccapowell/csharp-algorithm-coupon-code/ 11 | false 12 | This is a C# port of the Perl CPAN Algorithm::CouponCode to generate and validate coupon codes or license keys. 13 | 14 | Copyright © Rebecca Powell. All rights reserved. 15 | coupon code algorithm checksum license 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/Powells.CouponCode/SecureRandom.cs: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------------------------------------------------------------- 2 | // 3 | // (c) 2015 Rebecca Powell. 4 | // Credit: http://stackoverflow.com/a/16907577/119624 5 | // Eldar Agalarov: http://stackoverflow.com/users/2449744/eldar-agalarov 6 | // 7 | // 8 | // The secure random class. 9 | // 10 | // -------------------------------------------------------------------------------------------------------------------- 11 | 12 | using System; 13 | using System.Security.Cryptography; 14 | 15 | namespace Powell.CouponCode 16 | { 17 | /// 18 | /// The secure random. 19 | /// 20 | public class SecureRandom : RandomNumberGenerator 21 | { 22 | /// 23 | /// The random number generator. 24 | /// 25 | private readonly RandomNumberGenerator rng = new RNGCryptoServiceProvider(); 26 | 27 | /// 28 | /// The next. 29 | /// 30 | /// 31 | /// The . 32 | /// 33 | public int Next() 34 | { 35 | var data = new byte[sizeof(int)]; 36 | this.rng.GetBytes(data); 37 | return BitConverter.ToInt32(data, 0) & (int.MaxValue - 1); 38 | } 39 | 40 | /// 41 | /// The next. 42 | /// 43 | /// 44 | /// The max value. 45 | /// 46 | /// 47 | /// The . 48 | /// 49 | public int Next(int maxValue) 50 | { 51 | return this.Next(0, maxValue); 52 | } 53 | 54 | /// 55 | /// The next. 56 | /// 57 | /// The min value. 58 | /// The max value. 59 | /// 60 | /// The . 61 | /// 62 | /// minValue cannot be greater than maxValue 63 | public int Next(int minValue, int maxValue) 64 | { 65 | if (minValue > maxValue) 66 | { 67 | throw new ArgumentOutOfRangeException("minValue", minValue, "minValue cannot be greater than maxValue"); 68 | } 69 | 70 | return (int)Math.Floor((minValue + ((double)maxValue) - minValue) * this.NextDouble()); 71 | } 72 | 73 | /// 74 | /// The next double. 75 | /// 76 | /// 77 | /// The . 78 | /// 79 | public double NextDouble() 80 | { 81 | var data = new byte[sizeof(uint)]; 82 | this.rng.GetBytes(data); 83 | var randUint = BitConverter.ToUInt32(data, 0); 84 | return randUint / (uint.MaxValue + 1.0); 85 | } 86 | 87 | /// 88 | /// The get bytes. 89 | /// 90 | /// 91 | /// The data. 92 | /// 93 | public override void GetBytes(byte[] data) 94 | { 95 | this.rng.GetBytes(data); 96 | } 97 | 98 | /// 99 | /// The get non zero bytes. 100 | /// 101 | /// 102 | /// The data. 103 | /// 104 | public override void GetNonZeroBytes(byte[] data) 105 | { 106 | this.rng.GetNonZeroBytes(data); 107 | } 108 | 109 | /// 110 | /// The get unique key. 111 | /// 112 | /// 113 | /// The max size. 114 | /// 115 | /// 116 | /// The . 117 | /// 118 | 119 | } 120 | } -------------------------------------------------------------------------------- /src/Powells.CouponCodeTests/CouponTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Globalization; 6 | using System.Linq; 7 | using NUnit.Framework; 8 | using Powell.CouponCode; 9 | 10 | namespace Powell.CouponCodeTests 11 | { 12 | /// 13 | /// The coupon tests. 14 | /// 15 | public class CouponCodeTests 16 | { 17 | [Test] 18 | public static void BadWordsListCanBeSetByFunction() 19 | { 20 | var opts = new Options 21 | { 22 | Parts = 10, 23 | PartLength = 5 24 | }; 25 | var ccb = new CouponCodeBuilder 26 | { 27 | // delegate defined, should invoke 28 | // useful for setting this list using an external data source 29 | SetBadWordsList = () => new List {"DAMN", "NSFW"} 30 | }; 31 | 32 | var code = ccb.Generate(opts); 33 | var badWords = ccb.BadWordsList; 34 | var output = ccb.Validate(code, opts); 35 | 36 | Console.WriteLine(code); 37 | 38 | // check code and validation are the same 39 | Assert.That(badWords, Has.Member("DAMN")); 40 | Assert.That(badWords, Has.Member("NSFW")); 41 | Assert.AreEqual(code, output, "Expected test case to ensure that the generated code and validated code match."); 42 | 43 | } 44 | 45 | [Test] 46 | public static void BadWordsListSetAndCheckedForEmptyStrings() 47 | { 48 | var opts = new Options 49 | { 50 | Parts = 10, 51 | PartLength = 5 52 | }; 53 | var ccb = new CouponCodeBuilder 54 | { 55 | BadWordsList = new List {"DAMN", "NSFW", string.Empty} 56 | }; 57 | 58 | var code = ccb.Generate(opts); 59 | var badWords = ccb.BadWordsList; 60 | var output = ccb.Validate(code, opts); 61 | 62 | Console.WriteLine(code); 63 | 64 | // check code and validation are the same 65 | Assert.That(badWords, Has.Member("DAMN")); 66 | Assert.That(badWords, Has.Member("NSFW")); 67 | Assert.AreEqual(code, output, "Expected test case to ensure that the generated code and validated code match."); 68 | 69 | } 70 | 71 | /// 72 | /// The generate valid coupon codes also validates. 73 | /// 74 | /// 75 | /// The counter. 76 | /// 77 | [Test, TestCaseSource("GenerateTestCases")] 78 | public void GenerateValidCouponCodesAlsoValidates(int counter) 79 | { 80 | var opts = new Options(); 81 | var ccb = new CouponCodeBuilder(); 82 | var badWords = ccb.BadWordsList; 83 | 84 | var code = ccb.Generate(opts); 85 | var output = ccb.Validate(code, opts); 86 | 87 | Console.WriteLine(code); 88 | 89 | // check code and validation are the same 90 | Assert.IsNotNull(output, string.Format("Expected test case {0} to be not null or empty.", counter)); 91 | Assert.AreEqual(code, output, string.Format("Expected test case {0} to ensure that the generated code and validated code match.", counter)); 92 | 93 | // assert no bad words 94 | var parts = output.Split('-'); 95 | var contains = badWords.Any(part => parts.Any(item => part.ToUpperInvariant().Contains(item.ToUpperInvariant()))); 96 | Assert.IsFalse(contains, string.Format("Expected test case {0} to contain no bad words.", counter)); 97 | } 98 | 99 | /// 100 | /// The generate valid coupon codes also validates. 101 | /// 102 | /// 103 | /// The counter. 104 | /// 105 | [Test, TestCaseSource("GenerateTestCases")] 106 | public void GenerateLongerValidCouponCodesAlsoValidates(int counter) 107 | { 108 | var opts = new Options { PartLength = 10 }; 109 | var ccb = new CouponCodeBuilder(); 110 | var badWords = ccb.BadWordsList; 111 | 112 | var code = ccb.Generate(opts); 113 | var output = ccb.Validate(code, opts); 114 | 115 | Console.WriteLine(code); 116 | 117 | // check code and validation are the same 118 | Assert.IsNotNull(output, string.Format("Expected test case {0} to be not null or empty.", counter)); 119 | Assert.AreEqual(code, output, string.Format("Expected test case {0} to ensure that the generated code and validated code match.", counter)); 120 | 121 | // assert no bad words 122 | var parts = output.Split('-'); 123 | var contains = badWords.Any(part => parts.Any(item => part.ToUpperInvariant().Contains(item.ToUpperInvariant()))); 124 | Assert.IsFalse(contains, string.Format("Expected test case {0} to contain no bad words.", counter)); 125 | } 126 | 127 | /// 128 | /// The validate pre-generated coupon codes has no errors. 129 | /// 130 | /// 131 | /// The code. 132 | /// 133 | [Test, TestCaseSource("GenerateValidationTestCases")] 134 | public void ValidatePregeneratedCouponCodesHasNoErrors(string code) 135 | { 136 | var opts = new Options(); 137 | var ccb = new CouponCodeBuilder(); 138 | var output = ccb.Validate(code, opts); 139 | 140 | // check code and validation are the same 141 | Assert.IsNotNull(output, string.Format("Expected test case {0} to be not null or empty.", code)); 142 | Assert.AreEqual(code, output, string.Format("Expected test case {0} to ensure that the generated code and validated code match.", code)); 143 | } 144 | 145 | /// 146 | /// The invalid coupon code return empty string. 147 | /// 148 | [Test] 149 | public void InvalidCouponCodeReturnEmptyString() 150 | { 151 | var opts = new Options(); 152 | var ccb = new CouponCodeBuilder(); 153 | 154 | // valid is "9Y46-9M8E-UQB8" 155 | var output = ccb.Validate("9Y46-9M8E-UQBA", opts); 156 | 157 | // check output is empty 158 | Assert.IsEmpty(output); 159 | } 160 | 161 | /// 162 | /// The generate test cases. 163 | /// 164 | /// 165 | /// The . 166 | /// 167 | private static IEnumerable GenerateTestCases() 168 | { 169 | for (var i = 0; i < 500; i++) 170 | { 171 | yield return new TestCaseData(i); 172 | } 173 | } 174 | 175 | /// 176 | /// Gets the generate validation test cases. 177 | /// 178 | /// 179 | /// The . 180 | /// 181 | private static IEnumerable GenerateValidationTestCases() 182 | { 183 | yield return new TestCaseData("9Y46-9M8E-UQB8"); 184 | yield return new TestCaseData("BDW8-9HD5-RBG5"); 185 | yield return new TestCaseData("AWN6-PTW7-3275"); 186 | yield return new TestCaseData("4J1V-RTWG-E8F6"); 187 | yield return new TestCaseData("73F3-FEHA-1XNG"); 188 | yield return new TestCaseData("3WQP-BHWX-HMK7"); 189 | yield return new TestCaseData("T3MH-NG9R-GXVC"); 190 | yield return new TestCaseData("NCMF-07XQ-76A9"); 191 | yield return new TestCaseData("JCV4-H8GV-6WP5"); 192 | yield return new TestCaseData("YV9N-KLKL-LX35"); 193 | yield return new TestCaseData("JB8U-8WAQ-N9XE"); 194 | yield return new TestCaseData("EVUA-VY5P-E2NP"); 195 | yield return new TestCaseData("4HX7-PNYT-2GTN"); 196 | yield return new TestCaseData("1TC7-G6VD-DAW7"); 197 | yield return new TestCaseData("NJJ2-FV7J-305U"); 198 | yield return new TestCaseData("J870-8PQH-E97H"); 199 | yield return new TestCaseData("EU83-MCFV-5E8T"); 200 | yield return new TestCaseData("6PEM-V3Q6-G7YC"); 201 | yield return new TestCaseData("KCNJ-FD38-28BA"); 202 | yield return new TestCaseData("0Y9H-2AA8-LUBJ"); 203 | yield return new TestCaseData("4A43-KU4E-NE9T"); 204 | yield return new TestCaseData("2QQ1-PVCV-155L"); 205 | yield return new TestCaseData("5H1W-Y6JQ-CEAD"); 206 | yield return new TestCaseData("G0UE-DKK5-9P0M"); 207 | yield return new TestCaseData("LLP5-2L9B-QV1E"); 208 | yield return new TestCaseData("5AH5-MU0K-C26W"); 209 | yield return new TestCaseData("XGL2-4BLF-YA52"); 210 | yield return new TestCaseData("HDGN-VW6H-CEJM"); 211 | yield return new TestCaseData("C1AW-F43P-75N2"); 212 | yield return new TestCaseData("CNL4-WAT6-66DP"); 213 | yield return new TestCaseData("2W3D-CBDD-91U2"); 214 | yield return new TestCaseData("FLEL-74XE-8Q2N"); 215 | yield return new TestCaseData("GPD3-1KA4-1TGU"); 216 | yield return new TestCaseData("DW2F-9E96-39BJ"); 217 | yield return new TestCaseData("EYFQ-UK6Q-02XX"); 218 | yield return new TestCaseData("CPPT-Y56Q-XBVQ"); 219 | yield return new TestCaseData("886G-48QQ-4DFR"); 220 | yield return new TestCaseData("5M3E-5FHF-9CUR"); 221 | yield return new TestCaseData("EV2G-6PP7-RWXL"); 222 | yield return new TestCaseData("GHEE-E0UD-L2QM"); 223 | yield return new TestCaseData("UQET-U40C-83C5"); 224 | yield return new TestCaseData("HHPC-BFTL-QE1U"); 225 | yield return new TestCaseData("5PE1-86M1-B7BG"); 226 | yield return new TestCaseData("1H5F-XBF3-4CJ9"); 227 | yield return new TestCaseData("WV7B-PKY0-TGFT"); 228 | yield return new TestCaseData("8YQ6-N7UU-TV6V"); 229 | yield return new TestCaseData("KX0V-XHEP-3CY2"); 230 | yield return new TestCaseData("EFYF-29V7-E35R"); 231 | yield return new TestCaseData("0RL7-VKEM-833U"); 232 | yield return new TestCaseData("0U2U-D16N-AXJ6"); 233 | yield return new TestCaseData("A7RR-X575-0BTB"); 234 | yield return new TestCaseData("LYT0-BX22-CYEX"); 235 | yield return new TestCaseData("BG49-CGMP-JFG3"); 236 | yield return new TestCaseData("9CBQ-PQX1-QY7F"); 237 | yield return new TestCaseData("6MUU-C03B-AUB4"); 238 | yield return new TestCaseData("PXWD-DF14-8GXN"); 239 | yield return new TestCaseData("9QTK-AMVP-6L85"); 240 | yield return new TestCaseData("3UA2-7BG9-LTJ6"); 241 | yield return new TestCaseData("4EEU-HC72-0C04"); 242 | yield return new TestCaseData("LW31-PQPR-1TN2"); 243 | yield return new TestCaseData("GJXJ-1WCA-56D3"); 244 | yield return new TestCaseData("TLBL-KVVT-T1GM"); 245 | yield return new TestCaseData("4P0W-EUTW-32CA"); 246 | yield return new TestCaseData("PJX3-WBFE-FMV7"); 247 | yield return new TestCaseData("DV71-8MWF-XDUX"); 248 | yield return new TestCaseData("L8PR-EJBW-PLNJ"); 249 | yield return new TestCaseData("2Q08-8XJL-NC3D"); 250 | yield return new TestCaseData("3A5F-13WW-7C8V"); 251 | yield return new TestCaseData("R3FN-E3V9-7DY8"); 252 | yield return new TestCaseData("Q87U-W8JN-1GR1"); 253 | yield return new TestCaseData("XHAB-5G2K-RPMM"); 254 | yield return new TestCaseData("JNP3-MMCA-WLWM"); 255 | yield return new TestCaseData("D0PC-8QK0-YJ2U"); 256 | yield return new TestCaseData("3RRA-6AYG-6EJR"); 257 | yield return new TestCaseData("WK1L-QY00-T292"); 258 | yield return new TestCaseData("8HMG-3N86-NVCG"); 259 | yield return new TestCaseData("M573-D9P5-2B4W"); 260 | yield return new TestCaseData("N75V-22JK-22BL"); 261 | yield return new TestCaseData("2400-2BUD-D1RJ"); 262 | yield return new TestCaseData("CKVH-VMWC-HH49"); 263 | yield return new TestCaseData("YCBX-3ERT-4C5U"); 264 | yield return new TestCaseData("ENWN-AJY0-R3F7"); 265 | yield return new TestCaseData("VT8G-14QC-PR86"); 266 | yield return new TestCaseData("6AAJ-WE4W-UKPJ"); 267 | yield return new TestCaseData("4N4E-4XWD-3XMQ"); 268 | yield return new TestCaseData("7MAX-WC6Q-BQE1"); 269 | yield return new TestCaseData("UFA6-HFG6-TVH8"); 270 | yield return new TestCaseData("YA4G-GDF9-3RY1"); 271 | yield return new TestCaseData("3VAM-J45P-EME4"); 272 | yield return new TestCaseData("U9FM-NDMB-5UL6"); 273 | yield return new TestCaseData("RT6G-RAC5-QJ1A"); 274 | yield return new TestCaseData("P1C3-L123-W8T7"); 275 | yield return new TestCaseData("T9K5-UARU-REWD"); 276 | yield return new TestCaseData("B0KX-MTLL-FHFB"); 277 | yield return new TestCaseData("46G1-BVB4-KT43"); 278 | yield return new TestCaseData("3QVR-77HU-0BDW"); 279 | yield return new TestCaseData("M01T-EYN8-NAMQ"); 280 | yield return new TestCaseData("NV2M-A4GW-5KYL"); 281 | yield return new TestCaseData("VL4N-TY3C-GGQM"); 282 | yield return new TestCaseData("B5HX-B0M9-K2FP"); 283 | yield return new TestCaseData("BQ9B-W6WT-NHL1"); 284 | yield return new TestCaseData("4UET-LKVX-26UK"); 285 | yield return new TestCaseData("68KL-6EML-031L"); 286 | yield return new TestCaseData("Y2R9-ARV6-CDN6"); 287 | yield return new TestCaseData("X1PX-1UB2-BQM8"); 288 | yield return new TestCaseData("96KB-65P6-TQAJ"); 289 | yield return new TestCaseData("NAD0-R0UG-L3L5"); 290 | yield return new TestCaseData("A2WU-CC2M-6N04"); 291 | yield return new TestCaseData("EPHW-4UD2-NNYE"); 292 | yield return new TestCaseData("98YX-JDTW-REBT"); 293 | yield return new TestCaseData("0490-79YH-RXKV"); 294 | yield return new TestCaseData("D5F6-19PD-LARK"); 295 | yield return new TestCaseData("UL0W-VLQK-XL1D"); 296 | yield return new TestCaseData("L1QH-90AL-EPFC"); 297 | yield return new TestCaseData("N6FK-7MHE-277J"); 298 | yield return new TestCaseData("30JQ-XDA5-UA5F"); 299 | yield return new TestCaseData("GVPF-7BYQ-C71T"); 300 | yield return new TestCaseData("EAEU-MDMN-YPLG"); 301 | yield return new TestCaseData("7GL7-LL3Q-V4P1"); 302 | yield return new TestCaseData("6A4C-HC1U-5UWF"); 303 | yield return new TestCaseData("V0V7-MDTU-X7FV"); 304 | yield return new TestCaseData("0B66-TK0W-CLUL"); 305 | yield return new TestCaseData("K5AV-W5W7-MHC4"); 306 | yield return new TestCaseData("YJGR-GJLG-9RFC"); 307 | yield return new TestCaseData("GJWH-3626-E87W"); 308 | yield return new TestCaseData("5W5D-XHP1-6WT8"); 309 | yield return new TestCaseData("FCP1-HHC9-AHG5"); 310 | yield return new TestCaseData("KVE4-5H9E-VA65"); 311 | yield return new TestCaseData("Q6Q6-7U0J-NRN0"); 312 | yield return new TestCaseData("8YT8-TGCF-NPX1"); 313 | yield return new TestCaseData("TJKM-9JDQ-JL9W"); 314 | yield return new TestCaseData("E6NM-AGUL-CXUQ"); 315 | yield return new TestCaseData("8GR1-CH1N-35XP"); 316 | yield return new TestCaseData("NVWH-8FA6-L8L7"); 317 | yield return new TestCaseData("CM95-EWV7-923V"); 318 | yield return new TestCaseData("PYKN-7BXP-5Q0N"); 319 | yield return new TestCaseData("7A41-8A93-2BC6"); 320 | yield return new TestCaseData("U2B8-0Y7P-L6T6"); 321 | yield return new TestCaseData("WXT6-YPQC-UMW0"); 322 | yield return new TestCaseData("12RW-HDAQ-8YNL"); 323 | yield return new TestCaseData("HJQ1-P89H-EB9T"); 324 | yield return new TestCaseData("370F-JFCN-7LG2"); 325 | yield return new TestCaseData("KUCE-WJR2-UE5W"); 326 | yield return new TestCaseData("T6J9-82EB-N0FE"); 327 | yield return new TestCaseData("75E9-4YBE-B7W3"); 328 | yield return new TestCaseData("463K-N0E5-LQ5H"); 329 | yield return new TestCaseData("MVTR-E2D6-6YVH"); 330 | yield return new TestCaseData("H3NQ-J7WB-5ALQ"); 331 | yield return new TestCaseData("2HNM-0V3Q-TYJ4"); 332 | yield return new TestCaseData("8RE6-8PNF-D7R8"); 333 | yield return new TestCaseData("YFN5-8XX1-VP1X"); 334 | yield return new TestCaseData("AEV6-RFE9-UEPG"); 335 | yield return new TestCaseData("1YDA-LVAV-TJPA"); 336 | yield return new TestCaseData("VJXA-R67H-UJV4"); 337 | yield return new TestCaseData("378P-64E9-LN49"); 338 | yield return new TestCaseData("XH34-YJP9-R5KJ"); 339 | yield return new TestCaseData("QKL1-YWCM-X5X5"); 340 | yield return new TestCaseData("W4AP-2H4B-AQ75"); 341 | yield return new TestCaseData("H7WE-L1LM-1RWM"); 342 | yield return new TestCaseData("6F6G-R88R-DT9C"); 343 | yield return new TestCaseData("N25T-U8NH-ENJU"); 344 | yield return new TestCaseData("VKHG-AC9K-F7XN"); 345 | yield return new TestCaseData("JR6C-2EBP-MKWV"); 346 | yield return new TestCaseData("6VDN-NMJ5-XKYQ"); 347 | yield return new TestCaseData("D5B2-NKM1-TR1V"); 348 | yield return new TestCaseData("YVP5-VG5H-49XT"); 349 | yield return new TestCaseData("VWUX-DT61-B6P9"); 350 | yield return new TestCaseData("6682-2MRF-7JW8"); 351 | yield return new TestCaseData("16BW-57PQ-JWEK"); 352 | yield return new TestCaseData("RH71-6WV2-AQ42"); 353 | yield return new TestCaseData("WGA3-AJ11-1HTM"); 354 | yield return new TestCaseData("L5EM-TL4M-8DB8"); 355 | yield return new TestCaseData("VFK4-0X63-L0J8"); 356 | yield return new TestCaseData("67N4-7E0K-XUG6"); 357 | yield return new TestCaseData("VBUW-9MQX-DPNX"); 358 | yield return new TestCaseData("T3C8-G2MP-T794"); 359 | yield return new TestCaseData("QFPM-TQDD-8YJG"); 360 | yield return new TestCaseData("9J9C-4FEP-0RMQ"); 361 | yield return new TestCaseData("VQ67-N427-GEK9"); 362 | yield return new TestCaseData("JGC2-8KU6-X1RH"); 363 | yield return new TestCaseData("C5DF-YVB1-RHCN"); 364 | yield return new TestCaseData("XP5U-MFKU-VWNA"); 365 | yield return new TestCaseData("RYDR-V1NV-AGCD"); 366 | yield return new TestCaseData("K50J-58WJ-QRF2"); 367 | yield return new TestCaseData("LE0P-P4WP-XL0C"); 368 | yield return new TestCaseData("NEEF-GPMK-58UQ"); 369 | yield return new TestCaseData("Q9V5-31PR-HYLC"); 370 | yield return new TestCaseData("MUAM-DW7V-UM02"); 371 | yield return new TestCaseData("14R5-UTHD-A71H"); 372 | yield return new TestCaseData("XKV5-TN0Q-WDVB"); 373 | yield return new TestCaseData("R8BL-AMFA-EJMG"); 374 | yield return new TestCaseData("TWLE-5UEP-U869"); 375 | yield return new TestCaseData("T3UP-YWKV-T81F"); 376 | yield return new TestCaseData("LWFD-U62M-9CXV"); 377 | yield return new TestCaseData("80HX-C631-AAR5"); 378 | yield return new TestCaseData("J35U-4DNQ-PHAB"); 379 | yield return new TestCaseData("G53P-NPD7-UUJA"); 380 | yield return new TestCaseData("0TFM-WXK7-YAA7"); 381 | yield return new TestCaseData("6FX9-3E78-7H1P"); 382 | yield return new TestCaseData("7QM5-TL2K-1XTL"); 383 | yield return new TestCaseData("71XB-81CM-WNRQ"); 384 | yield return new TestCaseData("8VQB-1W75-47B0"); 385 | yield return new TestCaseData("K1EJ-E2RJ-53B6"); 386 | yield return new TestCaseData("C1F3-FN1N-P04P"); 387 | yield return new TestCaseData("20BV-DUQ7-CGAL"); 388 | yield return new TestCaseData("Q287-UHER-AXPB"); 389 | yield return new TestCaseData("MC7C-LQGL-YP1V"); 390 | yield return new TestCaseData("2BKV-971L-18BM"); 391 | yield return new TestCaseData("T66V-NT6T-P6Y9"); 392 | yield return new TestCaseData("GH11-6PQ8-P7RN"); 393 | yield return new TestCaseData("QYVL-BAVL-R576"); 394 | yield return new TestCaseData("X6KV-CJR3-AQKH"); 395 | yield return new TestCaseData("MHR1-MVCK-PFHB"); 396 | yield return new TestCaseData("TC1D-Q3YT-PYJ6"); 397 | yield return new TestCaseData("3JPX-0C2W-0JPH"); 398 | yield return new TestCaseData("6G64-KGWG-WFTG"); 399 | yield return new TestCaseData("UX24-D36W-RWWK"); 400 | yield return new TestCaseData("CE5P-N18J-YNR2"); 401 | yield return new TestCaseData("TWNG-LDFU-771K"); 402 | yield return new TestCaseData("715H-NFPL-8UXE"); 403 | yield return new TestCaseData("AHW2-018C-BUAP"); 404 | yield return new TestCaseData("TRDQ-N0B2-YK7L"); 405 | yield return new TestCaseData("8E88-BX55-G855"); 406 | yield return new TestCaseData("XQDP-LEXX-TPYL"); 407 | yield return new TestCaseData("JL6A-6UVT-THPN"); 408 | yield return new TestCaseData("6YAE-UQG5-PGM3"); 409 | yield return new TestCaseData("DWN4-8M7Q-KXYD"); 410 | yield return new TestCaseData("LVQ3-ABE5-0X49"); 411 | yield return new TestCaseData("PQ2U-8Q8L-CMWA"); 412 | yield return new TestCaseData("RDGU-89T1-408K"); 413 | yield return new TestCaseData("9RK0-99KE-77XH"); 414 | yield return new TestCaseData("D9HN-7MPL-YP40"); 415 | yield return new TestCaseData("7XCH-M3RN-UNNC"); 416 | yield return new TestCaseData("TW3V-LTAM-M7M9"); 417 | yield return new TestCaseData("V7PB-56R7-U4VH"); 418 | yield return new TestCaseData("J42C-KWK5-AQLJ"); 419 | yield return new TestCaseData("3TDH-A44H-MEKG"); 420 | yield return new TestCaseData("ARAB-DE7N-LUV4"); 421 | yield return new TestCaseData("A090-UCKV-DF5G"); 422 | yield return new TestCaseData("CXV9-BL51-K7Q3"); 423 | yield return new TestCaseData("U54U-VBLX-RN2E"); 424 | yield return new TestCaseData("81QR-XHR3-JHGA"); 425 | yield return new TestCaseData("4GC1-HW4C-L2KG"); 426 | yield return new TestCaseData("LFL0-YRE9-WTU9"); 427 | yield return new TestCaseData("Q5UM-7P9D-Q08G"); 428 | yield return new TestCaseData("2B1A-WQ75-JDW9"); 429 | yield return new TestCaseData("XTE0-8JLB-WMM1"); 430 | yield return new TestCaseData("JWMA-P3HP-9NFH"); 431 | yield return new TestCaseData("BTGR-QR3D-8LMU"); 432 | yield return new TestCaseData("T2W6-2EM2-78GN"); 433 | yield return new TestCaseData("7PYU-JMUU-L2MJ"); 434 | yield return new TestCaseData("0QBA-ANH0-T477"); 435 | yield return new TestCaseData("004C-Y2G8-XFP2"); 436 | yield return new TestCaseData("BCA1-8DWJ-HDA1"); 437 | yield return new TestCaseData("UK8J-PA1G-BE3H"); 438 | yield return new TestCaseData("L7R8-9X7W-ATNU"); 439 | yield return new TestCaseData("2KY6-RD7T-CV3Q"); 440 | yield return new TestCaseData("YPV8-X6L6-V6Q9"); 441 | yield return new TestCaseData("4V77-GUET-TRD9"); 442 | yield return new TestCaseData("9VKT-6URP-CL81"); 443 | yield return new TestCaseData("TLT4-HBPX-K5TW"); 444 | yield return new TestCaseData("VEA7-G1EV-URWE"); 445 | yield return new TestCaseData("FQVH-798R-85KK"); 446 | yield return new TestCaseData("PG30-FQ1W-GKVL"); 447 | yield return new TestCaseData("27WQ-RPE6-2DQR"); 448 | yield return new TestCaseData("JPDC-PV0G-8ADF"); 449 | yield return new TestCaseData("15UT-L75U-9NAC"); 450 | yield return new TestCaseData("G2PH-VQ6F-8CAK"); 451 | yield return new TestCaseData("3NF5-B16D-6D7T"); 452 | yield return new TestCaseData("C11L-YFF6-QVFV"); 453 | yield return new TestCaseData("38HL-LX2U-78QX"); 454 | yield return new TestCaseData("6WDA-PHB4-382M"); 455 | yield return new TestCaseData("Y9NF-P4JC-C4RQ"); 456 | yield return new TestCaseData("YGTV-UKH4-YXQW"); 457 | yield return new TestCaseData("WCGT-WW99-GNPA"); 458 | yield return new TestCaseData("917V-CL7P-LTM9"); 459 | yield return new TestCaseData("Q55X-QYRR-4L7T"); 460 | yield return new TestCaseData("D0G5-0JDX-RB0L"); 461 | yield return new TestCaseData("R9WT-9RAX-NKE2"); 462 | yield return new TestCaseData("FQH6-J048-8E7P"); 463 | yield return new TestCaseData("06XV-H5YH-4CTH"); 464 | yield return new TestCaseData("K901-EFA2-HMVG"); 465 | yield return new TestCaseData("3C6P-CXJ7-QWFG"); 466 | yield return new TestCaseData("GFLD-GE5J-72N7"); 467 | yield return new TestCaseData("1RKT-XF24-6NQV"); 468 | yield return new TestCaseData("DKFQ-W6JF-1K57"); 469 | yield return new TestCaseData("1PLL-HWQ1-CEX2"); 470 | yield return new TestCaseData("VK10-43MK-VWXJ"); 471 | yield return new TestCaseData("G3WB-DF8B-CM9M"); 472 | yield return new TestCaseData("50CU-NKT6-MVK3"); 473 | yield return new TestCaseData("N0AQ-HYDV-AGX0"); 474 | yield return new TestCaseData("95TX-YM2E-JKDE"); 475 | yield return new TestCaseData("M9FR-CTCJ-TTH1"); 476 | yield return new TestCaseData("UJ4T-FX7R-0W5N"); 477 | yield return new TestCaseData("HWAA-8W0E-C0DW"); 478 | yield return new TestCaseData("W6G5-RCBB-KE3N"); 479 | yield return new TestCaseData("0P5G-EL60-T618"); 480 | yield return new TestCaseData("4CNV-PFDX-D5X6"); 481 | yield return new TestCaseData("Q254-C85A-BNDQ"); 482 | yield return new TestCaseData("VMBH-83DW-PVTK"); 483 | yield return new TestCaseData("D8AU-69VR-PK8G"); 484 | yield return new TestCaseData("XRVT-4F2B-45YD"); 485 | yield return new TestCaseData("9CHX-7UVF-8QWJ"); 486 | yield return new TestCaseData("DQT6-FKE9-0YVM"); 487 | yield return new TestCaseData("Q593-U9FW-L8N9"); 488 | yield return new TestCaseData("AV73-5T5T-XV9J"); 489 | yield return new TestCaseData("N7MD-0XQM-WEUW"); 490 | yield return new TestCaseData("37DV-HG4D-RP55"); 491 | yield return new TestCaseData("0VM3-10BG-PBXA"); 492 | yield return new TestCaseData("QTQE-8AA4-3TB0"); 493 | yield return new TestCaseData("5DFW-DU4J-M611"); 494 | yield return new TestCaseData("L4QC-1J28-G386"); 495 | yield return new TestCaseData("WDVT-Y0J3-PU38"); 496 | yield return new TestCaseData("WL8F-HR3V-9N57"); 497 | yield return new TestCaseData("LXG2-B59X-4GBG"); 498 | yield return new TestCaseData("22NF-QGKD-16AD"); 499 | yield return new TestCaseData("W47L-F5NX-5N1G"); 500 | yield return new TestCaseData("DW3G-JEND-4PXD"); 501 | yield return new TestCaseData("FNXC-FBKH-RXR3"); 502 | yield return new TestCaseData("1532-98R1-CLJB"); 503 | yield return new TestCaseData("YLN7-QTVT-AL2H"); 504 | yield return new TestCaseData("FUAR-1366-N0WV"); 505 | yield return new TestCaseData("F718-16B6-TMEU"); 506 | yield return new TestCaseData("8GV4-JUA0-MHUK"); 507 | yield return new TestCaseData("AXQU-63T2-EQYG"); 508 | yield return new TestCaseData("VLQB-H5H3-G6UL"); 509 | yield return new TestCaseData("9GQL-3XPJ-8CDN"); 510 | yield return new TestCaseData("3L7M-VG3F-F0WC"); 511 | yield return new TestCaseData("TAMT-4T45-79NG"); 512 | yield return new TestCaseData("XLB7-Q0PP-3CKM"); 513 | yield return new TestCaseData("UGTA-FH5Q-0C59"); 514 | yield return new TestCaseData("RVTC-6GGN-04R1"); 515 | yield return new TestCaseData("C1G4-A0FE-TUTW"); 516 | yield return new TestCaseData("P74G-E0WF-UHF3"); 517 | yield return new TestCaseData("EACR-7JMP-CRXR"); 518 | yield return new TestCaseData("39XM-62XJ-C6LT"); 519 | yield return new TestCaseData("T525-KPD9-JLWJ"); 520 | yield return new TestCaseData("HAR5-14L8-B0QL"); 521 | yield return new TestCaseData("G54Q-BUX4-MQKL"); 522 | yield return new TestCaseData("QFB9-QC2D-0B9R"); 523 | yield return new TestCaseData("U56W-PACU-ALQ8"); 524 | yield return new TestCaseData("9GUP-TNME-HVDA"); 525 | yield return new TestCaseData("40UN-UJVU-BW7U"); 526 | yield return new TestCaseData("MBXG-FNB1-PFA4"); 527 | yield return new TestCaseData("9024-0A7U-G0GK"); 528 | yield return new TestCaseData("NQGM-FDFL-3QEU"); 529 | yield return new TestCaseData("6TJL-U729-UD7C"); 530 | yield return new TestCaseData("JYMH-E1W3-LJND"); 531 | yield return new TestCaseData("4F23-ACEQ-WQPW"); 532 | yield return new TestCaseData("DE8F-2UM1-VWYK"); 533 | yield return new TestCaseData("JHDN-VX10-2H0F"); 534 | yield return new TestCaseData("UFKF-4V08-FQ16"); 535 | yield return new TestCaseData("T61P-1FP3-5M95"); 536 | yield return new TestCaseData("5RC6-RYAX-MGPU"); 537 | yield return new TestCaseData("9GHD-B8P8-W7QH"); 538 | yield return new TestCaseData("HV1D-DQ6R-JMKU"); 539 | yield return new TestCaseData("2F80-U9WC-YG4N"); 540 | yield return new TestCaseData("DGP6-BNDG-NL8F"); 541 | yield return new TestCaseData("X1Y7-M18W-5PKN"); 542 | yield return new TestCaseData("0A0C-N9X6-5GLE"); 543 | yield return new TestCaseData("0AL1-N9DL-E0JC"); 544 | yield return new TestCaseData("9GPK-HEY2-YDE6"); 545 | yield return new TestCaseData("DQ4F-L7F6-0EBN"); 546 | yield return new TestCaseData("RR90-J9D2-KLR3"); 547 | yield return new TestCaseData("WD42-7QYP-2J69"); 548 | yield return new TestCaseData("6K81-VMDU-BVKL"); 549 | yield return new TestCaseData("5EY2-QRGT-BFJL"); 550 | yield return new TestCaseData("YNE6-XBYK-XF2C"); 551 | yield return new TestCaseData("D66G-4RGW-2EWJ"); 552 | yield return new TestCaseData("DUMU-FG88-K23B"); 553 | yield return new TestCaseData("UXVX-7M52-5T20"); 554 | yield return new TestCaseData("3NTG-N907-TNNP"); 555 | yield return new TestCaseData("V26P-L8T5-GYTW"); 556 | yield return new TestCaseData("ENE7-VTWE-PJYL"); 557 | yield return new TestCaseData("XNMQ-LLH7-0J93"); 558 | yield return new TestCaseData("69NB-WJU4-QV6K"); 559 | yield return new TestCaseData("XA0P-BY7T-1AXG"); 560 | yield return new TestCaseData("LTPT-APKM-K8HF"); 561 | yield return new TestCaseData("G4VW-4MLK-K8HF"); 562 | yield return new TestCaseData("0FM4-758B-8PHJ"); 563 | yield return new TestCaseData("0JAK-5UHT-RGWL"); 564 | yield return new TestCaseData("BDDP-V20R-B4M0"); 565 | yield return new TestCaseData("TJVX-05XH-FKV0"); 566 | yield return new TestCaseData("HT9E-DBNB-NCV7"); 567 | yield return new TestCaseData("X294-EYH3-MPM3"); 568 | yield return new TestCaseData("5L3T-BE9F-GRHX"); 569 | yield return new TestCaseData("L54B-TBAB-2AT1"); 570 | yield return new TestCaseData("DDC0-8C78-D4NA"); 571 | yield return new TestCaseData("EVP6-Y2A2-PHEF"); 572 | yield return new TestCaseData("B4LE-PXC4-K1AX"); 573 | yield return new TestCaseData("DR87-7F5C-VBYJ"); 574 | yield return new TestCaseData("EMPV-775F-6MTB"); 575 | yield return new TestCaseData("TTQP-JTMP-9N13"); 576 | yield return new TestCaseData("TEVG-6KNP-DDDH"); 577 | yield return new TestCaseData("VRRE-DPY0-1MT4"); 578 | yield return new TestCaseData("59QQ-80MB-QBUU"); 579 | yield return new TestCaseData("C8HE-G1N5-3NAG"); 580 | yield return new TestCaseData("B9MH-05D0-FJP7"); 581 | yield return new TestCaseData("2J1K-MUPB-15G0"); 582 | yield return new TestCaseData("EPM2-F7R9-3PPH"); 583 | yield return new TestCaseData("UA2U-U7GP-7HMC"); 584 | yield return new TestCaseData("BEA8-LJ5K-9LGB"); 585 | yield return new TestCaseData("DPXN-C7M7-F9DC"); 586 | yield return new TestCaseData("6F6G-C11V-PM9Q"); 587 | yield return new TestCaseData("U2QM-TNUL-B7JP"); 588 | yield return new TestCaseData("G3DT-8WN5-J111"); 589 | yield return new TestCaseData("MV65-F1RK-8RX7"); 590 | yield return new TestCaseData("0VVA-EY9T-7TR1"); 591 | yield return new TestCaseData("64E1-C7E0-B2Y3"); 592 | yield return new TestCaseData("QQAQ-986D-N3LE"); 593 | yield return new TestCaseData("9TPP-QEH4-R0DA"); 594 | yield return new TestCaseData("B71M-R9EK-AB10"); 595 | yield return new TestCaseData("URUU-B1LU-32CA"); 596 | yield return new TestCaseData("BV6N-M8WV-1R0P"); 597 | yield return new TestCaseData("71R6-GKK3-CGQ3"); 598 | yield return new TestCaseData("0YW6-3UPP-JRYN"); 599 | yield return new TestCaseData("30R0-BT4M-W1UX"); 600 | yield return new TestCaseData("TK7V-B2YT-A2VB"); 601 | yield return new TestCaseData("QC58-0LJB-QQ54"); 602 | yield return new TestCaseData("2UPT-JQWQ-EHKT"); 603 | yield return new TestCaseData("EMHN-36GL-C0N7"); 604 | yield return new TestCaseData("FDC9-19RF-FBLT"); 605 | yield return new TestCaseData("QN6D-0MQ5-V1FW"); 606 | yield return new TestCaseData("4RCH-B49B-VL7A"); 607 | yield return new TestCaseData("YD29-DCLV-YBTB"); 608 | yield return new TestCaseData("W9EW-52ED-Q92T"); 609 | yield return new TestCaseData("6U5T-7KB1-7A2F"); 610 | yield return new TestCaseData("U4CG-9XMC-YWK5"); 611 | yield return new TestCaseData("2WHU-UPFG-D12T"); 612 | yield return new TestCaseData("KAWJ-FUF7-KHM4"); 613 | yield return new TestCaseData("EYV6-RE4B-K0DE"); 614 | yield return new TestCaseData("5AH5-KLHJ-HQ0E"); 615 | yield return new TestCaseData("P7ET-GTUL-Q92T"); 616 | yield return new TestCaseData("DNU0-FWED-X3QP"); 617 | yield return new TestCaseData("PNTD-K7RU-B597"); 618 | yield return new TestCaseData("G62A-QM2W-02UU"); 619 | yield return new TestCaseData("W73B-NQFV-GEH7"); 620 | yield return new TestCaseData("7P62-R95A-7FAR"); 621 | yield return new TestCaseData("NEDE-W3DF-23KG"); 622 | yield return new TestCaseData("MVXW-H7RJ-84T7"); 623 | yield return new TestCaseData("DCDD-B35K-JTHU"); 624 | yield return new TestCaseData("DQ9L-H0FX-0DNE"); 625 | yield return new TestCaseData("DDWH-A9M5-2UM9"); 626 | yield return new TestCaseData("HDPW-K8A0-L3DW"); 627 | yield return new TestCaseData("CWXP-Y7PH-9L50"); 628 | yield return new TestCaseData("GRVR-0WMX-W1FJ"); 629 | yield return new TestCaseData("AN7D-P8X7-RNL1"); 630 | yield return new TestCaseData("LUWL-YTM4-21YM"); 631 | yield return new TestCaseData("0PU7-7864-E7GK"); 632 | yield return new TestCaseData("4J40-8J5U-CHTQ"); 633 | yield return new TestCaseData("L60T-3Q05-MF5M"); 634 | yield return new TestCaseData("N5CV-7AEK-YQ4K"); 635 | yield return new TestCaseData("7UN1-WGEF-XT24"); 636 | yield return new TestCaseData("673G-AJFF-L9QX"); 637 | yield return new TestCaseData("27PJ-THPE-1VG3"); 638 | yield return new TestCaseData("8D9M-7QH9-2U4P"); 639 | yield return new TestCaseData("ACAC-HN0X-GJW2"); 640 | yield return new TestCaseData("MTMD-KR36-HAMH"); 641 | yield return new TestCaseData("E2VD-PP0E-CYDW"); 642 | yield return new TestCaseData("YWMN-H6CH-FUKK"); 643 | yield return new TestCaseData("L322-V9V0-0Y70"); 644 | yield return new TestCaseData("KLF8-T8T1-GMCB"); 645 | yield return new TestCaseData("Q1N2-UTEA-Q43R"); 646 | yield return new TestCaseData("DHEG-T8DK-UG22"); 647 | yield return new TestCaseData("W6NB-0RFA-RYGD"); 648 | yield return new TestCaseData("3836-82B8-JAG1"); 649 | yield return new TestCaseData("5RE8-4GXU-K5QU"); 650 | yield return new TestCaseData("RWLR-7BVM-J74R"); 651 | yield return new TestCaseData("23FU-PR3Q-4MU3"); 652 | yield return new TestCaseData("RJ5J-KUDP-8G3T"); 653 | yield return new TestCaseData("P7AN-3TQ5-UEPG"); 654 | yield return new TestCaseData("6VJU-HDL3-A75M"); 655 | yield return new TestCaseData("JHY9-TX1N-H9MW"); 656 | yield return new TestCaseData("02BT-HYWD-20LN"); 657 | yield return new TestCaseData("7Q6M-1KE8-QCYK"); 658 | yield return new TestCaseData("PKH9-T4NE-GQRK"); 659 | yield return new TestCaseData("3Q52-KCGL-WQW4"); 660 | yield return new TestCaseData("HNQF-H4CA-3HGL"); 661 | yield return new TestCaseData("AVWR-X0LG-GLFT"); 662 | yield return new TestCaseData("LNYL-F0NV-ECDJ"); 663 | yield return new TestCaseData("MB4M-DQRD-CWEP"); 664 | yield return new TestCaseData("0A9M-6RQF-KBXP"); 665 | yield return new TestCaseData("464L-KBK4-QF6L"); 666 | yield return new TestCaseData("41BR-6MX7-HBH1"); 667 | yield return new TestCaseData("3Y28-J4N9-GLM1"); 668 | yield return new TestCaseData("8W8E-AKPB-ECMT"); 669 | yield return new TestCaseData("4TWN-0PYK-C22R"); 670 | yield return new TestCaseData("KYH2-R50N-492W"); 671 | yield return new TestCaseData("C8UQ-PNRL-6N04"); 672 | yield return new TestCaseData("7233-BF82-2BE8"); 673 | yield return new TestCaseData("M66M-W7L5-LF95"); 674 | yield return new TestCaseData("XYH5-JKVM-C8EU"); 675 | yield return new TestCaseData("2F1Q-D3KB-C09R"); 676 | yield return new TestCaseData("A3H3-FCF1-7F8P"); 677 | yield return new TestCaseData("9YAC-41LB-5MYU"); 678 | yield return new TestCaseData("MLX1-Y0P8-092B"); 679 | yield return new TestCaseData("0Q43-QYNN-QJCM"); 680 | yield return new TestCaseData("DBET-ELXQ-DHTD"); 681 | yield return new TestCaseData("AXHL-14AW-MR7U"); 682 | yield return new TestCaseData("1074-56K1-7L2K"); 683 | yield return new TestCaseData("90HK-8AB5-69FL"); 684 | yield return new TestCaseData("FHYB-UA79-13NX"); 685 | yield return new TestCaseData("N452-BQDP-2YW0"); 686 | yield return new TestCaseData("4PA8-PGBG-WFA0"); 687 | yield return new TestCaseData("V9MG-07RK-VV11"); 688 | yield return new TestCaseData("AY3R-KHY6-40XA"); 689 | yield return new TestCaseData("PB2V-2F44-M4RJ"); 690 | yield return new TestCaseData("W2GN-32G6-VKAR"); 691 | yield return new TestCaseData("LGQP-91DB-GX0F"); 692 | yield return new TestCaseData("99J5-UFU0-BRFM"); 693 | yield return new TestCaseData("EYGR-38L0-TXNL"); 694 | yield return new TestCaseData("9GTN-CED8-2RL1"); 695 | yield return new TestCaseData("9CGW-URAJ-2T88"); 696 | yield return new TestCaseData("ANR0-NVB7-KL2B"); 697 | yield return new TestCaseData("N604-HGJU-X6TL"); 698 | yield return new TestCaseData("F6XJ-4353-84N3"); 699 | yield return new TestCaseData("8VN9-UTRM-F987"); 700 | yield return new TestCaseData("G3CR-PVK4-1123"); 701 | yield return new TestCaseData("PD02-HUDE-526D"); 702 | yield return new TestCaseData("LQ84-5EW8-Q7AU"); 703 | yield return new TestCaseData("4KCU-GC8E-HMBX"); 704 | yield return new TestCaseData("3JQ0-G1R8-1DYC"); 705 | yield return new TestCaseData("1JA8-5D1P-7687"); 706 | yield return new TestCaseData("6UYM-0TFW-W3YA"); 707 | yield return new TestCaseData("KUTV-R5MC-D7H0"); 708 | yield return new TestCaseData("D1AJ-FT59-8Y97"); 709 | yield return new TestCaseData("GB6G-1ERH-EE8L"); 710 | yield return new TestCaseData("DF94-BU16-V0WQ"); 711 | yield return new TestCaseData("B3HP-2E6J-VJGC"); 712 | yield return new TestCaseData("H9LC-19NC-J3GP"); 713 | yield return new TestCaseData("2XJG-L5UB-1Q37"); 714 | yield return new TestCaseData("FK2L-1D15-3TNB"); 715 | yield return new TestCaseData("JU7L-KR8B-TUY3"); 716 | yield return new TestCaseData("GVB3-X97K-JV7Q"); 717 | yield return new TestCaseData("53M0-8XHK-BU1E"); 718 | yield return new TestCaseData("96NE-5W2J-YU0A"); 719 | yield return new TestCaseData("XR0W-GVY0-2A5B"); 720 | yield return new TestCaseData("U939-BKMW-190W"); 721 | yield return new TestCaseData("6CTA-T4JA-N41E"); 722 | yield return new TestCaseData("575W-3PM7-QFCT"); 723 | yield return new TestCaseData("UM5N-7YLM-ARVE"); 724 | yield return new TestCaseData("L5MV-DCR2-XHL6"); 725 | yield return new TestCaseData("V6RR-AB0N-9DYH"); 726 | yield return new TestCaseData("JP0X-A0YX-ENY9"); 727 | yield return new TestCaseData("N56N-DQJ6-WGMX"); 728 | yield return new TestCaseData("128C-H2MC-0VHF"); 729 | yield return new TestCaseData("BMCK-YJBV-J08L"); 730 | yield return new TestCaseData("UVA5-WWCC-FKAD"); 731 | yield return new TestCaseData("Y0CL-Q9N7-GLP3"); 732 | yield return new TestCaseData("H41N-PXLC-RX4D"); 733 | yield return new TestCaseData("L5X6-LYL2-XNJ6"); 734 | yield return new TestCaseData("XRKH-0GCN-HDND"); 735 | yield return new TestCaseData("JQTD-N788-XALV"); 736 | yield return new TestCaseData("HPX9-NJ91-2FLV"); 737 | yield return new TestCaseData("GD2K-NCHK-WR71"); 738 | yield return new TestCaseData("0WUV-Y1TX-VJ84"); 739 | yield return new TestCaseData("81DE-XTE8-RTKE"); 740 | yield return new TestCaseData("K7UM-TR8U-E9LX"); 741 | yield return new TestCaseData("U20V-PCXM-PET1"); 742 | yield return new TestCaseData("U3P8-62RD-YVRP"); 743 | yield return new TestCaseData("65X5-PWQV-V4EP"); 744 | yield return new TestCaseData("NDAP-9MJQ-FB5B"); 745 | yield return new TestCaseData("U4X3-YB9H-DVM0"); 746 | yield return new TestCaseData("VDBL-89EL-TDK4"); 747 | yield return new TestCaseData("7A0V-9VUB-B43D"); 748 | yield return new TestCaseData("36WR-15DL-DU6V"); 749 | yield return new TestCaseData("VWLP-2R37-XNL8"); 750 | yield return new TestCaseData("LDX3-86JW-U06C"); 751 | yield return new TestCaseData("0JMX-4GYV-3VWR"); 752 | yield return new TestCaseData("KY4L-V1R0-M4B4"); 753 | yield return new TestCaseData("WJEE-YPC0-MB13"); 754 | yield return new TestCaseData("JLJN-85KB-3DWK"); 755 | yield return new TestCaseData("CHBQ-X2W1-JG5B"); 756 | yield return new TestCaseData("EL4M-DLAF-F54L"); 757 | yield return new TestCaseData("XJ5R-R0B0-UDPV"); 758 | yield return new TestCaseData("VRK8-43FD-MJGU"); 759 | yield return new TestCaseData("0F7M-ARX8-NTA7"); 760 | yield return new TestCaseData("Y4WL-YBHR-HM5Q"); 761 | yield return new TestCaseData("VE85-4PW4-743T"); 762 | yield return new TestCaseData("48D5-F3MN-FW18"); 763 | yield return new TestCaseData("PRAP-CTMU-M6GG"); 764 | yield return new TestCaseData("BUVR-C9UL-2P5A"); 765 | yield return new TestCaseData("U05T-1BC9-3TG5"); 766 | yield return new TestCaseData("GPK9-XYMH-NE9T"); 767 | yield return new TestCaseData("NBDK-MVY7-TLD7"); 768 | yield return new TestCaseData("RJGW-61RR-7EJE"); 769 | yield return new TestCaseData("P8NN-V117-Y81N"); 770 | yield return new TestCaseData("AJJA-UXBM-RHN1"); 771 | yield return new TestCaseData("PUL9-H27W-UJLU"); 772 | yield return new TestCaseData("6DY3-6KX0-EPXU"); 773 | yield return new TestCaseData("AMJ5-K6FW-PHGH"); 774 | yield return new TestCaseData("P4XG-02A2-EAWU"); 775 | yield return new TestCaseData("RJU9-F71G-FRUL"); 776 | yield return new TestCaseData("4DMF-PHRJ-GJCG"); 777 | yield return new TestCaseData("4RGM-MNN8-652Q"); 778 | yield return new TestCaseData("HGPQ-K8XL-QHQE"); 779 | yield return new TestCaseData("51JM-639G-D59G"); 780 | yield return new TestCaseData("NTFU-UVFJ-H4GN"); 781 | yield return new TestCaseData("3Y8E-DT3W-5BVL"); 782 | yield return new TestCaseData("PG0V-K2JJ-KGE9"); 783 | yield return new TestCaseData("K617-5XCG-635L"); 784 | yield return new TestCaseData("ETQ0-VY1K-EP41"); 785 | yield return new TestCaseData("RK89-2L79-NYML"); 786 | yield return new TestCaseData("PF6F-CEVP-TK17"); 787 | yield return new TestCaseData("V41R-8274-VQRB"); 788 | yield return new TestCaseData("A7KK-K46D-96EN"); 789 | yield return new TestCaseData("X1AH-7078-EXJQ"); 790 | yield return new TestCaseData("4JD9-XXU4-RCP0"); 791 | yield return new TestCaseData("HWJJ-AWWM-RVQT"); 792 | yield return new TestCaseData("R90V-V3HX-L2JF"); 793 | yield return new TestCaseData("WCT5-MY68-PQT5"); 794 | yield return new TestCaseData("2Y0H-Q207-B8C5"); 795 | yield return new TestCaseData("0T28-H1PT-DRAR"); 796 | yield return new TestCaseData("7WH3-96VV-8R8G"); 797 | yield return new TestCaseData("T99T-BLHD-ENX8"); 798 | yield return new TestCaseData("UAGA-4K7X-ENMX"); 799 | yield return new TestCaseData("DVXQ-NXHL-9X21"); 800 | yield return new TestCaseData("C3B6-8ALE-VGRE"); 801 | yield return new TestCaseData("4AED-1JGN-GAFN"); 802 | yield return new TestCaseData("WE2K-LPJ3-HEN1"); 803 | yield return new TestCaseData("F1G2-1M45-GT34"); 804 | yield return new TestCaseData("EY09-H0EW-6WM3"); 805 | yield return new TestCaseData("R0K0-VJNA-R0HE"); 806 | yield return new TestCaseData("ERXJ-2LEG-VG9W"); 807 | yield return new TestCaseData("EH1P-7VFM-68AU"); 808 | yield return new TestCaseData("NWNW-P3NV-9W2D"); 809 | yield return new TestCaseData("2AC2-K0YQ-H8PC"); 810 | yield return new TestCaseData("C8PL-PC1P-CATF"); 811 | yield return new TestCaseData("LFER-VJ9V-DH0J"); 812 | yield return new TestCaseData("KDCU-DDTN-J92X"); 813 | yield return new TestCaseData("65AG-U872-5T86"); 814 | yield return new TestCaseData("1N0C-5JHA-TAK9"); 815 | yield return new TestCaseData("5P4N-DJ86-FFNB"); 816 | yield return new TestCaseData("98KJ-ALHQ-TQGQ"); 817 | yield return new TestCaseData("VEFC-A42F-JFXH"); 818 | yield return new TestCaseData("8462-AGRJ-LMTC"); 819 | yield return new TestCaseData("D9W3-1KNG-0F65"); 820 | yield return new TestCaseData("05W8-UEYG-37NN"); 821 | yield return new TestCaseData("ETLU-BF5X-38H5"); 822 | yield return new TestCaseData("RRVK-WFBQ-C978"); 823 | yield return new TestCaseData("97CP-D5HG-X21C"); 824 | yield return new TestCaseData("3JHQ-MDPQ-BWWJ"); 825 | yield return new TestCaseData("KT2G-B01L-4G05"); 826 | yield return new TestCaseData("BU74-J4VF-WNJH"); 827 | yield return new TestCaseData("URY0-CPKX-VFRT"); 828 | yield return new TestCaseData("J7BG-790H-4QW0"); 829 | yield return new TestCaseData("0HNC-JD58-QH2P"); 830 | yield return new TestCaseData("CVY5-JJ9E-UN8W"); 831 | yield return new TestCaseData("BGEK-TVBR-2JGK"); 832 | yield return new TestCaseData("TMPL-0W9J-M0T5"); 833 | yield return new TestCaseData("AR67-TCYL-6447"); 834 | yield return new TestCaseData("TKG6-T7VF-F9TR"); 835 | yield return new TestCaseData("V8U3-CJP1-A5Q2"); 836 | yield return new TestCaseData("2G8K-0H42-8ETB"); 837 | yield return new TestCaseData("029Q-5E7H-LCLM"); 838 | yield return new TestCaseData("JFBD-1YT0-3LWV"); 839 | yield return new TestCaseData("VCPD-E04M-VU6J"); 840 | yield return new TestCaseData("JAHH-Q12M-WJF0"); 841 | yield return new TestCaseData("EJKW-7PQV-A41N"); 842 | yield return new TestCaseData("GGR6-0N77-PMP7"); 843 | yield return new TestCaseData("8VCX-CCM9-E2FG"); 844 | yield return new TestCaseData("FAHK-VLQK-4MQ0"); 845 | yield return new TestCaseData("1PBB-2RPU-2M86"); 846 | yield return new TestCaseData("X3XD-87XW-YTPE"); 847 | yield return new TestCaseData("3XUE-2BUD-DF6H"); 848 | yield return new TestCaseData("XJXK-LJP6-217V"); 849 | yield return new TestCaseData("D57W-LWDK-W20N"); 850 | yield return new TestCaseData("EFN6-WFCR-N7GQ"); 851 | yield return new TestCaseData("PV8G-67LA-NY21"); 852 | yield return new TestCaseData("HHG5-3HYU-RTE9"); 853 | yield return new TestCaseData("G6LV-8R55-AFYD"); 854 | yield return new TestCaseData("4HV5-7GA5-7RG4"); 855 | yield return new TestCaseData("QK3F-24JT-F7H9"); 856 | yield return new TestCaseData("5VJ7-BKCL-5WYQ"); 857 | yield return new TestCaseData("7F32-EJWG-06BR"); 858 | yield return new TestCaseData("4BJ5-8JTH-GNL7"); 859 | yield return new TestCaseData("9LKW-ERC8-T7MG"); 860 | yield return new TestCaseData("BGT0-Q2BJ-LQ9M"); 861 | yield return new TestCaseData("8V8T-MU0K-4J4G"); 862 | yield return new TestCaseData("BEXV-YDQ8-A1LF"); 863 | yield return new TestCaseData("Q97F-XDGB-CN00"); 864 | yield return new TestCaseData("7VRP-PT1A-7F7N"); 865 | yield return new TestCaseData("7594-GXPX-Y0VM"); 866 | yield return new TestCaseData("5X73-6YT7-3DH7"); 867 | yield return new TestCaseData("8XYR-XKF0-LT5Q"); 868 | yield return new TestCaseData("F5RR-6Y9M-DHBW"); 869 | yield return new TestCaseData("QTMB-4KQG-TW2C"); 870 | yield return new TestCaseData("57G9-GQ6P-PFVN"); 871 | yield return new TestCaseData("B35B-9WTW-F3Y9"); 872 | yield return new TestCaseData("661T-W696-6UDK"); 873 | yield return new TestCaseData("9U4P-E2XP-T56R"); 874 | yield return new TestCaseData("429B-B19G-NYGF"); 875 | yield return new TestCaseData("MPEB-FFK0-DLK1"); 876 | yield return new TestCaseData("V44V-MV4B-R8LE"); 877 | yield return new TestCaseData("4NFR-5VXU-RFKN"); 878 | yield return new TestCaseData("YPQ4-AEG2-5W3U"); 879 | yield return new TestCaseData("FXP2-321N-H14F"); 880 | yield return new TestCaseData("95HM-E2E7-019M"); 881 | yield return new TestCaseData("91XL-1CYG-7M38"); 882 | yield return new TestCaseData("URPP-3XE9-MVDV"); 883 | yield return new TestCaseData("X8H2-Y4VU-AP0A"); 884 | yield return new TestCaseData("Y4E5-X3TH-UGBB"); 885 | yield return new TestCaseData("6693-E5G4-JFTD"); 886 | yield return new TestCaseData("45UQ-674R-V9DQ"); 887 | yield return new TestCaseData("4TPG-55YR-MTNX"); 888 | yield return new TestCaseData("M4R2-Q43H-C4GF"); 889 | yield return new TestCaseData("7XW3-JG64-QHG6"); 890 | yield return new TestCaseData("N9JH-D739-UYFM"); 891 | yield return new TestCaseData("JAAA-B8G1-HKXB"); 892 | yield return new TestCaseData("8X5X-FT48-VPLJ"); 893 | yield return new TestCaseData("8RUK-FUVL-T56R"); 894 | yield return new TestCaseData("H9UK-LG5C-9GT7"); 895 | yield return new TestCaseData("K2YP-ET7N-ENR3"); 896 | yield return new TestCaseData("XDD0-W313-43FM"); 897 | yield return new TestCaseData("FDA7-D3A2-GRDT"); 898 | yield return new TestCaseData("UYNC-BHDE-HUR3"); 899 | yield return new TestCaseData("X6HT-M1C2-TXKH"); 900 | yield return new TestCaseData("A427-4LKX-8WH8"); 901 | yield return new TestCaseData("FARU-LETT-TTUB"); 902 | yield return new TestCaseData("YND5-05H4-J0V9"); 903 | yield return new TestCaseData("UJ8X-F8NR-YG5P"); 904 | yield return new TestCaseData("RWU1-F8W1-LD5R"); 905 | yield return new TestCaseData("EUC7-CYLV-EMQE"); 906 | yield return new TestCaseData("QCMQ-H8CQ-XNC0"); 907 | yield return new TestCaseData("NBJQ-KDTJ-EMUH"); 908 | yield return new TestCaseData("JJ2X-P1UT-GBRL"); 909 | yield return new TestCaseData("JQPA-4WXT-QB00"); 910 | yield return new TestCaseData("AAWQ-VY2L-PUDJ"); 911 | yield return new TestCaseData("YX5R-812B-HWTB"); 912 | yield return new TestCaseData("EL7Q-2V98-T1RX"); 913 | yield return new TestCaseData("BXJA-J5QX-T9UW"); 914 | yield return new TestCaseData("V1A8-PVM6-QND5"); 915 | yield return new TestCaseData("JLQV-T698-J1BB"); 916 | yield return new TestCaseData("8RNE-0NUU-9AUJ"); 917 | yield return new TestCaseData("24TT-NM8T-58WT"); 918 | yield return new TestCaseData("0F5K-6DV8-CU35"); 919 | yield return new TestCaseData("CQUJ-0UCE-YT6V"); 920 | yield return new TestCaseData("J1DV-CYBK-95H6"); 921 | yield return new TestCaseData("PMGF-518K-6KR3"); 922 | yield return new TestCaseData("VU3X-GCAG-1M1A"); 923 | yield return new TestCaseData("4DA4-0XRN-4DLX"); 924 | yield return new TestCaseData("CK7U-A0QP-100D"); 925 | yield return new TestCaseData("Y2YF-5PRL-J5YE"); 926 | yield return new TestCaseData("7VTQ-F1WP-V9YB"); 927 | yield return new TestCaseData("V88F-EWLX-Y1N3"); 928 | yield return new TestCaseData("32T8-00L5-WMP3"); 929 | yield return new TestCaseData("BVP8-QPKN-YTTH"); 930 | yield return new TestCaseData("RRRG-N7AA-Y5WQ"); 931 | yield return new TestCaseData("6MCC-AJKK-0HLT"); 932 | yield return new TestCaseData("D251-9V3J-HJ2T"); 933 | yield return new TestCaseData("59TT-ABG7-E05X"); 934 | yield return new TestCaseData("9TEE-0C2W-FT7K"); 935 | yield return new TestCaseData("JBD1-J09D-X0HM"); 936 | yield return new TestCaseData("WK9V-C2K3-5C9L"); 937 | yield return new TestCaseData("UMVE-BK9H-DA8H"); 938 | yield return new TestCaseData("N637-6PCU-NH2E"); 939 | yield return new TestCaseData("5900-VA3R-844G"); 940 | yield return new TestCaseData("B8W6-RQHV-0Q8P"); 941 | yield return new TestCaseData("YH8W-5YJA-6TPA"); 942 | yield return new TestCaseData("LL9N-D3JA-UR9R"); 943 | yield return new TestCaseData("3KC7-2CNU-Q72K"); 944 | yield return new TestCaseData("LBXU-X6YH-CFC3"); 945 | yield return new TestCaseData("922B-A16Q-LARK"); 946 | yield return new TestCaseData("66D7-T7M8-21G6"); 947 | yield return new TestCaseData("MVHG-UU7N-THKJ"); 948 | yield return new TestCaseData("Q1GU-XK0G-60YL"); 949 | yield return new TestCaseData("YNWM-WAYB-ADLT"); 950 | yield return new TestCaseData("VQGH-R3BT-79RK"); 951 | yield return new TestCaseData("LRVC-YPRD-14XT"); 952 | yield return new TestCaseData("PC5K-PB03-8020"); 953 | yield return new TestCaseData("8JDU-HNCB-XNH5"); 954 | yield return new TestCaseData("K2RH-VU04-Y3XJ"); 955 | yield return new TestCaseData("4KL4-WA1C-GY7A"); 956 | yield return new TestCaseData("149L-CXXK-4RC2"); 957 | yield return new TestCaseData("Y653-UG80-GCGX"); 958 | yield return new TestCaseData("B7QD-P73P-1HNH"); 959 | yield return new TestCaseData("EA1E-YNHH-D1E7"); 960 | yield return new TestCaseData("3XBW-5FB9-EEHW"); 961 | yield return new TestCaseData("U19J-30UA-PNHL"); 962 | yield return new TestCaseData("UMXG-BJB0-UH7T"); 963 | yield return new TestCaseData("D56V-LYBQ-9BHU"); 964 | yield return new TestCaseData("N1LN-NA0T-CRQK"); 965 | yield return new TestCaseData("NRG9-10FL-94PQ"); 966 | yield return new TestCaseData("L53A-DYKG-5H6K"); 967 | yield return new TestCaseData("TL8H-WETL-HGP9"); 968 | yield return new TestCaseData("BWCG-2P96-TT7N"); 969 | yield return new TestCaseData("1BK8-CJR3-13CL"); 970 | yield return new TestCaseData("Q2ED-6G17-AM7A"); 971 | yield return new TestCaseData("KXVR-MDQR-1CPG"); 972 | yield return new TestCaseData("0LJ3-86W9-QJX8"); 973 | yield return new TestCaseData("DN8C-6PL4-G4DX"); 974 | yield return new TestCaseData("9FP0-J3XW-Q2DV"); 975 | yield return new TestCaseData("KRKE-U60K-80FD"); 976 | yield return new TestCaseData("J0C8-FAFR-088W"); 977 | yield return new TestCaseData("N0N5-PKVV-Q019"); 978 | yield return new TestCaseData("1Y74-PPH0-CHRP"); 979 | yield return new TestCaseData("DE4B-ME7U-J22M"); 980 | yield return new TestCaseData("NAE1-DNWA-YJRK"); 981 | yield return new TestCaseData("C5GJ-XF24-FLC3"); 982 | yield return new TestCaseData("3E1R-MD01-7FEW"); 983 | yield return new TestCaseData("WGKC-MJ69-HW9R"); 984 | yield return new TestCaseData("VA2G-KGE1-9CPM"); 985 | yield return new TestCaseData("ACFH-EMP5-VVMM"); 986 | yield return new TestCaseData("MQM6-FTBF-LPTK"); 987 | yield return new TestCaseData("HXBX-UKM8-H0C4"); 988 | yield return new TestCaseData("0PCP-KHU2-17KA"); 989 | yield return new TestCaseData("1Y41-869L-H9Q1"); 990 | yield return new TestCaseData("GAVK-B14B-VUYC"); 991 | yield return new TestCaseData("CMEA-3MJV-TMM3"); 992 | yield return new TestCaseData("YBWW-BPB2-AN5U"); 993 | yield return new TestCaseData("KGV7-4N7R-CXYV"); 994 | yield return new TestCaseData("T5DG-X67Q-0JPH"); 995 | yield return new TestCaseData("HMTW-AX5G-TBHT"); 996 | yield return new TestCaseData("M212-4L6H-Y1U8"); 997 | yield return new TestCaseData("B4UM-JYRW-40JW"); 998 | yield return new TestCaseData("H7AT-5QUA-TWDP"); 999 | yield return new TestCaseData("HYBJ-P57L-GE3Q"); 1000 | yield return new TestCaseData("B2D0-88M8-UNMB"); 1001 | yield return new TestCaseData("4GXK-U4M2-LXNQ"); 1002 | yield return new TestCaseData("4NFR-93MT-3570"); 1003 | yield return new TestCaseData("NAK6-B8M6-E3C1"); 1004 | yield return new TestCaseData("3182-K713-BRJQ"); 1005 | yield return new TestCaseData("QWCV-HN54-2EB0"); 1006 | yield return new TestCaseData("E7TD-HWV5-P02M"); 1007 | yield return new TestCaseData("P28J-XBXJ-VDNG"); 1008 | yield return new TestCaseData("62L0-99UN-L0LA"); 1009 | yield return new TestCaseData("GTTB-17BR-N6G5"); 1010 | yield return new TestCaseData("GQFQ-9688-R259"); 1011 | yield return new TestCaseData("N2B1-LV8T-8D85"); 1012 | yield return new TestCaseData("MA86-0Y6N-F03H"); 1013 | yield return new TestCaseData("XEDK-587U-2JX2"); 1014 | yield return new TestCaseData("C9XF-YM4G-VNLX"); 1015 | yield return new TestCaseData("BB58-A33V-5E0J"); 1016 | yield return new TestCaseData("BR3Q-42EQ-H5YR"); 1017 | yield return new TestCaseData("9DNP-QGYR-LWM4"); 1018 | yield return new TestCaseData("7T7W-2LQT-DHRC"); 1019 | yield return new TestCaseData("8KW0-MMB9-PTDX"); 1020 | yield return new TestCaseData("VRF4-M1NC-NARV"); 1021 | yield return new TestCaseData("U6V8-WC2L-KHK2"); 1022 | yield return new TestCaseData("Q7JK-8PG9-426Q"); 1023 | yield return new TestCaseData("VC8W-VU8C-88LF"); 1024 | yield return new TestCaseData("KGP2-87DC-N4Q6"); 1025 | yield return new TestCaseData("QYQG-870X-A7RA"); 1026 | yield return new TestCaseData("5AYK-DL16-2MDB"); 1027 | yield return new TestCaseData("JMF7-3HXT-4UHE"); 1028 | yield return new TestCaseData("8VAV-MNXG-GMVU"); 1029 | yield return new TestCaseData("D6HU-B7PL-2JQU"); 1030 | yield return new TestCaseData("PQ2U-XEWC-369M"); 1031 | yield return new TestCaseData("HKQL-88TD-BQH4"); 1032 | yield return new TestCaseData("LH2L-RN04-47UG"); 1033 | yield return new TestCaseData("YJGR-YM0C-2U5Q"); 1034 | yield return new TestCaseData("U87R-DU1F-9TJ3"); 1035 | yield return new TestCaseData("FVW1-D6J5-3FEB"); 1036 | yield return new TestCaseData("CDLK-HYN6-WMCP"); 1037 | yield return new TestCaseData("GXQP-1KF9-DXCW"); 1038 | yield return new TestCaseData("WA8B-JN7T-BH5E"); 1039 | yield return new TestCaseData("V1WU-Y1EJ-Y3PB"); 1040 | yield return new TestCaseData("8RTJ-VMUA-9K7E"); 1041 | yield return new TestCaseData("43LA-JWPL-529G"); 1042 | yield return new TestCaseData("02XE-3YYE-76GF"); 1043 | yield return new TestCaseData("HRF1-RU9F-R9L2"); 1044 | yield return new TestCaseData("GN35-1HF2-A5Y9"); 1045 | yield return new TestCaseData("1A9A-C91R-NUG1"); 1046 | yield return new TestCaseData("10HE-600C-VUXB"); 1047 | yield return new TestCaseData("7H6C-M94N-GX3J"); 1048 | yield return new TestCaseData("F1XG-DBPC-4FAU"); 1049 | yield return new TestCaseData("3TPU-J10P-U4VH"); 1050 | yield return new TestCaseData("CM2W-FLL3-0GPA"); 1051 | yield return new TestCaseData("J6DX-HMM1-L3UC"); 1052 | yield return new TestCaseData("9V18-U16P-HXJN"); 1053 | yield return new TestCaseData("QJLD-VL3W-EKEV"); 1054 | yield return new TestCaseData("UMP9-57WX-F648"); 1055 | yield return new TestCaseData("7KR7-1RWD-K3UP"); 1056 | yield return new TestCaseData("BLBX-XEYE-F41W"); 1057 | yield return new TestCaseData("KVK9-E149-8VHL"); 1058 | yield return new TestCaseData("18GA-9KWV-WW6E"); 1059 | yield return new TestCaseData("LBJF-0280-HLHH"); 1060 | yield return new TestCaseData("L6QK-G9T6-KGJD"); 1061 | yield return new TestCaseData("KNKK-UJQP-998B"); 1062 | yield return new TestCaseData("149L-RKQ2-Q7XG"); 1063 | yield return new TestCaseData("7GYJ-9XC3-HYH9"); 1064 | yield return new TestCaseData("L3AA-022R-BQL7"); 1065 | yield return new TestCaseData("KENR-M9WG-FCVN"); 1066 | yield return new TestCaseData("BHRJ-PKKK-HL44"); 1067 | yield return new TestCaseData("JU3G-6TT5-UL8N"); 1068 | yield return new TestCaseData("QJRJ-1813-P9AE"); 1069 | yield return new TestCaseData("J5GE-L9LJ-9G7K"); 1070 | yield return new TestCaseData("YVL2-43RP-9RGD"); 1071 | yield return new TestCaseData("QMQC-DDEA-0RW1"); 1072 | yield return new TestCaseData("EXYM-8FB7-AYPX"); 1073 | yield return new TestCaseData("1FFJ-NMTD-QV3G"); 1074 | yield return new TestCaseData("C2Q0-KDMD-YD80"); 1075 | yield return new TestCaseData("PRT8-WL3J-K6XM"); 1076 | yield return new TestCaseData("9M53-N3VE-HBDV"); 1077 | yield return new TestCaseData("5JCV-XYMH-NCFR"); 1078 | yield return new TestCaseData("WRDN-YNFF-QP1C"); 1079 | yield return new TestCaseData("3C5N-8M8R-WPUE"); 1080 | yield return new TestCaseData("VWPT-QJ89-J27T"); 1081 | yield return new TestCaseData("C80V-XYMH-6742"); 1082 | yield return new TestCaseData("VHWM-AR3C-HGF1"); 1083 | yield return new TestCaseData("QAKF-MU9V-3RDE"); 1084 | yield return new TestCaseData("QL00-J517-ALFX"); 1085 | yield return new TestCaseData("7Y92-HRJC-QT9F"); 1086 | yield return new TestCaseData("5FUH-YUQT-WU9A"); 1087 | yield return new TestCaseData("HV6J-1HH4-GF4D"); 1088 | yield return new TestCaseData("QM8U-JK0Q-KBXP"); 1089 | yield return new TestCaseData("EHPE-A6C1-7YMX"); 1090 | yield return new TestCaseData("5QEL-5MNA-HBCU"); 1091 | yield return new TestCaseData("XX55-B4RU-ULBR"); 1092 | yield return new TestCaseData("GM3H-AJ00-LTRD"); 1093 | yield return new TestCaseData("M8H8-MHBT-GPT1"); 1094 | yield return new TestCaseData("JCT2-9GQV-BU8M"); 1095 | yield return new TestCaseData("BL3N-0NJJ-3PVN"); 1096 | yield return new TestCaseData("3GAA-FWNM-CW1A"); 1097 | yield return new TestCaseData("RFUE-5UX8-RQF3"); 1098 | yield return new TestCaseData("ED19-HQR0-0KXC"); 1099 | yield return new TestCaseData("NJ8P-3UTT-9GGV"); 1100 | yield return new TestCaseData("145G-XPRQ-2WTM"); 1101 | yield return new TestCaseData("F053-7RR5-G3RP"); 1102 | yield return new TestCaseData("CQQF-9983-R0QM"); 1103 | yield return new TestCaseData("XML4-AJBB-PJ1M"); 1104 | yield return new TestCaseData("CQWL-UE9R-LG1G"); 1105 | yield return new TestCaseData("2EBF-DV8A-F0M4"); 1106 | yield return new TestCaseData("N34D-CD6D-E4NX"); 1107 | yield return new TestCaseData("71U8-5KK0-UE1R"); 1108 | yield return new TestCaseData("4Y94-Q93K-WRB5"); 1109 | yield return new TestCaseData("E2N7-02PF-WLH9"); 1110 | yield return new TestCaseData("QE1B-MKUJ-CKNU"); 1111 | yield return new TestCaseData("LUVK-2YNG-3LKJ"); 1112 | yield return new TestCaseData("86CF-U3ME-WQCJ"); 1113 | yield return new TestCaseData("18NG-LBLR-95UG"); 1114 | yield return new TestCaseData("3R4L-9JW9-RHV7"); 1115 | yield return new TestCaseData("FH4F-212F-5ABF"); 1116 | yield return new TestCaseData("AM8T-YPL8-5H0D"); 1117 | yield return new TestCaseData("3NTG-6ATB-UT15"); 1118 | yield return new TestCaseData("FCJU-T05E-Q48X"); 1119 | yield return new TestCaseData("HHWJ-46YQ-H5A4"); 1120 | yield return new TestCaseData("PRT8-M8QP-2NDX"); 1121 | yield return new TestCaseData("QVWT-FTW2-HBXE"); 1122 | yield return new TestCaseData("B5P5-RBU8-3LGF"); 1123 | yield return new TestCaseData("M42A-69DA-WNKJ"); 1124 | yield return new TestCaseData("3F6J-XFQT-JRND"); 1125 | yield return new TestCaseData("K5PA-UAQT-88RL"); 1126 | yield return new TestCaseData("XTWF-N9GP-E3XK"); 1127 | yield return new TestCaseData("FNT8-765U-VA98"); 1128 | yield return new TestCaseData("DUFM-Y8GW-EYXQ"); 1129 | yield return new TestCaseData("G5K8-73EA-4D5F"); 1130 | yield return new TestCaseData("9M53-21Q6-TV3R"); 1131 | yield return new TestCaseData("Y4C3-3BY6-YU2C"); 1132 | yield return new TestCaseData("W2BH-MMPM-0AB8"); 1133 | yield return new TestCaseData("38Y3-03Q4-GAU3"); 1134 | yield return new TestCaseData("9NBV-6LC1-5R4E"); 1135 | yield return new TestCaseData("Y1UP-C03B-RR8F"); 1136 | yield return new TestCaseData("YAAN-L0EU-FU11"); 1137 | yield return new TestCaseData("46P8-KHY6-NDDB"); 1138 | yield return new TestCaseData("P08B-J517-QDDL"); 1139 | yield return new TestCaseData("Q8J7-RM3K-87KT"); 1140 | yield return new TestCaseData("TK5T-HY7N-VYUN"); 1141 | yield return new TestCaseData("0TMU-X17N-FE78"); 1142 | yield return new TestCaseData("8RJA-ARBL-3XFJ"); 1143 | yield return new TestCaseData("K39L-5LHH-563Q"); 1144 | yield return new TestCaseData("DYK8-AVHM-HKT7"); 1145 | yield return new TestCaseData("GCKH-D93G-N3MF"); 1146 | yield return new TestCaseData("EJR4-K8B1-FLC3"); 1147 | yield return new TestCaseData("1DXT-J9B0-4L3N"); 1148 | yield return new TestCaseData("0UPH-UBXL-YCMR"); 1149 | yield return new TestCaseData("MJFA-VJL8-JHA4"); 1150 | yield return new TestCaseData("JUFV-GG2N-E234"); 1151 | yield return new TestCaseData("CCQ4-WXNA-E7CF"); 1152 | yield return new TestCaseData("JYC8-48VV-P2JD"); 1153 | yield return new TestCaseData("0WGH-QJCD-5Y33"); 1154 | yield return new TestCaseData("C95M-Q19V-R7QX"); 1155 | yield return new TestCaseData("5LYP-NL32-YPYU"); 1156 | yield return new TestCaseData("NDM3-3R1R-XKC5"); 1157 | yield return new TestCaseData("HLUB-TXJ8-LNNU"); 1158 | yield return new TestCaseData("XTTC-QB1Q-2MLJ"); 1159 | yield return new TestCaseData("K2JA-GJ84-MCPD"); 1160 | yield return new TestCaseData("PEF5-5H49-QQFE"); 1161 | yield return new TestCaseData("0PU7-UARU-JXNF"); 1162 | yield return new TestCaseData("X0C0-AL07-0QVC"); 1163 | yield return new TestCaseData("QLY0-F6LG-EJLF"); 1164 | yield return new TestCaseData("7GH4-J78M-HD2Q"); 1165 | yield return new TestCaseData("5K7B-XPFE-AH8V"); 1166 | yield return new TestCaseData("PNH4-K43A-X76K"); 1167 | yield return new TestCaseData("K30B-AKRD-QG24"); 1168 | yield return new TestCaseData("K7MF-MWLF-UQGD"); 1169 | yield return new TestCaseData("B3R0-8QBP-JVDX"); 1170 | yield return new TestCaseData("GXXW-Y32D-GE8W"); 1171 | yield return new TestCaseData("G3YD-63KT-UVXA"); 1172 | yield return new TestCaseData("PAU3-V3R7-AWJJ"); 1173 | yield return new TestCaseData("JN9L-QGRK-P1Y7"); 1174 | yield return new TestCaseData("CGWP-ARQ2-JKY1"); 1175 | yield return new TestCaseData("WB4T-L29W-HQDU"); 1176 | yield return new TestCaseData("J0GC-2MQE-12WJ"); 1177 | yield return new TestCaseData("4GUG-1UQF-20EG"); 1178 | yield return new TestCaseData("313V-KLEF-M4KC"); 1179 | yield return new TestCaseData("XAKB-QLAJ-J33A"); 1180 | yield return new TestCaseData("W098-NAMG-DGNM"); 1181 | yield return new TestCaseData("GAA1-L2TF-7UWQ"); 1182 | yield return new TestCaseData("DRED-HCTM-FEAB"); 1183 | yield return new TestCaseData("BTQ2-1CWE-669K"); 1184 | } 1185 | } 1186 | } -------------------------------------------------------------------------------- /src/Powells.CouponCodeTests/Powell.CouponCodeTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | --------------------------------------------------------------------------------