├── .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 | 
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 © 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 |
--------------------------------------------------------------------------------