asxinyu/KwCombinatorics.Core

初始化
dongbinhui authored at 2023-02-23 22:25:53
c32e213
Tree
0 Parent(s)
Summary: 10 changed files with 4120 additions and 0 deletions.
Added +218 -0
Added +63 -0
Added +372 -0
Added +729 -0
Added +159 -0
Added +9 -0
Added +25 -0
Added +743 -0
Added +1190 -0
Added +612 -0
Added +218 -0
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..1fc171e
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,218 @@
+# EditorConfig is awesome:http://EditorConfig.org
+# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference
+
+# top-most EditorConfig file
+root = true
+
+# Don't use tabs for indentation.
+[*]
+indent_style = space
+# (Please don't specify an indent_size here; that has too many unintended consequences.)
+
+# Code files
+[*.{cs,csx,vb,vbx}]
+indent_size = 4
+insert_final_newline = false
+charset = utf-8-bom
+end_of_line = crlf
+
+# Xml project files
+[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
+indent_size = 4
+
+# Xml config files
+[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
+indent_size = 2
+
+# JSON files
+[*.json]
+indent_size = 2
+
+# Dotnet code style settings:
+[*.{cs,vb}]
+# Sort using and Import directives with System.* appearing first
+dotnet_sort_system_directives_first = true
+
+csharp_indent_case_contents = true
+csharp_indent_switch_labels = true
+csharp_indent_labels = flush_left
+
+#csharp_space_after_cast = true
+#csharp_space_after_keywords_in_control_flow_statements = true
+#csharp_space_between_method_declaration_parameter_list_parentheses = true
+#csharp_space_between_method_call_parameter_list_parentheses = true
+#csharp_space_between_parentheses = control_flow_statements, type_casts
+
+# 单行放置代码
+csharp_preserve_single_line_statements = true
+csharp_preserve_single_line_blocks = true
+
+# Avoid "this." and "Me." if not necessary
+dotnet_style_qualification_for_field = false:warning
+dotnet_style_qualification_for_property = false:warning
+dotnet_style_qualification_for_method = false:warning
+dotnet_style_qualification_for_event = false:warning
+
+# Use language keywords instead of framework type names for type references
+dotnet_style_predefined_type_for_locals_parameters_members = false:suggestion
+dotnet_style_predefined_type_for_member_access = false:suggestion
+#dotnet_style_require_accessibility_modifiers = for_non_interface_members:none/always:suggestion
+
+# Suggest more modern language features when available
+dotnet_style_object_initializer = true:suggestion
+dotnet_style_collection_initializer = true:suggestion
+dotnet_style_coalesce_expression = true:suggestion
+dotnet_style_null_propagation = true:suggestion
+dotnet_style_explicit_tuple_names = true:suggestion
+dotnet_style_prefer_inferred_tuple_names = true:suggestion
+dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
+dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
+tab_width = 4
+
+# CSharp code style settings:
+[*.cs]
+# Prefer "var" everywhere
+csharp_style_var_for_built_in_types = true:warning
+csharp_style_var_when_type_is_apparent = true:warning
+csharp_style_var_elsewhere = true:warning
+
+# Prefer method-like constructs to have a block body
+csharp_style_expression_bodied_methods = when_on_single_line:suggestion
+csharp_style_expression_bodied_constructors = when_on_single_line:suggestion
+csharp_style_expression_bodied_operators = when_on_single_line:suggestion
+
+# Prefer property-like constructs to have an expression-body
+csharp_style_expression_bodied_properties = true:suggestion
+csharp_style_expression_bodied_indexers = true:suggestion
+#csharp_style_expression_bodied_accessors = true:suggestion
+
+# Suggest more modern language features when available
+csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
+csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
+csharp_style_inlined_variable_declaration = true:suggestion
+
+csharp_prefer_simple_default_expression = true:suggestion
+csharp_style_deconstructed_variable_declaration = true:suggestion
+csharp_style_pattern_local_over_anonymous_function = true:suggestion
+
+csharp_style_throw_expression = true:suggestion
+csharp_style_conditional_delegate_call = true:suggestion
+
+# 单行不需要大括号
+csharp_prefer_braces = false:suggestion
+
+# Newline settings
+csharp_new_line_before_open_brace = all
+csharp_new_line_before_else = true
+csharp_new_line_before_catch = true
+csharp_new_line_before_finally = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_between_query_expression_clauses = true
+csharp_using_directive_placement = outside_namespace:silent
+csharp_style_expression_bodied_accessors = true:silent
+csharp_style_expression_bodied_lambdas = true:silent
+csharp_style_expression_bodied_local_functions = false:silent
+csharp_style_prefer_parameter_null_checking = true:suggestion
+csharp_prefer_simple_using_statement = true:suggestion
+csharp_style_namespace_declarations = block_scoped:silent
+csharp_style_prefer_method_group_conversion = true:silent
+
+[*.md]
+trim_trailing_whitespace = false
+[*.cs]
+#### 命名样式 ####
+
+# 命名规则
+
+dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
+dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
+dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
+
+dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.types_should_be_pascal_case.symbols = types
+dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
+
+dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
+dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
+
+# 符号规范
+
+dotnet_naming_symbols.interface.applicable_kinds = interface
+dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.interface.required_modifiers = 
+
+dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
+dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.types.required_modifiers = 
+
+dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
+dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.non_field_members.required_modifiers = 
+
+# 命名样式
+
+dotnet_naming_style.begins_with_i.required_prefix = I
+dotnet_naming_style.begins_with_i.required_suffix = 
+dotnet_naming_style.begins_with_i.word_separator = 
+dotnet_naming_style.begins_with_i.capitalization = pascal_case
+
+dotnet_naming_style.pascal_case.required_prefix = 
+dotnet_naming_style.pascal_case.required_suffix = 
+dotnet_naming_style.pascal_case.word_separator = 
+dotnet_naming_style.pascal_case.capitalization = pascal_case
+
+dotnet_naming_style.pascal_case.required_prefix = 
+dotnet_naming_style.pascal_case.required_suffix = 
+dotnet_naming_style.pascal_case.word_separator = 
+dotnet_naming_style.pascal_case.capitalization = pascal_case
+csharp_space_around_binary_operators = before_and_after
+
+[*.vb]
+#### 命名样式 ####
+
+# 命名规则
+
+dotnet_naming_rule.interface_should_be_以_i_开始.severity = suggestion
+dotnet_naming_rule.interface_should_be_以_i_开始.symbols = interface
+dotnet_naming_rule.interface_should_be_以_i_开始.style = 以_i_开始
+
+dotnet_naming_rule.类型_should_be_帕斯卡拼写法.severity = suggestion
+dotnet_naming_rule.类型_should_be_帕斯卡拼写法.symbols = 类型
+dotnet_naming_rule.类型_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
+
+dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.severity = suggestion
+dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.symbols = 非字段成员
+dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
+
+# 符号规范
+
+dotnet_naming_symbols.interface.applicable_kinds = interface
+dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
+dotnet_naming_symbols.interface.required_modifiers = 
+
+dotnet_naming_symbols.类型.applicable_kinds = class, struct, interface, enum
+dotnet_naming_symbols.类型.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
+dotnet_naming_symbols.类型.required_modifiers = 
+
+dotnet_naming_symbols.非字段成员.applicable_kinds = property, event, method
+dotnet_naming_symbols.非字段成员.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
+dotnet_naming_symbols.非字段成员.required_modifiers = 
+
+# 命名样式
+
+dotnet_naming_style.以_i_开始.required_prefix = I
+dotnet_naming_style.以_i_开始.required_suffix = 
+dotnet_naming_style.以_i_开始.word_separator = 
+dotnet_naming_style.以_i_开始.capitalization = pascal_case
+
+dotnet_naming_style.帕斯卡拼写法.required_prefix = 
+dotnet_naming_style.帕斯卡拼写法.required_suffix = 
+dotnet_naming_style.帕斯卡拼写法.word_separator = 
+dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case
+
+dotnet_naming_style.帕斯卡拼写法.required_prefix = 
+dotnet_naming_style.帕斯卡拼写法.required_suffix = 
+dotnet_naming_style.帕斯卡拼写法.word_separator = 
+dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case
Added +63 -0
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..1ff0c42
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,63 @@
+###############################################################################
+# Set default behavior to automatically normalize line endings.
+###############################################################################
+* text=auto
+
+###############################################################################
+# Set default behavior for command prompt diff.
+#
+# This is need for earlier builds of msysgit that does not have it on by
+# default for csharp files.
+# Note: This is only used by command line
+###############################################################################
+#*.cs     diff=csharp
+
+###############################################################################
+# Set the merge driver for project and solution files
+#
+# Merging from the command prompt will add diff markers to the files if there
+# are conflicts (Merging from VS is not affected by the settings below, in VS
+# the diff markers are never inserted). Diff markers may cause the following 
+# file extensions to fail to load in VS. An alternative would be to treat
+# these files as binary and thus will always conflict and require user
+# intervention with every merge. To do so, just uncomment the entries below
+###############################################################################
+#*.sln       merge=binary
+#*.csproj    merge=binary
+#*.vbproj    merge=binary
+#*.vcxproj   merge=binary
+#*.vcproj    merge=binary
+#*.dbproj    merge=binary
+#*.fsproj    merge=binary
+#*.lsproj    merge=binary
+#*.wixproj   merge=binary
+#*.modelproj merge=binary
+#*.sqlproj   merge=binary
+#*.wwaproj   merge=binary
+
+###############################################################################
+# behavior for image files
+#
+# image files are treated as binary by default.
+###############################################################################
+#*.jpg   binary
+#*.png   binary
+#*.gif   binary
+
+###############################################################################
+# diff behavior for common document formats
+# 
+# Convert binary document formats to text before diffing them. This feature
+# is only available from the command line. Turn it on by uncommenting the 
+# entries below.
+###############################################################################
+#*.doc   diff=astextplain
+#*.DOC   diff=astextplain
+#*.docx  diff=astextplain
+#*.DOCX  diff=astextplain
+#*.dot   diff=astextplain
+#*.DOT   diff=astextplain
+#*.pdf   diff=astextplain
+#*.PDF   diff=astextplain
+#*.rtf   diff=astextplain
+#*.RTF   diff=astextplain
Added +372 -0
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a51ed08
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,372 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Bb]in - JT/
+[Oo]bj/
+[Oo]ut/
+[Ll]og/
+[Dd]ata/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*.json
+coverage*.xml
+coverage*.info
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
+
+# Fody - auto-generated XML schema
+FodyWeavers.xsd
+
+/.idea
+Config
+*.txt
+appsettings.json
+Helper.cs
+*.zip
\ No newline at end of file
Added +729 -0
diff --git a/Combination.cs b/Combination.cs
new file mode 100644
index 0000000..869afb0
--- /dev/null
+++ b/Combination.cs
@@ -0,0 +1,729 @@
+//
+// KwCombinatorics v3.0.x/v4.0.x
+// Copyright © 2009-2012 Kasey Osborn (Kasewick@gmail.com)
+// Ms-PL - Use and redistribute freely
+//
+// File: Combination.cs
+//
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Kw.Combinatorics
+{
+    /// <summary>
+    /// Represents an ascending sequence of non-repeating picks from a supplied number of choices.
+    /// </summary>
+    /// <remarks>
+    /// <para>
+    /// <em>K</em>-combinations are also known as pick-combinations.
+    /// 
+    /// The defining variables are <em>n</em> which is the number of possible choices and
+    /// <em>k</em> which is the number of non-repeatable picks from those choices.
+    /// 
+    /// This is contrasted to <em>k</em>-multicombinations where the elements in a row may repeat.
+    /// </para>
+    /// <para>
+    /// The <see cref="Combination"/> class produces <em>k</em>-combinations with ascending
+    /// elements.
+    /// 
+    /// While sequence order of the elements is not a requirement of <em>k</em>-combinations,
+    /// producing an ascending sequence allows ranking the rows into an ordered table.
+    /// </para>
+    /// <para>
+    /// Use the <see cref="Picks"/> property to get the number of elements (<em>k</em>)
+    /// of a <see cref="Combination"/> taken from a possible number of
+    /// <see cref="Choices"/> (<em>n</em>).
+    /// 
+    /// Use the <see cref="RowCount"/> property to get the number of distinct possible
+    /// <see cref="Combination"/> sequences.
+    /// 
+    /// Use the <see cref="P:Kw.Combinatorics.Combination.Item(System.Int32)">indexer</see>
+    /// to get a specified element of the sequence.
+    /// 
+    /// Use the <see cref="GetEnumerator">default enumerator</see> to iterate thru
+    /// the elements of a <see cref="Combination"/>.
+    /// 
+    /// Use the <see cref="Permute">Permute</see> method to pick objects from a supplied array
+    /// of choices based on the current sequence.
+    /// </para>
+    /// <para>
+    /// Use the <see cref="Rank"/> property to get or set the row index in a lexicographically
+    /// ordered <see cref="Combination"/> table of all possible sequences in an ascending order.
+    /// 
+    /// Use <see cref="GetRows"/> to iterate thru all possible sequences
+    /// of the <see cref="Combination"/> ordered by <see cref="Rank"/>.
+    /// 
+    /// Use <see cref="GetRowsForAllPicks"/> to iterate
+    /// thru every table of all picks in the range (1..<see cref="Picks"/>).
+    /// </para>
+    /// <para>
+    /// The default appearance of a <see cref="Combination"/> row is a list of
+    /// integers (starting at 0) enclosed in braces. The appearance may be tailored 3 ways:
+    /// <ul>
+    ///   <li>
+    ///     Map each element to a different value using the
+    ///     <see cref="GetEnumerator">default enumerator</see> or the
+    ///     <see cref="P:Kw.Combinatorics.Multicombination.Item(System.Int32)">indexer</see>.
+    ///   </li>
+    ///   <li>
+    ///     Use the <see cref="Permute">Permute</see> method and output the rearranged values.
+    ///   </li>
+    ///   <li>
+    ///     Define a subclass of <see cref="Combination"/> and override
+    ///     <see cref="System.Object.ToString">ToString()</see>.
+    ///     (See <see cref="GetRowsForAllPicks"/> for an example.)
+    ///   </li>
+    /// </ul>
+    /// </para>
+    /// <para>
+    /// For more information about <em>k</em>-combinations, see:
+    /// </para>
+    /// <para>
+    /// <em>http://en.wikipedia.org/wiki/Combination</em>
+    /// </para>
+    /// </remarks>
+    /// <example>
+    /// <para>
+    /// Iterating thru <c>new Combination (6, 3).GetRows()</c> produces:
+    /// </para>
+    /// <para>
+    /// <c>{ 0, 1, 2 }</c><br/>
+    /// <c>{ 0, 1, 3 }</c><br/>
+    /// <c>{ 0, 1, 4 }</c><br/>
+    /// <c>{ 0, 1, 5 }</c><br/>
+    /// <c>{ 0, 2, 3 }</c><br/>
+    /// <c>{ 0, 2, 4 }</c><br/>
+    /// <c>{ 0, 2, 5 }</c><br/>
+    /// <c>{ 0, 3, 4 }</c><br/>
+    /// <c>{ 0, 3, 5 }</c><br/>
+    /// <c>{ 0, 4, 5 }</c><br/>
+    /// <c>{ 1, 2, 3 }</c><br/>
+    /// <c>{ 1, 2, 4 }</c><br/>
+    /// <c>{ 1, 2, 5 }</c><br/>
+    /// <c>{ 1, 3, 4 }</c><br/>
+    /// <c>{ 1, 3, 5 }</c><br/>
+    /// <c>{ 1, 4, 5 }</c><br/>
+    /// <c>{ 2, 3, 4 }</c><br/>
+    /// <c>{ 2, 3, 5 }</c><br/>
+    /// <c>{ 2, 4, 5 }</c><br/>
+    /// <c>{ 3, 4, 5 }</c>
+    /// </para>
+    /// </example>
+    public class Combination :
+        IComparable,
+        System.Collections.IEnumerable,
+        IComparable<Combination>,
+        IEquatable<Combination>,
+        IEnumerable<int>
+    {
+        private int[] data;     // The picks for the current rank. Length is 'k'.
+        private int choices;    // Number of possible values 'n'.
+        private long rowCount;  // Row count of the table of k-combinations.
+        private long rank;      // Row index.
+
+        #region Constructors
+
+        /// <summary>
+        /// Make an empty <see cref="Combination"/>.
+        /// </summary>
+        public Combination ()
+        {
+            this.data = new int[0];
+            this.choices = 0;
+            this.rowCount = 0;
+            this.rank = 0;
+        }
+
+
+        /// <summary>
+        /// Make a copy of a <see cref="Combination"/>.
+        /// </summary>
+        /// <param name="source">Instance to copy.</param>
+        /// <exception cref="ArgumentNullException">When <em>source</em> is <b>null</b>.</exception>
+        public Combination (Combination source)
+        {
+            if (source == null)
+                throw new ArgumentNullException ("source");
+
+            this.data = new int[source.data.Length];
+            source.data.CopyTo (this.data, 0);
+
+            this.choices = source.choices;
+            this.rowCount = source.RowCount;
+            this.rank = source.rank;
+        }
+
+
+        /// <summary>
+        /// Make a new <see cref="Combination"/> from the supplied
+        /// <em>choices</em> of all <see cref="Picks"/> of <see cref="Rank"/> 0.
+        /// </summary>
+        /// <param name="choices">Number of elements in the sequence.</param>
+        /// <exception cref="ArgumentOutOfRangeException">
+        /// When <em>choices</em> less than 0.
+        /// </exception>
+        public Combination (int choices)
+        {
+            if (choices < 0)
+                throw new ArgumentOutOfRangeException ("choices", "Value is less than zero.");
+
+            this.data = new int[choices];
+            for (int ki = 0; ki < this.data.Length; ++ki)
+                this.data[ki] = ki;
+
+            this.choices = choices;
+            this.rowCount = choices == 0? 0 : 1;
+            this.rank = 0;
+        }
+
+
+        /// <summary>
+        /// Make a new <see cref="Combination"/> from the supplied
+        /// <em>choices</em> and <em>picks</em> of <see cref="Rank"/> 0.
+        /// </summary>
+        /// <param name="choices">Number of values to pick from.</param>
+        /// <param name="picks">Number of elements in the sequence.</param>
+        /// <example>
+        /// <code source="Examples\Combination\CnExample01\CnExample01.cs" lang="cs" />
+        /// </example>
+        /// <exception cref="ArgumentOutOfRangeException">
+        /// When negative value supplied; when <em>picks</em> greater than <em>choices</em>.
+        /// </exception>
+        /// <exception cref="OverflowException">When the numbers are just too big.</exception>
+        public Combination (int choices, int picks)
+        {
+            if (choices < 0)
+                throw new ArgumentOutOfRangeException ("choices", "Value is less than zero.");
+
+            if (picks < 0)
+                throw new ArgumentOutOfRangeException ("picks", "Value is less than zero.");
+
+            if (picks > choices)
+                throw new ArgumentOutOfRangeException ("picks", "Value is greater than choices.");
+
+            this.data = new int[picks];
+            for (int ki = 0; ki < picks; ++ki)
+                this.data[ki] = ki;
+
+            this.choices = choices;
+            this.rowCount = picks == 0? 0 : Combinatoric.BinomialCoefficient (choices, picks);
+            this.rank = 0;
+        }
+
+
+        /// <summary>
+        /// Make a new <see cref="Combination"/> from the supplied
+        /// <em>choices</em> and <em>picks</em> of the supplied <em>rank</em>.
+        /// </summary>
+        /// <remarks>
+        /// If the supplied <em>rank</em> is out of the range (0..<see cref="RowCount"/>-1),
+        /// it will be normalized to the valid range. For example, a value of -1 will
+        /// produce the last row in the ordered table.
+        /// </remarks>
+        /// <param name="choices">Number of values to pick from.</param>
+        /// <param name="picks">Number of elements in the sequence.</param>
+        /// <param name="rank">Initial row index in the ordered <see cref="Combination"/> table.</param>
+        /// <example>
+        /// <code source="Examples\Combination\CnExample05\CnExample05.cs" lang="cs" />
+        /// </example>
+        /// <exception cref="ArgumentOutOfRangeException">
+        /// When negative value supplied; when <em>picks</em> greater than <em>choices</em>.
+        /// </exception>
+        /// <exception cref="OverflowException">When too many <em>choices</em>.</exception>
+        public Combination (int choices, int picks, long rank)
+        {
+            if (choices < 0)
+                throw new ArgumentOutOfRangeException ("choices", "Value is less than zero.");
+
+            if (picks < 0)
+                throw new ArgumentOutOfRangeException ("picks", "Value is less than zero.");
+
+            if (picks > choices)
+                throw new ArgumentOutOfRangeException ("picks", "Value is greater than choices.");
+
+            this.data = new int[picks];
+            this.choices = choices;
+            this.rowCount = picks == 0? 0 : Combinatoric.BinomialCoefficient (choices, picks);
+            Rank = rank;
+        }
+
+
+        /// <summary>
+        /// Make a new <see cref="Combination"/> from the supplied elements.
+        /// </summary>
+        /// <param name="choices">Number of values to pick from.</param>
+        /// <param name="source">Array of integers.</param>
+        /// <example>
+        /// <code source="Examples\Combination\CnExample04\CnExample04.cs" lang="cs" />
+        /// </example>
+        /// <exception cref="ArgumentNullException">When <em>source</em> is <b>null</b>.</exception>
+        /// <exception cref="ArgumentOutOfRangeException">
+        /// When length of <em>source</em> is greater than <em>picks</em>;
+        /// when <em>source</em> contains invalid data.
+        /// </exception>
+        public Combination (int choices, int[] source)
+        {
+            if (source == null)
+                throw new ArgumentNullException ("source");
+
+            if (choices < 0)
+                throw new ArgumentOutOfRangeException ("choices", "Value is less than zero.");
+
+            if (choices < source.Length)
+                throw new ArgumentOutOfRangeException ("choices", "Value is less than picks.");
+
+            this.data = new int[source.Length];
+            source.CopyTo (this.data, 0);
+            Array.Sort (this.data);
+
+            this.choices = choices;
+            this.rowCount = Picks == 0? 0 : Combinatoric.BinomialCoefficient (choices, Picks);
+
+            for (int ki = 0; ki < Picks; ++ki)
+            {
+                if (this.data[ki] < 0 || this.data[ki] >= choices)
+                    throw new ArgumentOutOfRangeException ("source", "Element is out of range.");
+
+                if (ki > 0)
+                    if (this.data[ki] == this.data[ki-1])
+                        throw new ArgumentOutOfRangeException ("source", "Elements must be unique.");
+            }
+
+            //
+            // Perform ranking:
+            //
+
+            this.rank = 0;
+            int ji = 0;
+            for (int ki = 0; ki < Picks; ++ki)
+            {
+                for (; ji < this.data[ki]; ++ji)
+                    rank += Combinatoric.BinomialCoefficient (Choices - ji - 1, Picks - ki - 1);
+
+                ji = this.data[ki] + 1;
+            }
+        }
+
+        #endregion
+
+        #region Properties
+
+        /// <summary>
+        /// The available number of integers to choose from.
+        /// </summary>
+        /// <remarks>
+        /// Also known as <em>n</em>.
+        /// </remarks>
+        public int Choices
+        {
+            get { return choices; }
+        }
+
+
+        /// <summary>
+        /// Number of elements in the <see cref="Combination"/>.
+        /// </summary>
+        /// <remarks>
+        /// Also known as <em>k</em>.
+        /// </remarks>
+        public int Picks
+        {
+            get { return data.Length; }
+        }
+
+
+        /// <summary>
+        /// Row index in the ordered <see cref="Combination"/> table.
+        /// </summary>
+        /// <remarks>
+        /// Any assigned value out of range will be normalized to (0..<see cref="RowCount"/>-1).
+        /// </remarks>
+        /// <example>
+        /// <code source="Examples\Combination\CnExample04\CnExample04.cs" lang="cs" />
+        /// </example>
+        public long Rank
+        {
+            get { return rank; }
+            set
+            {
+                if (RowCount == 0)
+                    return;
+
+                // Normalize the new rank.
+                if (value < 0)
+                {
+                    rank = value % RowCount;
+                    if (rank < 0)
+                        rank += RowCount;
+                }
+                else
+                    rank = value < RowCount? value : value % RowCount;
+
+                //
+                // Perform unranking:
+                //
+
+                long diminishingRank = RowCount - rank - 1;
+                int combinaticAtom = Choices;
+
+                for (int ki = Picks; ki > 0; --ki)
+                    for (;;)
+                    {
+                        --combinaticAtom;
+
+                        long trialCount = Combinatoric.BinomialCoefficient (combinaticAtom, ki);
+                        if (trialCount <= diminishingRank)
+                        {
+                            diminishingRank -= trialCount;
+                            data[Picks - ki] = Choices - combinaticAtom - 1;
+                            break;
+                        }
+                    }
+            }
+        }
+
+
+        /// <summary>
+        /// Count of distinct sequences in the <see cref="Combination"/> table.
+        /// </summary>
+        public long RowCount
+        {
+            get { return rowCount; }
+        }
+
+
+        /// <summary>
+        /// Get a element of the <see cref="Combination"/> at the supplied column.
+        /// </summary>
+        /// <param name="index">Zero-based index value.</param>
+        /// <returns>Sequence value at <em>index</em>.</returns>
+        /// <example>
+        /// <code source="Examples\Combination\CnExample05\CnExample05.cs" lang="cs" />
+        /// </example>
+        /// <exception cref="IndexOutOfRangeException">
+        /// When <em>index</em> not in range (0..<see cref="Picks"/>-1).
+        /// </exception>
+        public int this[int index]
+        {
+            get { return data[index]; }
+        }
+
+        #endregion
+
+        #region Instance methods
+
+        /// <summary>Compare two <see cref="Combination"/>s.</summary>
+        /// <param name="obj">Target of the comparison.</param>
+        /// <returns>
+        /// A signed integer indicating the sort order of this instance to <em>obj</em>.
+        /// </returns>
+        public int CompareTo (object obj)
+        { return CompareTo (obj as Combination); }
+
+
+        /// <summary>Compare two <see cref="Combination"/>s.</summary>
+        /// <param name="other">Target of the comparison.</param>
+        /// <returns>
+        /// A signed integer indicating the sort order of this instance to <em>other</em>.
+        /// </returns>
+        public int CompareTo (Combination other)
+        {
+            if ((object) other == null)
+                return 1;
+
+            int result = this.Picks - other.Picks;
+            if (result == 0)
+            {
+                result = this.Choices - other.Choices;
+                if (result == 0)
+                {
+                    long rankDiff = this.Rank - other.Rank;
+
+                    if (rankDiff == 0)
+                        result = 0;
+                    else
+                        result = rankDiff < 0 ? -1 : 1;
+                }
+            }
+
+            return result;
+        }
+
+
+        /// <summary>
+        /// Copy the entire sequence to the supplied destination.
+        /// </summary>
+        /// <param name="array">Destination of copy.</param>
+        /// <exception cref="ArgumentNullException">When <em>array</em> is <b>null</b>.</exception>
+        /// <exception cref="ArgumentException">When not enough space in <em>array</em>.</exception>
+        public void CopyTo (int[] array)
+        {
+            if (array == null)
+                throw new ArgumentNullException ("array");
+
+            if (array.Length < Picks)
+                throw new ArgumentException ("Destination array is not long enough.");
+
+            this.data.CopyTo (array, 0);
+        }
+
+
+        /// <summary>
+        /// Indicate whether two <see cref="Combination"/>s have the same value.
+        /// </summary>
+        /// <param name="obj">Target of the comparison.</param>
+        /// <returns>
+        /// <b>true</b> if <em>obj</em> has the same value as this object; otherwise, <b>false</b>.
+        /// </returns>
+        public override bool Equals (object obj)
+        { return Equals (obj as Combination); }
+
+
+        /// <summary>
+        /// Indicate whether two <see cref="Combination"/>s have the same value.
+        /// </summary>
+        /// <param name="other">Target of the comparison.</param>
+        /// <returns>
+        /// <b>true</b> if <em>other</em> has the same value as this instance;
+        /// otherwise, <b>false</b>.
+        /// </returns>
+        public bool Equals (Combination other)
+        {
+            return (object) other != null
+                && other.Rank == Rank && other.Choices == Choices && other.Picks == Picks;
+        }
+
+
+        /// <summary>Get an object-based enumerator of the elements.</summary>
+        /// <returns>Object-based elemental enumerator.</returns>
+        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
+        { return this.GetEnumerator(); }
+
+
+        /// <summary>Enumerate all elements of a <see cref="Combination"/>.</summary>
+        /// <returns>
+        /// An <c>IEnumerator&lt;int&gt;</c> for this <see cref="Combination"/>.
+        /// </returns>
+        /// <example>
+        /// <code source="Examples\Combination\CnExample05\CnExample05.cs" lang="cs" />
+        /// </example>
+        public IEnumerator<int> GetEnumerator ()
+        {
+            for (int ei = 0; ei < Picks; ++ei)
+                yield return this.data[ei];
+        }
+
+
+        /// <summary>Get the hash oode of the <see cref="Combination"/>.</summary>
+        /// <returns>A 32-bit signed integer hash code.</returns>
+        public override int GetHashCode ()
+        { return unchecked ((int) Rank); }
+
+
+        /// <summary>
+        /// Iterate thru all rows of the <see cref="Combination"/> table
+        /// for every value of <see cref="Rank"/> ascending.
+        /// </summary>
+        /// <returns>An iterator for a <see cref="Combination"/> table.</returns>
+        /// <remarks>
+        /// If the start row is not of <see cref="Rank"/> 0, the iteration will wrap around
+        /// so that <see cref="RowCount"/> items are always produced.
+        /// </remarks>
+        /// <example>
+        /// <code source="Examples\Combination\CnExample01\CnExample01.cs" lang="cs" />
+        /// </example>
+        public IEnumerable<Combination> GetRows ()
+        {
+            if (RowCount > 0)
+            {
+                long startRank = rank;
+                for (Combination current = (Combination) MemberwiseClone();;)
+                {
+                    yield return current;
+                    current.Rank = current.Rank + 1;
+                    if (current.Rank == startRank)
+                        break;
+                }
+            }
+        }
+
+
+        /// <summary>
+        /// Iterate thru all rows of all <see cref="Combination"/> tables for every
+        /// pick in the range (1..<see cref="Picks"/>).
+        /// </summary>
+        /// <returns>An iterator for a series of <see cref="Combination"/> tables.</returns>
+        /// <example>
+        /// <code source="Examples\Combination\CnExample02\CnExample02.cs" lang="cs" />
+        /// </example>
+        public IEnumerable<Combination> GetRowsForAllPicks ()
+        {
+            for (int k = 1; k <= Picks; ++k)
+            {
+                Combination current = (Combination) MemberwiseClone();
+
+                current.data = new int[k];
+                for (int ei = 0; ei < current.data.Length; ++ei)
+                    current.data[ei] = ei;
+                current.rowCount = Combinatoric.BinomialCoefficient (choices, k);
+                current.rank = 0;
+
+                for (;;)
+                {
+                    yield return current;
+                    current.Rank = current.Rank + 1;
+                    if (current.Rank == 0)
+                        break;
+                }
+            }
+        }
+
+
+        /// <summary>
+        /// Provide a readable form of the <see cref="Combination"/> sequence.
+        /// </summary>
+        /// <returns>A <c>string</c> that represents the sequence.</returns>
+        /// <remarks>Result is enclosed in braces and separated by commas.</remarks>
+        /// <example>
+        /// <code source="Examples\Combination\CnExample04\CnExample04.cs" lang="cs" />
+        /// </example>
+        public override string ToString ()
+        {
+            if (RowCount == 0)
+                return ("{ }");
+
+            StringBuilder result = new StringBuilder ("{ ");
+
+            for (int ei = 0;;)
+            {
+                result.Append (this.data[ei]);
+
+                ++ei;
+                if (ei >= Picks)
+                    break;
+
+                result.Append (", ");
+            }
+
+            result.Append (" }");
+
+            return result.ToString();
+        }
+
+        #endregion
+
+        #region Static methods
+
+        /// <summary>
+        /// Apply a <see cref="Combination"/> sequence to rearrange the supplied list or array.
+        /// </summary>
+        /// <typeparam name="T">Type of items to rearrange.</typeparam>
+        /// <param name="arrangement">New arrangement for items.</param>
+        /// <param name="source">List of items to rearrange.</param>
+        /// <returns>List of rearranged items.</returns>
+        /// <example>
+        /// <code source="Examples\Combination\CnExample03\CnExample03.cs" lang="cs" />
+        /// </example>
+        /// <exception cref="ArgumentNullException">
+        /// When <em>arrangement</em> or <em>source</em> is <b>null</b>.
+        /// </exception>
+        /// <exception cref="ArgumentException">
+        /// When length of <em>source</em> is less than <see cref="Picks"/>.
+        /// </exception>
+        public static List<T> Permute<T> (Combination arrangement, IList<T> source)
+        {
+            if (arrangement == null)
+                throw new ArgumentNullException ("arrangement");
+
+            if (source == null)
+                throw new ArgumentNullException ("source");
+
+            if (source.Count < arrangement.Choices)
+                throw new ArgumentException ("Not enough supplied values.", "source");
+
+            List<T> result = new List<T> (arrangement.Picks);
+
+            for (int ai = 0; ai < arrangement.Picks; ++ai)
+                result.Add (source[arrangement[ai]]);
+
+            return result;
+        }
+
+        
+        /// <summary>Indicate whether 2 <see cref="Combination"/>s are equal.</summary>
+        /// <param name="param1">A <see cref="Combination"/> sequence.</param>
+        /// <param name="param2">A <see cref="Combination"/> sequence.</param>
+        /// <returns><b>true</b> if supplied sequences are equal;
+        /// otherwise, <b>false</b>.</returns>
+        public static bool operator == (Combination param1, Combination param2)
+        {
+            if ((object) param1 == null)
+                return (object) param2 == null;
+            else
+                return param1.Equals (param2);
+        }
+
+
+        /// <summary>Indicate whether 2 <see cref="Combination"/>s are not equal.</summary>
+        /// <param name="param1">A <see cref="Combination"/> sequence.</param>
+        /// <param name="param2">A <see cref="Combination"/> sequence.</param>
+        /// <returns><b>true</b> if supplied sequences are not equal;
+        /// otherwise, <b>false</b>.</returns>
+        public static bool operator != (Combination param1, Combination param2)
+        { return !(param1 == param2); }
+
+
+        /// <summary>Indicate whether the left <see cref="Combination"/> is less than
+        /// the right <see cref="Combination"/>.</summary>
+        /// <param name="param1">A <see cref="Combination"/> sequence.</param>
+        /// <param name="param2">A <see cref="Combination"/> sequence.</param>
+        /// <returns><b>true</b> if the left sequence is less than
+        /// the right sequence; otherwise, <b>false</b>.</returns>
+        public static bool operator < (Combination param1, Combination param2)
+        {
+            if ((object) param1 == null)
+                return (object) param2 != null;
+            else
+                return param1.CompareTo (param2) < 0;
+        }
+
+
+        /// <summary>Indicate whether the left <see cref="Combination"/> is greater than
+        /// or equal to the right <see cref="Combination"/>.</summary>
+        /// <param name="param1">A <see cref="Combination"/> sequence.</param>
+        /// <param name="param2">A <see cref="Combination"/> sequence.</param>
+        /// <returns><b>true</b> if the left sequence is greater than or equal to
+        /// the right sequence; otherwise, <b>false</b>.</returns>
+        public static bool operator >= (Combination param1, Combination param2)
+        { return !(param1 < param2); }
+
+
+        /// <summary>Indicate whether the left <see cref="Combination"/> is greater than
+        /// the right <see cref="Combination"/>.</summary>
+        /// <param name="param1">A <see cref="Combination"/> sequence.</param>
+        /// <param name="param2">A <see cref="Combination"/> sequence.</param>
+        /// <returns><b>true</b> if the left sequence is greater than
+        /// the right sequence; otherwise, <b>false</b>.</returns>
+        public static bool operator > (Combination param1, Combination param2)
+        {
+            if ((object) param1 == null)
+                return false;
+            else
+                return param1.CompareTo (param2) > 0;
+        }
+
+
+        /// <summary>Indicate whether the left <see cref="Combination"/> is less than or equal
+        /// to the right <see cref="Combination"/>.</summary>
+        /// <param name="param1">A <see cref="Combination"/> sequence.</param>
+        /// <param name="param2">A <see cref="Combination"/> sequence.</param>
+        /// <returns><b>true</b> if the left sequence is less than or equal to
+        /// the right sequence; otherwise, <b>false</b>.</returns>
+        public static bool operator <= (Combination param1, Combination param2)
+        { return !(param1 > param2); }
+
+        #endregion
+    }
+}
Added +159 -0
diff --git a/Combinatoric.cs b/Combinatoric.cs
new file mode 100644
index 0000000..843428c
--- /dev/null
+++ b/Combinatoric.cs
@@ -0,0 +1,159 @@
+using System;
+using System.Collections.Generic;
+
+namespace Kw.Combinatorics
+{
+    /// <summary>
+    /// Provides static methods for combinatorics.
+    /// </summary>
+    /// <remarks>
+    /// This class cannot be instantiated.
+    /// </remarks>
+    public static class Combinatoric
+    {
+        #region Private static fields
+
+        static internal readonly long[] factorial = new long[]
+        {
+            1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 
+            479001600, 6227020800, 87178291200, 1307674368000, 20922789888000,
+            355687428096000, 6402373705728000, 121645100408832000, 2432902008176640000
+        };
+
+        static private List<long[]> pascalsTriangle = null;
+        static private int pascalsTriangleMaxN = -1;
+
+        #endregion
+
+        #region Private static methods
+
+        // Returns as many complete rows of Pascal's triangle as possible.
+        static private List<long[]> BuildPascalsTriangle ()
+        {
+            var result = new List<long[]> { new long[] { 1 } };
+
+            try
+            {
+                for (int n = 1; ; ++n)
+                {
+                    var row = new long[n+1];
+                    row[0] = 1;
+                    for (int k = 1; k <= n-1; ++k)
+                        row[k] = checked (result[n-1][k-1] + result[n-1][k]);
+                    row[n] = 1;
+                    result.Add (row);
+                }
+            }
+            catch (OverflowException)
+            { }
+
+            return result;
+        }
+
+        #endregion
+
+        #region Public static methods
+
+        /// <summary>
+        /// Returns the binomial coefficient of the supplied values.
+        /// </summary>
+        /// <param name="n">Number of choices.</param>
+        /// <param name="k">Number of picks.</param>
+        /// <returns>
+        /// The binomial coefficient of <em>n</em> choose <em>k</em>.
+        /// </returns>
+        /// <remarks>
+        /// The result is equal to row <em>n</em>, column <em>k</em> of Pascal's triangle
+        /// with counting starting at 0.
+        /// </remarks>
+        /// <example>
+        /// <para>
+        /// The number of rows in a <see cref="Combination"/> table of <em>k</em> picks
+        /// from <em>n</em> choices is:<br />
+        /// <br />
+        /// <c>Combinatoric.BinomialCoefficient (n, k)</c>
+        /// </para>
+        /// <para>
+        /// The number of rows in a <see cref="Multicombination"/> table of <em>k</em> picks
+        /// from <em>n</em> choices is:<br />
+        /// <br />
+        /// <c>Combinatoric.BinomialCoefficient (k+n-1, k)</c>
+        /// </para>
+        /// <para>
+        /// An exception to the above formulas is the special case where the numer of elements
+        /// is 0. While mathematics treats this result as 1 row containing the empty product,
+        /// this library returns 0 rows.
+        /// </para>
+        /// </example>
+        /// <exception cref="OverflowException">When the numbers are just too big.</exception>
+        static public long BinomialCoefficient (int n, int k)
+        {
+            if (k < 0 || k > n)
+                return 0;
+
+            if (n <= pascalsTriangleMaxN)
+                return pascalsTriangle[n][k];
+
+            if (pascalsTriangle == null)
+            {
+                pascalsTriangle = BuildPascalsTriangle ();
+                pascalsTriangleMaxN = pascalsTriangle.Count - 1;
+
+                if (n < pascalsTriangleMaxN)
+                    return pascalsTriangle[n][k];
+            }
+
+            // Row is beyond precalculated table so fall back to multiplicative formula:
+
+            if (k > n - k)
+                k = n - k;
+
+            int factor = n - k;
+            long bc = 1;
+
+            for (int ki = 1; ki <= k; ++ki)
+            {
+                ++factor;
+                bc = checked (bc * factor) / ki;
+            }
+
+            return bc;
+        }
+
+
+        /// <summary>Returns the factorial of the supplied value.</summary>
+        /// <param name="value">Non-negative integer.</param>
+        /// <returns>
+        /// For increasing values starting at 0, returns
+        /// 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 
+        /// 479001600, 6227020800, 87178291200, 1307674368000, 20922789888000,
+        /// 355687428096000, 6402373705728000, 121645100408832000, 2432902008176640000.
+        /// </returns>
+        /// <example>
+        /// <para>
+        /// The number of rows in a <see cref="Permutation"/> table of <em>n</em> choices is:<br />
+        /// <br />
+        /// <c>Combinatoric.Factorial (n)</c><br />
+        /// <br />
+        /// The number of rows in a <see cref="Permutation"/> table of <em>k</em> picks
+        /// from <em>n</em> choices is:<br />
+        /// <br />
+        /// <c>Combinatoric.Factorial (n) / Combinatoric.Factorial (n-k)</c>
+        /// </para>
+        /// <para>
+        /// An exception to the above formulas is the special case where the number of elements
+        /// in the permutation is 0. While mathematics treats this result as 1 row containing the
+        /// empty product, this library returns 0 rows.
+        /// </para>
+        /// </example>
+        /// <exception cref="IndexOutOfRangeException">
+        /// When <em>value</em> not in range (0..20).
+        /// </exception>
+        static public long Factorial (int value)
+        {
+            return factorial[value];
+        }
+
+        #endregion
+    }
+}
Added +9 -0
diff --git a/KwCombinatorics.Core.csproj b/KwCombinatorics.Core.csproj
new file mode 100644
index 0000000..132c02c
--- /dev/null
+++ b/KwCombinatorics.Core.csproj
@@ -0,0 +1,9 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net6.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+
+</Project>
Added +25 -0
diff --git a/KwCombinatorics.Core.sln b/KwCombinatorics.Core.sln
new file mode 100644
index 0000000..89ae7e4
--- /dev/null
+++ b/KwCombinatorics.Core.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.4.33213.308
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KwCombinatorics.Core", "KwCombinatorics.Core.csproj", "{4587390E-EAA6-4960-9E06-519B7EA3BF60}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{4587390E-EAA6-4960-9E06-519B7EA3BF60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{4587390E-EAA6-4960-9E06-519B7EA3BF60}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{4587390E-EAA6-4960-9E06-519B7EA3BF60}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{4587390E-EAA6-4960-9E06-519B7EA3BF60}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {328CE31D-8FBF-4435-A521-78F179749FDF}
+	EndGlobalSection
+EndGlobal
Added +743 -0
diff --git a/Multicombination.cs b/Multicombination.cs
new file mode 100644
index 0000000..81d83fa
--- /dev/null
+++ b/Multicombination.cs
@@ -0,0 +1,743 @@
+//
+// KwCombinatorics v3.0.x/v4.0.x
+// Copyright © 2009-2012 Kasey Osborn (Kasewick@gmail.com)
+// Ms-PL - Use and redistribute freely
+//
+// File: Multicombination.cs
+//
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Kw.Combinatorics
+{
+    /// <summary>
+    /// Represents an ascending sequence of repeatable picks from a supplied number of choices.
+    /// </summary>
+    /// <remarks>
+    /// <para>
+    /// <em>k</em>-multicombinations are also known as pick-multicombinations or
+    /// <em>k</em>-combinations with repetitions.
+    /// 
+    /// The defining variables are <em>n</em> which is the number of possible choices and
+    /// <em>k</em> which is the number of repeatable picks from those choices.
+    /// 
+    /// This is contrasted to <em>k</em>-combinations where the picks may not repeat.
+    /// </para>
+    /// <para>
+    /// The <see cref="Multicombination"/> class produces <em>k</em>-multicombinations with
+    /// ascending elements that may repeat as many as <em>k</em> times. 
+    /// 
+    /// While sequence order of the elements is not a requirement of <em>k</em>-multicombinations,
+    /// producing an ascending sequence allows ranking the rows into an ordered table.
+    /// </para>
+    /// <para>
+    /// Use the <see cref="Picks"/> property to get the number of elements (<em>k</em>)
+    /// of a <see cref="Multicombination"/> taken from a possible number of
+    /// <see cref="Choices"/> (<em>n</em>).
+    /// 
+    /// Use the <see cref="RowCount"/> property to get the number of distinct possible
+    /// <see cref="Multicombination"/> sequences.
+    /// 
+    /// Use the <see cref="P:Kw.Combinatorics.Multicombination.Item(System.Int32)">indexer</see>
+    /// to get a specified element of the sequence.
+    ///
+    /// Use the <see cref="GetEnumerator">default enumerator</see> to iterate thru
+    /// the elements of a <see cref="Multicombination"/>.
+    /// 
+    /// Use the <see cref="Permute">Permute</see> method to pick objects from a supplied array
+    /// of choices based on the current sequence.
+    /// </para>
+    /// <para>
+    /// Use the <see cref="Rank"/> property to get or set the row index in a lexicographically
+    /// ordered <see cref="Multicombination"/> table of all possible non-descending sequences.
+    /// 
+    /// Use <see cref="GetRows"/> to iterate thru all possible sequences of the
+    /// <see cref="Multicombination"/> ordered by <see cref="Rank"/>.
+    /// 
+    /// Use <see cref="GetRowsForPicks">GetRowsForPicks (startPick, stopPick)</see> to iterate
+    /// thru every table of all picks in the range (<em>startPick</em>..<em>stopPick</em>).
+    /// </para>
+    /// <para>
+    /// The default appearance of a <see cref="Multicombination"/> row is a list of
+    /// integers (starting at 0) enclosed in braces. The appearance may be tailored 3 ways:
+    /// <ul>
+    ///   <li>
+    ///     Map each element to a different value using the
+    ///     <see cref="GetEnumerator">default enumerator</see> or the
+    ///     <see cref="P:Kw.Combinatorics.Multicombination.Item(System.Int32)">indexer</see>.
+    ///   </li>
+    ///   <li>
+    ///     Use the <see cref="Permute">Permute</see> method and output the rearranged values.
+    ///   </li>
+    ///   <li>
+    ///     Define a subclass of <see cref="Multicombination"/> and override
+    ///     <see cref="System.Object.ToString">ToString()</see>.
+    ///     (See the <see cref="GetRowsForPicks">GetRowsForPicks</see> method
+    ///     for an example.)
+    ///   </li>
+    /// </ul>
+    /// </para>
+    /// <para>
+    /// For more information about <em>k</em>-multicombinations, see:
+    /// </para>
+    /// <para>
+    /// <em>http://en.wikipedia.org/wiki/Combination</em>
+    /// </para>
+    /// </remarks>
+    /// <example>
+    /// <para>
+    /// Iterating thru <c>new Multicombination (4, 3).GetRows()</c> produces:
+    /// </para>
+    /// <para>
+    /// <c>{ 0, 0, 0 }</c><br/>
+    /// <c>{ 0, 0, 1 }</c><br/>
+    /// <c>{ 0, 0, 2 }</c><br/>
+    /// <c>{ 0, 0, 3 }</c><br/>
+    /// <c>{ 0, 1, 1 }</c><br/>
+    /// <c>{ 0, 1, 2 }</c><br/>
+    /// <c>{ 0, 1, 3 }</c><br/>
+    /// <c>{ 0, 2, 2 }</c><br/>
+    /// <c>{ 0, 2, 3 }</c><br/>
+    /// <c>{ 0, 3, 3 }</c><br/>
+    /// <c>{ 1, 1, 1 }</c><br/>
+    /// <c>{ 1, 1, 2 }</c><br/>
+    /// <c>{ 1, 1, 3 }</c><br/>
+    /// <c>{ 1, 2, 2 }</c><br/>
+    /// <c>{ 1, 2, 3 }</c><br/>
+    /// <c>{ 1, 3, 3 }</c><br/>
+    /// <c>{ 2, 2, 2 }</c><br/>
+    /// <c>{ 2, 2, 3 }</c><br/>
+    /// <c>{ 2, 3, 3 }</c><br/>
+    /// <c>{ 3, 3, 3 }</c>
+    /// </para>
+    /// </example>
+    public class Multicombination :
+        IComparable,
+        System.Collections.IEnumerable,
+        IComparable<Multicombination>,
+        IEquatable<Multicombination>,
+        IEnumerable<int>
+    {
+        private int[] data;     // The picks for the current rank. Length is 'k'.
+        private int choices;    // Number of possible values 'n'.
+        private long rowCount;  // Row count of the table of k-multicombinations.
+        private long rank;      // Row index.
+
+        #region Constructors
+
+        /// <summary>
+        /// Make an empty <see cref="Multicombination"/>.
+        /// </summary>
+        public Multicombination ()
+        {
+            this.data = new int[0];
+            this.choices = 0;
+            this.rowCount = 0;
+            this.rank = 0;
+        }
+
+        
+        /// <summary>
+        /// Make a copy of a <see cref="Multicombination"/>.
+        /// </summary>
+        /// <param name="source">Instance to copy.</param>
+        /// <exception cref="ArgumentNullException">When <em>source</em> is <b>null</b>.</exception>
+        public Multicombination (Multicombination source)
+        {
+            if (source == null)
+                throw new ArgumentNullException ("source");
+
+            this.data = new int[source.data.Length];
+            source.data.CopyTo (this.data, 0);
+
+            this.choices = source.Choices;
+            this.rowCount = source.RowCount;
+            this.rank = source.rank;
+        }
+
+
+        /// <summary>
+        /// Make a new <see cref="Multicombination"/> from the supplied
+        /// <em>choices</em> of the same <em>Picks</em>.
+        /// </summary>
+        /// <param name="choices">Number of elements in the sequence.</param>
+        /// <exception cref="ArgumentOutOfRangeException">
+        /// When <em>choices</em> less than 0.
+        /// </exception>
+        public Multicombination (int choices)
+        {
+            if (choices < 0)
+                throw new ArgumentOutOfRangeException ("choices", "Value is less than zero.");
+
+            this.data = new int[choices];
+            this.choices = choices;
+            this.rowCount = choices == 0? 0 : Combinatoric.BinomialCoefficient (Picks + choices - 1, Picks);
+            this.rank = 0;
+        }
+
+
+        /// <summary>
+        /// Make a new <see cref="Multicombination"/> from the supplied
+        /// <em>choices</em> and <em>picks</em> of <see cref="Rank"/> 0.
+        /// </summary>
+        /// <param name="choices">Number of values to pick from.</param>
+        /// <param name="picks">Number of elements in the sequence.</param>
+        /// <example>
+        /// <code source="Examples\Multicombination\McExample01\McExample01.cs" lang="cs" />
+        /// </example>
+        /// <exception cref="ArgumentOutOfRangeException">
+        /// When negative value supplied; when <em>choices</em> is zero and <em>picks</em> is nonzero.
+        /// </exception>
+        /// <exception cref="OverflowException">When the numbers are just too big.</exception>
+        public Multicombination (int choices, int picks)
+        {
+            if (choices < 0)
+                throw new ArgumentOutOfRangeException ("choices", "Value is less than zero.");
+
+            if (picks < 0)
+                throw new ArgumentOutOfRangeException ("picks", "Value is less than zero.");
+
+            if (choices == 0 && picks > 0)
+                throw new ArgumentOutOfRangeException ("choices", "Value is zero and picks is nonzero.");
+
+            this.data = new int[picks];
+            this.choices = choices;
+            this.rowCount = picks == 0? 0 : Combinatoric.BinomialCoefficient (picks + choices - 1, picks);
+            this.rank = 0;
+        }
+
+
+        /// <summary>
+        /// Make a new <see cref="Multicombination"/> from the supplied
+        /// <em>choices</em> and <em>picks</em> of the supplied <em>rank</em>.
+        /// </summary>
+        /// <remarks>
+        /// If the supplied <em>rank</em> is out of the range (0..<see cref="RowCount"/>-1),
+        /// it will be normalized to the valid range. For example, a value of -1 will
+        /// produce the last row in the ordered table.
+        /// </remarks>
+        /// <param name="choices">Number of values to pick from.</param>
+        /// <param name="picks">Number of elements in the sequence.</param>
+        /// <param name="rank">Initial row index in the ordered <see cref="Multicombination"/> table.</param>
+        /// <example>
+        /// <code source="Examples\Multicombination\McExample05\McExample05.cs" lang="cs" />
+        /// </example>
+        /// <exception cref="ArgumentOutOfRangeException">
+        /// When negative value supplied; when <em>choices</em> is 0 and <em>picks</em> is not 0.
+        /// </exception>
+        /// <exception cref="OverflowException">When too many <em>choices</em>.</exception>
+        public Multicombination (int choices, int picks, long rank)
+        {
+            if (choices < 0)
+                throw new ArgumentOutOfRangeException ("choices", "Value is less than zero.");
+
+            if (picks < 0)
+                throw new ArgumentOutOfRangeException ("picks", "Value is less than zero.");
+
+            if (choices == 0 && picks > 0)
+                throw new ArgumentOutOfRangeException ("choices", "Value is zero and picks is nonzero.");
+
+            this.data = new int[picks];
+            this.choices = choices;
+            this.rowCount = picks == 0? 0 : Combinatoric.BinomialCoefficient (picks + choices - 1, picks);
+            Rank = rank;
+        }
+
+
+        /// <summary>
+        /// Make a new <see cref="Multicombination"/> from the supplied elements.
+        /// </summary>
+        /// <param name="choices">Number of values to pick from.</param>
+        /// <param name="source">Array of integers.</param>
+        /// <example>
+        /// <code source="Examples\Multicombination\McExample04\McExample04.cs" lang="cs" />
+        /// </example>
+        /// <exception cref="ArgumentNullException">When <em>source</em> is <b>null</b>.</exception>
+        /// <exception cref="ArgumentOutOfRangeException">
+        /// When <em>source</em> contains invalid data;
+        /// when <em>choices</em> is 0 and <em>source</em> is not empty.
+        /// </exception>
+        public Multicombination (int choices, int[] source)
+        {
+            if (source == null)
+                throw new ArgumentNullException ("source");
+
+            if (choices < 0)
+                throw new ArgumentOutOfRangeException ("choices", "Value is less than zero.");
+
+            if (choices == 0 && source.Length > 0)
+                throw new ArgumentOutOfRangeException ("choices", "Value is zero and picks is nonzero.");
+
+            this.data = new int[source.Length];
+            source.CopyTo (this.data, 0);
+            Array.Sort (this.data);
+
+            this.choices = choices;
+            this.rowCount = Picks == 0? 0 : Combinatoric.BinomialCoefficient (Picks + choices - 1, Picks);
+
+            for (int ki = 0; ki < Picks; ++ki)
+                if (this.data[ki] < 0 || this.data[ki] >= choices)
+                    throw new ArgumentOutOfRangeException ("source", "Element is out of range.");
+
+            //
+            // Perform ranking:
+            //
+
+            this.rank = 0;
+            if (RowCount == 0)
+                return;
+
+            int comboElement = this.data[0];
+            int ji = 0;
+            for (int ki = 0;;)
+            {
+                for (; ji < comboElement; ++ji)
+                    rank += Combinatoric.BinomialCoefficient (Choices + Picks - ji - 2, Picks - ki - 1);
+
+                ++ki;
+                if (ki >= Picks)
+                    break;
+
+                ji = comboElement + 1;
+                comboElement = this.data[ki] - this.data[ki-1] + ji;
+            }
+        }
+
+        #endregion
+
+        #region Properties
+
+        /// <summary>
+        /// The available number of integers to choose from.
+        /// </summary>
+        /// <remarks>
+        /// Also known as <em>n</em>.
+        /// </remarks>
+        public int Choices
+        {
+            get { return choices; }
+        }
+
+
+        /// <summary>
+        /// Number of elements in the <see cref="Multicombination"/>.
+        /// </summary>
+        /// <remarks>
+        /// Also known as <em>k</em>.
+        /// </remarks>
+        public int Picks
+        {
+            get { return data.Length; }
+        }
+
+
+        /// <summary>
+        /// Row index in the ordered <see cref="Multicombination"/> table.
+        /// </summary>
+        /// <remarks>
+        /// Any assigned value out of range will be normalized to (0..<see cref="RowCount"/>-1).
+        /// </remarks>
+        /// <example>
+        /// <code source="Examples\Multicombination\McExample04\McExample04.cs" lang="cs" />
+        /// </example>
+        public long Rank
+        {
+            get { return rank; }
+            set
+            {
+                if (RowCount == 0)
+                    return;
+
+                // Normalize the new rank.
+                if (value < 0)
+                {
+                    rank = value % RowCount;
+                    if (rank < 0)
+                        rank += RowCount;
+                }
+                else
+                    rank = value < RowCount? value : value % RowCount;
+
+                //
+                // Perform unranking:
+                //
+
+                long diminishingRank = RowCount - rank - 1;
+                int combinaticAtom = Choices + Picks - 1;
+
+                for (int ki = Picks; ki > 0; --ki)
+                    for (;;)
+                    {
+                        --combinaticAtom;
+
+                        long trialCount = Combinatoric.BinomialCoefficient (combinaticAtom, ki);
+                        if (trialCount <= diminishingRank)
+                        {
+                            diminishingRank -= trialCount;
+                            data[Picks - ki] = Choices - combinaticAtom + ki - 2;
+                            break;
+                        }
+                    }
+            }
+        }
+
+
+        /// <summary>
+        /// Count of distinct sequences in the <see cref="Multicombination"/> table.
+        /// </summary>
+        public long RowCount
+        {
+            get { return rowCount; }
+        }
+
+
+        /// <summary>
+        /// Get a element of the <see cref="Multicombination"/> at the supplied column.
+        /// </summary>
+        /// <param name="index">Zero-based index value.</param>
+        /// <returns>Sequence value at <em>index</em>.</returns>
+        /// <example>
+        /// <code source="Examples\Multicombination\McExample05\McExample05.cs" lang="cs" />
+        /// </example>
+        /// <exception cref="IndexOutOfRangeException">
+        /// When <em>index</em> not in range (0..<see cref="Picks"/>-1).
+        /// </exception>
+        public int this[int index]
+        {
+            get { return data[index]; }
+        }
+
+        #endregion
+
+        #region Instance methods
+
+        /// <summary>Compare two <see cref="Multicombination"/>s.</summary>
+        /// <param name="obj">Target of the comparison.</param>
+        /// <returns>
+        /// A signed integer indicating the sort order of this instance to <em>obj</em>.
+        /// </returns>
+        public int CompareTo (object obj)
+        { return CompareTo (obj as Multicombination); }
+
+
+        /// <summary>Compare two <see cref="Multicombination"/>s.</summary>
+        /// <param name="other">Target of the comparison.</param>
+        /// <returns>
+        /// A signed integer indicating the sort order of this instance to <em>other</em>.
+        /// </returns>
+        public int CompareTo (Multicombination other)
+        {
+            if ((object) other == null)
+                return 1;
+
+            int result = this.Picks - other.Picks;
+            if (result == 0)
+            {
+                result = this.Choices - other.Choices;
+                if (result == 0)
+                {
+                    long rankDiff = this.Rank - other.Rank;
+
+                    if (rankDiff == 0)
+                        result = 0;
+                    else
+                        result = rankDiff < 0 ? -1 : 1;
+                }
+            }
+
+            return result;
+        }
+
+
+        /// <summary>
+        /// Copy the entire sequence to the supplied destination.
+        /// </summary>
+        /// <param name="array">Destination of copy.</param>
+        /// <exception cref="ArgumentNullException">When <em>array</em> is <b>null</b>.</exception>
+        /// <exception cref="ArgumentException">When not enough space in <em>array</em>.</exception>
+        public void CopyTo (int[] array)
+        {
+            if (array == null)
+                throw new ArgumentNullException ("array");
+
+            if (array.Length < this.data.Length)
+                throw new ArgumentException ("Destination array is not long enough.");
+
+            this.data.CopyTo (array, 0);
+        }
+
+
+        /// <summary>
+        /// Indicate whether two <see cref="Multicombination"/>s have the same value.
+        /// </summary>
+        /// <param name="obj">Target of the comparison.</param>
+        /// <returns>
+        /// <b>true</b> if <em>obj</em> has the same value as this object; otherwise, <b>false</b>.
+        /// </returns>
+        public override bool Equals (object obj)
+        { return Equals (obj as Multicombination); }
+
+        
+        /// <summary>
+        /// Indicate whether two <see cref="Multicombination"/>s have the same value.
+        /// </summary>
+        /// <param name="other">Target of the comparison.</param>
+        /// <returns>
+        /// <b>true</b> if <em>other</em> has the same value as this instance;
+        /// otherwise, <b>false</b>.
+        /// </returns>
+        public bool Equals (Multicombination other)
+        {
+            return (object) other != null
+                && other.Rank == Rank && other.Choices == Choices && other.Picks == Picks;
+        }
+
+        
+        /// <summary>Get an object-based enumerator of the elements.</summary>
+        /// <returns>Object-based elemental enumerator.</returns>
+        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
+        { return this.GetEnumerator(); }
+
+
+        /// <summary>Enumerate all elements of a <see cref="Multicombination"/>.</summary>
+        /// <returns>
+        /// An <c>IEnumerator&lt;int&gt;</c> for this <see cref="Multicombination"/>.
+        /// </returns>
+        /// <example>
+        /// <code source="Examples\Multicombination\McExample05\McExample05.cs" lang="cs" />
+        /// </example>
+        public IEnumerator<int> GetEnumerator ()
+        {
+            for (int ei = 0; ei < Picks; ++ei)
+                yield return this.data[ei];
+        }
+
+
+        /// <summary>Get the hash oode of the <see cref="Multicombination"/>.</summary>
+        /// <returns>A 32-bit signed integer hash code.</returns>
+        public override int GetHashCode ()
+        { return unchecked ((int) Rank); }
+
+
+        /// <summary>
+        /// Iterate thru all rows of the <see cref="Multicombination"/> table
+        /// for every value of <see cref="Rank"/> ascending.
+        /// </summary>
+        /// <returns>An iterator for a <see cref="Multicombination"/> table.</returns>
+        /// <remarks>
+        /// If the start row is not of <see cref="Rank"/> 0, the iteration will wrap around
+        /// so that <see cref="RowCount"/> items are always produced.
+        /// </remarks>
+        /// <example>
+        /// <code source="Examples\Multicombination\McExample01\McExample01.cs" lang="cs" />
+        /// </example>
+        public IEnumerable<Multicombination> GetRows ()
+        {
+            if (RowCount > 0)
+            {
+                long startRank = rank;
+                for (Multicombination current = (Multicombination) MemberwiseClone();;)
+                {
+                    yield return current;
+                    current.Rank = current.Rank + 1;
+                    if (current.Rank == startRank)
+                        break;
+                }
+            }
+        }
+
+
+        /// <summary>
+        /// Iterate thru all rows of all <see cref="Multicombination"/> tables for every
+        /// pick in the range (<em>startPicks</em>..<em>stopPicks</em>).
+        /// </summary>
+        /// <returns>An iterator for a series of <see cref="Multicombination"/> tables.</returns>
+        /// <remarks>
+        /// Unlike <see cref="Combination"/>, <see cref="Picks"/> may exceed <see cref="Choices"/>.
+        /// </remarks>
+        /// <param name="startPicks">Number of picks for first table.</param>
+        /// <param name="stopPicks">Number of picks for last table.</param>
+        /// <example>
+        /// <code source="Examples\Multicombination\McExample02\McExample02.cs" lang="cs" />
+        /// </example>
+        /// <exception cref="ArgumentOutOfRangeException">
+        /// When <em>startPicks</em> is less than 0 or greater than <em>stopPicks</em>.
+        /// </exception>
+        public IEnumerable<Multicombination> GetRowsForPicks (int startPicks, int stopPicks)
+        {
+            if (startPicks < 0 || startPicks > stopPicks)
+                throw new ArgumentOutOfRangeException ("startPicks", "Pick range is not valid.");
+
+            if (Choices == 0)
+                yield break;
+
+            if (startPicks == 0)
+                startPicks = 1;
+
+            for (int k = startPicks; k <= stopPicks; ++k)
+            {
+                Multicombination current = (Multicombination) MemberwiseClone();
+                current.data = new int[k];
+                current.rowCount = Combinatoric.BinomialCoefficient (k + choices - 1, k);
+                current.rank = 0;
+
+                for (;;)
+                {
+                    yield return current;
+                    
+                    current.Rank = current.Rank + 1;
+                    if (current.Rank == 0)
+                        break;
+                }
+            }
+        }
+
+
+        /// <summary>
+        /// Provide a readable form of the <see cref="Multicombination"/> sequence.
+        /// </summary>
+        /// <returns>A <c>string</c> that represents the sequence.</returns>
+        /// <remarks>Result is enclosed in braces and separated by commas.</remarks>
+        /// <example>
+        /// <code source="Examples\Multicombination\McExample04\McExample04.cs" lang="cs" />
+        /// </example>
+        public override string ToString ()
+        {
+            if (RowCount == 0)
+                return ("{ }");
+
+            StringBuilder result = new StringBuilder ("{ ");
+
+            for (int ei = 0;;)
+            {
+                result.Append (this.data[ei]);
+
+                ++ei;
+                if (ei >= Picks)
+                    break;
+
+                result.Append (", ");
+            }
+
+            result.Append (" }");
+
+            return result.ToString();
+        }
+
+        #endregion
+
+        #region Static methods
+
+        /// <summary>
+        /// Apply a <see cref="Multicombination"/> sequence to rearrange the supplied list or array.
+        /// </summary>
+        /// <typeparam name="T">Type of items to rearrange.</typeparam>
+        /// <param name="arrangement">New arrangement for items.</param>
+        /// <param name="source">List of items to rearrange.</param>
+        /// <returns>List of rearranged items.</returns>
+        /// <example>
+        /// <code source="Examples\Multicombination\McExample03\McExample03.cs" lang="cs" />
+        /// </example>
+        /// <exception cref="ArgumentNullException">
+        /// When <em>arrangement</em> or <em>source</em> is <b>null</b>.
+        /// </exception>
+        /// <exception cref="ArgumentException">
+        /// When length of <em>source</em> is less than <see cref="Choices"/>.
+        /// </exception>
+        public static List<T> Permute<T> (Multicombination arrangement, IList<T> source)
+        {
+            if (arrangement == null)
+                throw new ArgumentNullException ("arrangement");
+
+            if (source == null)
+                throw new ArgumentNullException ("source");
+
+            if (source.Count < arrangement.Choices)
+                throw new ArgumentException ("Not enough supplied values.", "source");
+
+            List<T> result = new List<T> (arrangement.Picks);
+
+            for (int ei = 0; ei < arrangement.Picks; ++ei)
+                result.Add (source[arrangement[ei]]);
+
+            return result;
+        }
+
+        
+        /// <summary>Indicate whether 2 <see cref="Multicombination"/>s are equal.</summary>
+        /// <param name="param1">A <see cref="Multicombination"/> sequence.</param>
+        /// <param name="param2">A <see cref="Multicombination"/> sequence.</param>
+        /// <returns><b>true</b> if supplied sequences are equal;
+        /// otherwise, <b>false</b>.</returns>
+        public static bool operator == (Multicombination param1, Multicombination param2)
+        {
+            if ((object) param1 == null)
+                return (object) param2 == null;
+            else
+                return param1.Equals (param2);
+        }
+
+
+        /// <summary>Indicate whether 2 <see cref="Multicombination"/>s are not equal.</summary>
+        /// <param name="param1">A <see cref="Multicombination"/> sequence.</param>
+        /// <param name="param2">A <see cref="Multicombination"/> sequence.</param>
+        /// <returns><b>true</b> if supplied sequences are not equal;
+        /// otherwise, <b>false</b>.</returns>
+        public static bool operator != (Multicombination param1, Multicombination param2)
+        { return !(param1 == param2); }
+
+
+        /// <summary>Indicate whether the left <see cref="Multicombination"/> is less than
+        /// the right <see cref="Multicombination"/>.</summary>
+        /// <param name="param1">A <see cref="Multicombination"/> sequence.</param>
+        /// <param name="param2">A <see cref="Multicombination"/> sequence.</param>
+        /// <returns><b>true</b> if the left sequence is less than
+        /// the right sequence; otherwise, <b>false</b>.</returns>
+        public static bool operator < (Multicombination param1, Multicombination param2)
+        {
+            if ((object) param1 == null)
+                return (object) param2 != null;
+            else
+                return param1.CompareTo (param2) < 0;
+        }
+
+
+        /// <summary>Indicate whether the left <see cref="Multicombination"/> is greater than
+        /// or equal to the right <see cref="Multicombination"/>.</summary>
+        /// <param name="param1">A <see cref="Multicombination"/> sequence.</param>
+        /// <param name="param2">A <see cref="Multicombination"/> sequence.</param>
+        /// <returns><b>true</b> if the left sequence is greater than or equal to
+        /// the right sequence; otherwise, <b>false</b>.</returns>
+        public static bool operator >= (Multicombination param1, Multicombination param2)
+        { return !(param1 < param2); }
+
+
+        /// <summary>Indicate whether the left <see cref="Multicombination"/> is greater than
+        /// the right <see cref="Multicombination"/>.</summary>
+        /// <param name="param1">A <see cref="Multicombination"/> sequence.</param>
+        /// <param name="param2">A <see cref="Multicombination"/> sequence.</param>
+        /// <returns><b>true</b> if the left sequence is greater than
+        /// the right sequence; otherwise, <b>false</b>.</returns>
+        public static bool operator > (Multicombination param1, Multicombination param2)
+        {
+            if ((object) param1 == null)
+                return false;
+            else
+                return param1.CompareTo (param2) > 0;
+        }
+
+
+        /// <summary>Indicate whether the left <see cref="Multicombination"/> is less than
+        /// or equal to the right <see cref="Multicombination"/>.</summary>
+        /// <param name="param1">A <see cref="Multicombination"/> sequence.</param>
+        /// <param name="param2">A <see cref="Multicombination"/> sequence.</param>
+        /// <returns><b>true</b> if the left sequence is less than or equal to
+        /// the right sequence; otherwise, <b>false</b>.</returns>
+        public static bool operator <= (Multicombination param1, Multicombination param2)
+        { return !(param1 > param2); }
+
+        #endregion
+    }
+}
Added +1190 -0
diff --git a/Permutation.cs b/Permutation.cs
new file mode 100644
index 0000000..c0828df
--- /dev/null
+++ b/Permutation.cs
@@ -0,0 +1,1190 @@
+//
+// KwCombinatorics v3.0.x/v4.0.x
+// Copyright © 2009-2012 Kasey Osborn (Kasewick@gmail.com)
+// Ms-PL - Use and redistribute freely
+//
+// File: Permutation.cs
+//
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Kw.Combinatorics
+{
+    /// <summary>
+    /// Represents an arrangement of distinct values taken from a supplied number of choices.
+    /// </summary>
+    /// <remarks>
+    /// <para>
+    /// Unlike combinations, the arrangement of the elements of a permutation is significant.
+    /// 
+    /// Permutations typically contain all of the available choices. In contrast,
+    /// <em>k</em>-permutations contain arrangements that pick fewer elements than the
+    /// available choices. 
+    /// </para>
+    /// <para>
+    /// The <see cref="Permutation"/> class uses the inherent sequencing of the elements
+    /// to arrange the rows into an lexicographically ordered table.
+    /// 
+    /// Support for <em>k</em>-permutations is provided by supplying a <em>picks</em> value
+    /// that is less than the supplied <em>choices</em> value to the appropriate constructors.
+    /// </para>
+    /// <para>
+    /// Use the <see cref="Choices"/> property to get the number of elements to choose from.
+    /// 
+    /// Use the <see cref="Picks"/> property to get the number of elements of a
+    /// <see cref="Permutation"/>.
+    /// 
+    /// Use the <see cref="RowCount"/> property to get the number of distinct possible
+    /// sequences of a <see cref="Permutation"/>.
+    /// 
+    /// Use the <see cref="P:Kw.Combinatorics.Permutation.Item(System.Int32)">indexer</see>
+    /// to get a specified element of the sequence.
+    /// 
+    /// Use the <see cref="GetEnumerator">default enumerator</see> to iterate thru
+    /// the elements of a <see cref="Permutation"/>.
+    /// 
+    /// Use the <see cref="Permute">Permute</see> method to
+    /// rearrange a supplied array based on the current sequence.
+    /// </para>
+    /// <para>
+    /// Use the <see cref="Rank"/> property to get or set the row index in a lexicographically
+    /// ordered <see cref="Permutation"/> table of all possible sequences.
+    /// 
+    /// Use <see cref="GetRows"/> to iterate thru all possible sequences
+    /// of the <see cref="Permutation"/> ordered by <see cref="Rank"/>.
+    /// 
+    /// Use <see cref="GetRowsForAllChoices"/> to iterate
+    /// thru every table of all choices in the range (1..<see cref="Choices"/>).
+    /// 
+    /// Use <see cref="GetRowsForAllPicks"/> to iterate
+    /// thru every table of all picks in the range (1..<see cref="Picks"/>).
+    /// </para>
+    /// <para>
+    /// Use the <see cref="PlainRank"/> property to get or set the row index in a table
+    /// ordered for plain changes where adjacent rows differ by only a single swap of
+    /// 2 adjacent elements.
+    /// 
+    /// Use <see cref="GetRowsOfPlainChanges"/> to iterate thru all possible sequences
+    /// of a <see cref="Permutation"/> ordered by <see cref="PlainRank"/>.
+    /// </para>
+    /// <para>
+    /// Use the <see cref="Swaps"/> property to get the number of element swaps that would
+    /// transform a row into the sequence of <see cref="Rank"/> 0.
+    /// 
+    /// Use the <see cref="Backtrack">Backtrack</see> method to minimally advance
+    /// <see cref="Rank"/> while changing a specified element.
+    /// </para>
+    /// <para>
+    /// The default appearance of a <see cref="Permutation"/> row is a list of integers
+    /// (starting at 0) enclosed in braces. The appearance may be tailored 3 ways:
+    /// <ul>
+    ///   <li>
+    ///     Map each element to a different value using the
+    ///     <see cref="GetEnumerator">default enumerator</see> or the
+    ///     <see cref="P:Kw.Combinatorics.Permutation.Item(System.Int32)">indexer</see>.
+    ///   </li>
+    ///   <li>
+    ///     Use the <see cref="Permute">Permute</see> method and output the rearranged values.
+    ///   </li>
+    ///   <li>
+    ///     Define a subclass of <see cref="Permutation"/> and override
+    ///     <see cref="System.Object.ToString">ToString()</see>.
+    ///     (See <see cref="GetRowsForAllPicks"/> for an example.)
+    ///   </li>
+    /// </ul>
+    /// </para>
+    /// <para>
+    /// For more information about permutations and <em>k</em>-permutations, see:
+    /// </para>
+    /// <para>
+    /// <em>http://en.wikipedia.org/wiki/Permutation</em><br/>
+    /// <em>http://en.wikipedia.org/wiki/Eight_queens_puzzle</em>
+    /// </para>
+    /// </remarks>
+    /// <example>
+    /// <para>
+    /// Iterating thru <c>new Permutation (4).GetRows()</c> produces:
+    /// </para>
+    /// <para>
+    /// <c>{ 0, 1, 2, 3 }</c><br/>
+    /// <c>{ 0, 1, 3, 2 }</c><br/>
+    /// <c>{ 0, 2, 1, 3 }</c><br/>
+    /// <c>{ 0, 2, 3, 1 }</c><br/>
+    /// <c>{ 0, 3, 1, 2 }</c><br/>
+    /// <c>{ 0, 3, 2, 1 }</c><br/>
+    /// <c>{ 1, 0, 2, 3 }</c><br/>
+    /// <c>{ 1, 0, 3, 2 }</c><br/>
+    /// <c>{ 1, 2, 0, 3 }</c><br/>
+    /// <c>{ 1, 2, 3, 0 }</c><br/>
+    /// <c>{ 1, 3, 0, 2 }</c><br/>
+    /// <c>{ 1, 3, 2, 0 }</c><br/>
+    /// <c>{ 2, 0, 1, 3 }</c><br/>
+    /// <c>{ 2, 0, 3, 1 }</c><br/>
+    /// <c>{ 2, 1, 0, 3 }</c><br/>
+    /// <c>{ 2, 1, 3, 0 }</c><br/>
+    /// <c>{ 2, 3, 0, 1 }</c><br/>
+    /// <c>{ 2, 3, 1, 0 }</c><br/>
+    /// <c>{ 3, 0, 1, 2 }</c><br/>
+    /// <c>{ 3, 0, 2, 1 }</c><br/>
+    /// <c>{ 3, 1, 0, 2 }</c><br/>
+    /// <c>{ 3, 1, 2, 0 }</c><br/>
+    /// <c>{ 3, 2, 0, 1 }</c><br/>
+    /// <c>{ 3, 2, 1, 0 }</c>
+    /// </para>
+    /// <para>
+    /// Iterating thru <c>new Permutation (4, 3).GetRows()</c> produces:
+    /// </para>
+    /// <para>
+    /// <c>{ 0, 1, 2 }</c><br/>
+    /// <c>{ 0, 1, 3 }</c><br/>
+    /// <c>{ 0, 2, 1 }</c><br/>
+    /// <c>{ 0, 2, 3 }</c><br/>
+    /// <c>{ 0, 3, 1 }</c><br/>
+    /// <c>{ 0, 3, 2 }</c><br/>
+    /// <c>{ 1, 0, 2 }</c><br/>
+    /// <c>{ 1, 0, 3 }</c><br/>
+    /// <c>{ 1, 2, 0 }</c><br/>
+    /// <c>{ 1, 2, 3 }</c><br/>
+    /// <c>{ 1, 3, 0 }</c><br/>
+    /// <c>{ 1, 3, 2 }</c><br/>
+    /// <c>{ 2, 0, 1 }</c><br/>
+    /// <c>{ 2, 0, 3 }</c><br/>
+    /// <c>{ 2, 1, 0 }</c><br/>
+    /// <c>{ 2, 1, 3 }</c><br/>
+    /// <c>{ 2, 3, 0 }</c><br/>
+    /// <c>{ 2, 3, 1 }</c><br/>
+    /// <c>{ 3, 0, 1 }</c><br/>
+    /// <c>{ 3, 0, 2 }</c><br/>
+    /// <c>{ 3, 1, 0 }</c><br/>
+    /// <c>{ 3, 1, 2 }</c><br/>
+    /// <c>{ 3, 2, 0 }</c><br/>
+    /// <c>{ 3, 2, 1 }</c>
+    /// </para>
+    /// <para>
+    /// Iterating thru <c>new Permutation (4).GetRowsOfPlainChanges()</c> produces:
+    /// </para>
+    /// <para>
+    /// <c>{ 0, 1, 2, 3 }</c><br/>
+    /// <c>{ 0, 1, 3, 2 }</c><br/>
+    /// <c>{ 0, 3, 1, 2 }</c><br/>
+    /// <c>{ 3, 0, 1, 2 }</c><br/>
+    /// <c>{ 3, 0, 2, 1 }</c><br/>
+    /// <c>{ 0, 3, 2, 1 }</c><br/>
+    /// <c>{ 0, 2, 3, 1 }</c><br/>
+    /// <c>{ 0, 2, 1, 3 }</c><br/>
+    /// <c>{ 2, 0, 1, 3 }</c><br/>
+    /// <c>{ 2, 0, 3, 1 }</c><br/>
+    /// <c>{ 2, 3, 0, 1 }</c><br/>
+    /// <c>{ 3, 2, 0, 1 }</c><br/>
+    /// <c>{ 3, 2, 1, 0 }</c><br/>
+    /// <c>{ 2, 3, 1, 0 }</c><br/>
+    /// <c>{ 2, 1, 3, 0 }</c><br/>
+    /// <c>{ 2, 1, 0, 3 }</c><br/>
+    /// <c>{ 1, 2, 0, 3 }</c><br/>
+    /// <c>{ 1, 2, 3, 0 }</c><br/>
+    /// <c>{ 1, 3, 2, 0 }</c><br/>
+    /// <c>{ 3, 1, 2, 0 }</c><br/>
+    /// <c>{ 3, 1, 0, 2 }</c><br/>
+    /// <c>{ 1, 3, 0, 2 }</c><br/>
+    /// <c>{ 1, 0, 3, 2 }</c><br/>
+    /// <c>{ 1, 0, 2, 3 }</c>
+    /// </para>
+    /// </example>
+    public class Permutation :
+        IComparable,
+        System.Collections.IEnumerable,
+        IComparable<Permutation>,
+        IEquatable<Permutation>,
+        IEnumerable<int>
+    {
+        private int[] data;     // The arrangement for the current rank. Length is 'k'.
+        private int choices;    // Number of possible values 'n'.
+        private long rowCount;  // Row count of the table of (k-)permutations.
+        private long rank;      // Row index.
+
+        #region Constructors
+
+        /// <summary>
+        /// Make an empty <see cref="Permutation"/>.
+        /// </summary>
+        public Permutation ()
+        {
+            this.data = new int[0];
+            this.choices = 0;
+            this.rowCount = 0;
+            this.rank = 0;
+        }
+
+
+        /// <summary>
+        /// Make a copy of a <see cref="Permutation"/>.
+        /// </summary>
+        /// <param name="source">Instance to copy.</param>
+        /// <exception cref="ArgumentNullException">When <em>source</em> is <b>null</b>.</exception>
+        public Permutation (Permutation source)
+        {
+            if (source == null)
+                throw new ArgumentNullException ("source");
+
+            this.data = new int[source.data.Length];
+            source.data.CopyTo (this.data, 0);
+
+            this.choices = source.choices;
+            this.rowCount = source.rowCount;
+            this.rank = source.rank;
+        }
+
+
+        /// <summary>
+        /// Make a new <see cref="Permutation"/> of all the supplied number of
+        /// <em>choices</em> with a <see cref="Rank"/> of 0.
+        /// </summary>
+        /// <param name="choices">Number of elements in the sequence.</param>
+        /// <example>
+        /// <code source="Examples\Permutation\PnExample01\PnExample01.cs" lang="cs" />
+        /// </example>
+        /// <exception cref="ArgumentOutOfRangeException">
+        /// When <em>choices</em> is less than 0 or greater than 20.
+        /// </exception>
+        public Permutation (int choices)
+        {
+            if (choices < 0)
+                throw new ArgumentOutOfRangeException ("choices", "Value is less than zero.");
+
+            if (choices > MaxChoices)
+                throw new ArgumentOutOfRangeException ("choices", "Value is greater than maximum allowed.");
+
+            this.data = new int[choices];
+            for (int ei = 0; ei < choices; ++ei)
+                this.data[ei] = ei;
+
+            this.choices = choices;
+            this.rowCount = choices == 0? 0 : Combinatoric.factorial[choices];
+            this.rank = 0;
+        }
+
+
+        /// <summary>
+        /// Make a new <see cref="Permutation"/> with <em>picks</em> number of elements taken
+        /// from a possible number of <em>choices</em> of <see cref="Rank"/> 0.
+        /// </summary>
+        /// <param name="choices">Number of values to choose from.</param>
+        /// <param name="picks">Number of elements in the sequence.</param>
+        /// <example>
+        /// <code source="Examples\Permutation\PnExample06\PnExample06.cs" lang="cs" />
+        /// </example>
+        /// <exception cref="ArgumentOutOfRangeException">
+        /// When <em>picks</em> less than 0, greater than 20, or greater than <em>choices</em>;
+        /// when <em>choices</em> greater than 20.
+        /// </exception>
+        public Permutation (int choices, int picks) : this (choices, picks, 0)
+        { }
+
+
+        /// <summary>
+        /// Make a new <see cref="Permutation"/> with <em>picks</em> number of elements taken
+        /// from a possible number of <em>choices</em> of the supplied <em>rank</em>.
+        /// </summary>
+        /// <remarks>
+        /// If the supplied <em>rank</em> is out of the range (0..<see cref="RowCount"/>-1),
+        /// it will be normalized to the valid range. For example, a value of -1 will
+        /// produce the last row in the ordered table.
+        /// </remarks>
+        /// <param name="choices">Number of values to choose from.</param>
+        /// <param name="picks">Number of elements in the sequence.</param>
+        /// <param name="rank">Initial row index in the lexicographically ordered <see cref="Permutation"/> table.</param>
+        /// <example>
+        /// <code source="Examples\Permutation\PnExample05\PnExample05.cs" lang="cs" />
+        /// </example>
+        /// <exception cref="ArgumentOutOfRangeException">
+        /// When <em>picks</em> less than 0 or greater than <em>choices</em>;
+        /// when <em>choices</em> greater than 20.
+        /// </exception>
+        public Permutation (int choices, int picks, long rank)
+        {
+            if (picks < 0)
+                throw new ArgumentOutOfRangeException ("picks", "Value is less than zero.");
+
+            if (picks > choices)
+                throw new ArgumentOutOfRangeException ("picks", "Value is greater than choices.");
+
+            if (choices > MaxChoices)
+                throw new ArgumentOutOfRangeException ("choices", "Value is greater than maximum allowed.");
+
+            this.data = new int[picks];
+            this.choices = choices;
+            this.rowCount = CalcCount (choices, picks);
+            Rank = rank;
+        }
+
+
+        /// <summary>
+        /// Make a new <see cref="Permutation"/> from the supplied elements.
+        /// </summary>
+        /// <param name="source">Array of integers.</param>
+        /// <example>
+        /// <code source="Examples\Permutation\PnExample04\PnExample04.cs" lang="cs" />
+        /// </example>
+        /// <exception cref="ArgumentNullException">When <em>source</em> is <b>null</b>.</exception>
+        /// <exception cref="ArgumentOutOfRangeException">
+        /// When length of <em>source</em> is greater than 20 or contains invalid data;
+        /// When <em>source</em> contains out of range values.
+        /// </exception>
+        /// <exception cref="ArgumentException">
+        /// When <em>source</em> contains repeated values.
+        /// </exception>
+        public Permutation (int[] source)
+        {
+            if (source == null)
+                throw new ArgumentNullException ("source");
+
+            this.choices = source.Length;
+            Construct (this, source);
+        }
+
+
+        /// <summary>
+        /// Make a new <see cref="Permutation"/> from the supplied elements taken from the
+        /// available number of <em>choices</em>.
+        /// </summary>
+        /// <remarks>
+        /// Supplying a value for <em>choices</em> that is greater than the number of
+        /// elements in <em>source</em> will create a <em>k</em>-permutation.
+        /// </remarks>
+        /// <param name="source">Array of integers with elements in the range (0..<em>choices</em>-1).</param>
+        /// <param name="choices">Number of values that <em>source</em> may pick from.</param>
+        /// <exception cref="ArgumentNullException">When <em>source</em> is <b>null</b>.</exception>
+        /// <exception cref="ArgumentOutOfRangeException">
+        /// When length of <em>source</em> is greater than 20 or contains invalid data;
+        /// When <em>source</em> contains out of range values;
+        /// When <em>choices</em> is less than 0 or greater than 20.
+        /// </exception>
+        /// <exception cref="ArgumentException">
+        /// When <em>source</em> contains repeated values.
+        /// </exception>
+        public Permutation (int[] source, int choices)
+        {
+            if (source == null)
+                throw new ArgumentNullException ("source");
+
+            this.choices = choices;
+            Construct (this, source);
+        }
+
+        #endregion
+
+        #region Private static methods
+
+        // On entry: source may be unvalidated
+        // On exit: pn will have correct rank, rowcount for source data
+        static private void Construct (Permutation pn, int[] source)
+        {
+            if (source.Length > MaxChoices)
+                throw new ArgumentOutOfRangeException ("source", "Too many values.");
+
+            if (pn.choices < 0 || pn.choices > MaxChoices)
+                throw new ArgumentOutOfRangeException ("choices");
+
+            pn.data = new int[source.Length];
+            source.CopyTo (pn.data, 0);
+
+            bool[] isUsed = new bool[pn.Choices];
+            for (int ei = 0; ei < pn.Picks; ++ei)
+            {
+                if (pn.data[ei] < 0 || pn.data[ei] >= pn.Choices)
+                    throw new ArgumentOutOfRangeException ("source", "Value is out of range.");
+
+                if (isUsed[pn.data[ei]])
+                    throw new ArgumentException ("Value is repeated.", "source");
+                isUsed[pn.data[ei]] = true;
+            }
+
+            pn.rowCount = CalcCount (pn.Choices, pn.Picks);
+            pn.rank = CalcRank (pn.data, pn.Choices);
+        }
+
+
+        // On entry: choices, picks assumed valid.
+        private static long CalcCount (int choices, int picks)
+        {
+            if (picks == 0)
+                return 0;
+
+            long result = Combinatoric.Factorial (choices);
+            if (picks < choices)
+                result = result / Combinatoric.Factorial (choices - picks);
+
+            return result;
+        }
+
+
+        // On entry: elements & choices assumed valid, picks = elements.Length.
+        private static long CalcRank (int[] elements, int choices)
+        {
+            long result = 0;
+            bool[] isUsed = new bool[choices];
+
+            //
+            // Perform ranking:
+            //
+
+            for (int ei1 = 0; ei1 < elements.Length; ++ei1)
+            {
+                isUsed[elements[ei1]] = true;
+
+                int digit = 0;
+                for (int ei2 = 0; ei2 < elements[ei1]; ++ei2)
+                    if (!isUsed[ei2])
+                        ++digit;
+
+                result += digit * Combinatoric.Factorial (choices - ei1 - 1);
+            }
+
+            if (elements.Length < choices)
+                result = result / Combinatoric.Factorial (choices - elements.Length);
+
+            return result;
+        }
+
+
+        // On exit: returns swap count, array is garbled
+        private static int CalcSwapCount (int[] array, int choices)
+        {
+            int result = 0;
+            int md = 0;
+            var val = new int[array.Length];
+            var map = new int[choices];
+
+            for (int i1 = 0; i1 < array.Length; ++i1)
+                val[i1] = array[i1];
+
+            for (var ei = 0; ei < choices; ++ei)
+                map[ei] = -1;
+
+            for (var ei = 0; ei < array.Length; ++ei)
+                map[array[ei]] = ei;
+
+            for (var mi = 0; mi < choices; ++mi)
+            {
+                int ep = map[mi];
+                if (ep < 0)
+                    --md;
+                else if (ep > mi + md)
+                {
+                    int ei = val[mi + md];
+                    map[ei] = ep;
+                    val[ep] = ei;
+                    ++result;
+                }
+            }
+
+            return result;
+        }
+
+
+        private static long CalcPlainRank (int[] elements)
+        {
+            int n = elements.Length;
+            long pr = 0;
+
+            if (n <= 1)
+                return 0;
+
+            for (int ei = 1; ei < n; ++ei)
+            {
+                int xr = 0;
+                for (int i = 0; i < n; ++i)
+                    if (elements[i] <= ei)
+                    {
+                        ++xr;
+                        if (elements[i] == ei)
+                            break;
+                    }
+
+                pr = pr * (ei + 1) + (pr % 2 == 0? ei - xr + 1 : xr - 1);
+            }
+
+            return pr;
+        }
+
+
+        // On entry: elements allocated for choices.
+        // On exit: result will be in elements.
+        private static void CalcPlainUnrank (int[] elements, long plainRank)
+        {
+            elements[0] = 0;
+            for (int ei = 1; ei < elements.Length; ++ei)
+            {
+                int yd = (int) (Combinatoric.Factorial (elements.Length) / Combinatoric.Factorial (ei+1));
+                int yi = (int) ((plainRank / yd) % ((ei + 1) * 2));
+                int ip = yi <= ei? ei - yi : yi - ei - 1;
+
+                for (int si = ei; si > ip; --si)
+                    elements[si] = elements[si-1];
+                elements[ip] = ei;
+            }
+        }
+        
+        #endregion
+
+        #region Properties
+
+        /// <summary
+        /// >Number of available choices for the elements of the <see cref="Permutation"/>.
+        /// </summary>
+        /// <remarks>
+        /// If no <em>picks</em> value was specified when constructing this
+        /// <see cref="Permutation"/>, then this is also the number of elements.
+        /// </remarks>
+        public int Choices
+        {
+            get { return choices; }
+        }
+
+
+        /// <summary
+        /// >Number of elements in the <see cref="Permutation"/>.
+        /// </summary>
+        /// <remarks>
+        /// Also known as <em>k</em>. If value is less than <em>Choices</em>,
+        /// then this is a <em>k</em>-permutation.
+        /// </remarks>
+        public int Picks
+        {
+            get { return data.Length; }
+        }
+
+
+        /// <summary>
+        /// Row index of the sequence in the plain ordered <see cref="Permutation"/> table.
+        /// </summary>
+        /// <remarks>
+        /// <para>
+        /// Plain changes produces a table where adjacent rows differ by only a single swap of
+        /// 2 adjacent elements. The table always begins with the same row that begins the
+        /// lexicographically ordered table of the same <see cref="Choices"/>.
+        /// </para>
+        /// <para>
+        /// Any assigned value out of range will be normalized to (0..<see cref="RowCount"/>-1).
+        /// </para>
+        /// </remarks>
+        /// <example>
+        /// <code source="Examples\Permutation\PnExample07\PnExample07.cs" lang="cs" />
+        /// </example>
+        /// <exception cref="InvalidOperationException">
+        /// When <em>Choices</em> not equal to <em>Picks</em>.
+        /// </exception>
+        /// <seealso cref="GetRowsOfPlainChanges"/>
+        public long PlainRank
+        {
+            get
+            {
+                if (Picks != Choices)
+                    throw new InvalidOperationException ("Choices and Picks must be equal.");
+
+                return CalcPlainRank (data);
+            }
+            set
+            {
+                if (Picks != Choices)
+                    throw new InvalidOperationException ("Choices and Picks must be equal.");
+
+                if (RowCount == 0)
+                    return;
+
+                // Normalize the new rank.
+                if (value < 0)
+                {
+                    value = value % RowCount;
+                    if (value < 0)
+                        value += RowCount;
+                }
+                else if (value >= RowCount)
+                    value = value % RowCount;
+
+                CalcPlainUnrank (data, value);
+                rank = CalcRank (data, Choices);
+            }
+        }
+
+
+        /// <summary>
+        /// Row index of the sequence in the lexicographically ordered
+        /// <see cref="Permutation"/> table.
+        /// </summary>
+        /// <remarks>
+        /// Any assigned value out of range will be normalized to (0..<see cref="RowCount"/>-1).
+        /// </remarks>
+        /// <example>
+        /// <code source="Examples\Permutation\PnExample04\PnExample04.cs" lang="cs" />
+        /// </example>
+        public long Rank
+        {
+            get { return rank; }
+            set
+            {
+                if (RowCount == 0)
+                    return;
+
+                // Normalize the new rank.
+                if (value < 0)
+                {
+                    value = value % RowCount;
+                    if (value < 0)
+                        value += RowCount;
+                }
+                else if (value >= RowCount)
+                    value = value % RowCount;
+
+                rank = value;
+
+                //
+                // Perform unranking:
+                //
+
+                var isUsed = new bool[Choices];
+                var factoradic = new int[Choices];
+
+                // Build the factoradic from the diminishing rank.
+                value = value * Combinatoric.Factorial (Choices - Picks);
+                for (int fi = Choices - 1; fi >= 0; --fi)
+                    factoradic[fi] = (int) Math.DivRem (value, Combinatoric.Factorial (fi), out value);
+
+                // Build the permutation from the diminishing factoradic.
+                for (int fi = Choices - 1; fi >= Choices - Picks; --fi)
+                    for (int newAtom = 0; ; ++newAtom)
+                        if (!isUsed[newAtom])
+                            if (factoradic[fi] > 0)
+                                --factoradic[fi];
+                            else
+                            {
+                                data[Choices - fi - 1] = newAtom;
+                                isUsed[newAtom] = true;
+                                break;
+                            }
+            }
+        }
+
+
+        /// <summary>
+        /// Returns number of distinct possible arrangements of this <see cref="Permutation"/>.
+        /// </summary>
+        public long RowCount
+        {
+            get { return rowCount; }
+        }
+
+
+        /// <summary>
+        /// Returns number of element swaps needed to transform this <see cref="Permutation"/>
+        /// into <see cref="Rank"/> 0.
+        /// </summary>
+        /// <remarks>
+        /// <para>
+        /// If additional swaps are applied resulting again in a row of <see cref="Rank"/> 0,
+        /// those additional swaps will always be a multiple of 2.
+        /// </para>
+        /// <para>
+        /// Any <see cref="Permutation"/> with a <see cref="Rank"/> of 0 always has a
+        /// <see cref="PlainRank"/> of 0.
+        /// </para>
+        /// </remarks>
+        /// <example>
+        /// <code source="Examples\Permutation\PnExample07\PnExample07.cs" lang="cs" />
+        /// </example>
+        public int Swaps
+        {
+            get
+            {
+                var elements = new int[Picks];
+                for (int ei = 0; ei < Picks; ++ei)
+                    elements[ei] = this[ei];
+
+                return CalcSwapCount (elements, Choices);
+            }
+        }
+
+
+        /// <summary>
+        /// Get an element of the <see cref="Permutation"/> at the supplied column.
+        /// </summary>
+        /// <param name="index">Index value.</param>
+        /// <returns>Sequence value at <em>index</em>.</returns>
+        /// <example>
+        /// <code source="Examples\Permutation\PnExample05\PnExample05.cs" lang="cs" />
+        /// </example>
+        /// <exception cref="IndexOutOfRangeException">
+        /// When <em>index</em> not in range (0..<see cref="Picks"/>-1).
+        /// </exception>
+        public int this[int index]
+        {
+            get { return data[index]; }
+        }
+
+        #endregion
+
+        #region Instance methods
+
+        /// <summary>
+        /// Advance <see cref="Rank"/> a minimum while changing element at <em>nodeIndex</em>.
+        /// </summary>
+        /// <returns>Lowest index of actual changed element if successful; else <b>-1</b>.</returns>
+        /// <remarks>
+        /// This method provides support for backtracking algorithms by pruning permutations that
+        /// cannot be completed to a solution.
+        /// </remarks>
+        /// <param name="nodeIndex">Element to change.</param>
+        /// <example>
+        /// <code source="Examples\Queens\PnBacktrack\PnBacktrack.cs" lang="cs" />
+        /// </example>
+        /// <exception cref="InvalidOperationException">
+        /// When <em>Choices</em> not equal to <em>Picks</em>.
+        /// </exception>
+        /// <exception cref="ArgumentOutOfRangeException">
+        /// When <em>nodeIndex</em> not in range (0..<see cref="Picks"/>-1).
+        /// </exception>
+        public int Backtrack (int nodeIndex)
+        {
+            if (Picks != Choices)
+                throw new InvalidOperationException ("Choices and Picks must be equal.");
+
+            if (nodeIndex < 0 || nodeIndex >= this.data.Length)
+                throw new ArgumentOutOfRangeException ("nodeIndex", "Value is out of range.");
+
+            Array.Sort (this.data, nodeIndex + 1, this.data.Length - nodeIndex - 1);
+            for (int tailIndex = nodeIndex+1; tailIndex < this.data.Length; ++tailIndex)
+            {
+                int swap = this.data[tailIndex];
+                if (swap > this.data[nodeIndex])
+                {
+                    this.data[tailIndex] = this.data[nodeIndex];
+                    this.data[nodeIndex] = swap;
+                    this.rank = CalcRank (this.data, this.choices);
+                    return nodeIndex;
+                }
+            }
+
+            for (;;)
+            {
+                if (--nodeIndex < 0)
+                    return nodeIndex;
+
+                int tail = this.data[nodeIndex+1];
+                for (int tailIndex = nodeIndex+2; tailIndex < this.data.Length; ++tailIndex)
+                    if (this.data[tailIndex] < this.data[nodeIndex])
+                        this.data[tailIndex-1] = this.data[tailIndex];
+                    else
+                    {
+                        this.data[tailIndex-1] = this.data[nodeIndex];
+                        this.data[nodeIndex] = this.data[tailIndex];
+                        while (++tailIndex < this.data.Length)
+                            this.data[tailIndex-1] = this.data[tailIndex];
+                        this.data[tailIndex-1] = tail;
+                        this.rank = CalcRank (this.data, this.choices);
+                        return nodeIndex;
+                    }
+                if (tail > this.data[nodeIndex])
+                {
+                    this.data[this.data.Length-1] = this.data[nodeIndex];
+                    this.data[nodeIndex] = tail;
+                    this.rank = CalcRank (this.data, this.choices);
+                    return nodeIndex;
+                }
+                this.data[this.data.Length-1] = tail;
+            }
+        }
+
+
+        /// <summary>Compare 2 <see cref="Permutation"/>s.</summary>
+        /// <param name="obj">Target of the comparison.</param>
+        /// <returns>A signed integer indicating the sort order of this instance to <em>obj</em>.</returns>
+        public int CompareTo (object obj)
+        { return CompareTo (obj as Permutation); }
+
+
+        /// <summary>Compare 2 <see cref="Permutation"/>s.</summary>
+        /// <param name="other">Target of the comparison.</param>
+        /// <returns>A signed integer indicating the sort order of this instance to <em>other</em>.</returns>
+        public int CompareTo (Permutation other)
+        {
+            if ((object) other == null)
+                return 1;
+
+            int result = this.Picks - other.Picks;
+
+            if (result == 0)
+                if (this.Rank > other.Rank)
+                    result = 1;
+                else if (this.Rank < other.Rank)
+                    result = -1;
+
+            return result;
+        }
+
+
+        /// <summary>
+        /// Copy the entire sequence to the supplied destination.
+        /// </summary>
+        /// <param name="array">Destination of copy.</param>
+        /// <exception cref="ArgumentNullException">When <em>array</em> is <b>null</b>.</exception>
+        /// <exception cref="ArgumentException">When not enough space in <em>array</em>.</exception>
+        public void CopyTo (int[] array)
+        {
+            if (array == null)
+                throw new ArgumentNullException ("array");
+
+            if (array.Length < this.data.Length)
+                throw new ArgumentException ("Destination array is not long enough.");
+
+            this.data.CopyTo (array, 0);
+        }
+
+        /// <summary>
+        /// Indicate whether 2 <see cref="Permutation"/>s have the same value.
+        /// </summary>
+        /// <param name="obj">Target of the comparison.</param>
+        /// <returns>
+        /// <b>true</b> if <em>obj</em> has the same value as this object; otherwise, <b>false</b>.
+        /// </returns>
+        public override bool Equals (object obj)
+        { return Equals (obj as Permutation); }
+
+
+        /// <summary>
+        /// Indicate whether 2 <see cref="Permutation"/>s have the same value.
+        /// </summary>
+        /// <param name="other">Target of the comparison.</param>
+        /// <returns>
+        /// <b>true</b> if <em>other</em> has the same value as this instance;
+        /// otherwise, <b>false</b>.
+        /// </returns>
+        public bool Equals (Permutation other)
+        {
+            return (object) other != null && other.Rank == Rank && other.Picks == Picks;
+        }
+
+
+        /// <summary>Get an object-based enumerator of the elements.</summary>
+        /// <returns>Object-based elemental enumerator.</returns>
+        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
+        { return GetEnumerator(); }
+
+
+        /// <summary>Enumerate all elements of a <see cref="Permutation"/>.</summary>
+        /// <returns>
+        /// An <c>IEnumerator&lt;int&gt;</c> for this <see cref="Permutation"/>.
+        /// </returns>
+        /// <example>
+        /// <code source="Examples\Permutation\PnExample05\PnExample05.cs" lang="cs" />
+        /// </example>
+        public IEnumerator<int> GetEnumerator ()
+        {
+            for (int ei = 0; ei < Picks; ++ei)
+                yield return this.data[ei];
+        }
+
+
+        /// <summary>Get the hash oode of the <see cref="Permutation"/>.</summary>
+        /// <returns>A 32-bit signed integer hash code.</returns>
+        public override int GetHashCode ()
+        { return unchecked ((int) Rank); }
+
+
+        /// <summary>
+        /// Iterate thru all rows of the <see cref="Permutation"/> table
+        /// for every value of <see cref="Rank"/> ascending.
+        /// </summary>
+        /// <returns>An iterator for a <see cref="Permutation"/> table.</returns>
+        /// <remarks>
+        /// If the start row is not of <see cref="Rank"/> 0, the iteration will wrap around
+        /// so that <see cref="RowCount"/> items are always produced.
+        /// </remarks>
+        /// <example>
+        /// <code source="Examples\Permutation\PnExample01\PnExample01.cs" lang="cs" />
+        /// </example>
+        public IEnumerable<Permutation> GetRows ()
+        {
+            if (RowCount > 0)
+            {
+                long startRank = Rank;
+                for (Permutation current = (Permutation) MemberwiseClone();;)
+                {
+                    yield return current;
+                    current.Rank = current.Rank + 1;
+                    if (current.Rank == startRank)
+                        break;
+                }
+            }
+        }
+
+
+        /// <summary>
+        /// Iterate thru all rows of all <see cref="Permutation"/> tables for every
+        /// <see cref="Choices"/> value in the range (1..<see cref="Choices"/>).
+        /// </summary>
+        /// <returns>An iterator for a series of <see cref="Permutation"/> tables.</returns>
+        /// <example>
+        /// <code source="Examples\Permutation\PnExample02\PnExample02.cs" lang="cs" />
+        /// </example>
+        public IEnumerable<Permutation> GetRowsForAllChoices ()
+        {
+            for (int w = 1; w <= Choices; ++w)
+            {
+                Permutation current = (Permutation) MemberwiseClone();
+                current.data = new int[w];
+                for (int ei = 0; ei < current.data.Length; ++ei)
+                    current.data[ei] = ei;
+                current.choices = w;
+                current.rowCount = CalcCount (w, w);
+                current.rank = 0;
+
+                for (;;)
+                {
+                    yield return current;
+                    current.Rank = current.Rank + 1;
+                    if (current.Rank == 0)
+                        break;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Iterate thru all rows of all <see cref="Permutation"/> tables for every
+        /// <see cref="Picks"/> value in the range (1..<see cref="Picks"/>).
+        /// </summary>
+        /// <returns>An iterator for a series of <see cref="Permutation"/> tables.</returns>
+        /// <example>
+        /// <code source="Examples\Permutation\PnExample02\PnExample02.cs" lang="cs" />
+        /// </example>
+        public IEnumerable<Permutation> GetRowsForAllPicks ()
+        {
+            for (int w = 1; w <= Picks; ++w)
+            {
+                Permutation current = (Permutation) MemberwiseClone ();
+                current.data = new int[w];
+                for (int ei = 0; ei < current.data.Length; ++ei)
+                    current.data[ei] = ei;
+                current.rowCount = CalcCount (current.Choices, w);
+                current.rank = 0;
+
+                for (;;)
+                {
+                    yield return current;
+                    current.Rank = current.Rank + 1;
+                    if (current.Rank == 0)
+                        break;
+                }
+            }
+        }
+
+
+        /// <summary>
+        /// Iterate thru all rows of the <see cref="Permutation"/> table
+        /// while swapping only 2 values in each result.
+        /// </summary>
+        /// <returns>An iterator for a <see cref="Permutation"/> table.</returns>
+        /// <remarks>
+        /// <para>
+        /// The results of this iterator are commonly known as "plain changes".
+        /// </para>
+        /// <para>
+        /// Usage note:
+        /// <ul>
+        ///   <li>
+        ///     Using this iterator will not perform as fast as using a class that is
+        ///     designed and optimized for generating plain changes without the overhead
+        ///     of calculating the lexicographical rank for each row.
+        ///   </li>
+        /// </ul>
+        /// </para>
+        /// </remarks>
+        /// <example>
+        /// <code source="Examples\Permutation\PnExample07\PnExample07.cs" lang="cs" />
+        /// </example>
+        /// <exception cref="InvalidOperationException">
+        /// When <em>Choices</em> not equal to <em>Picks</em>.
+        /// </exception>
+        /// <seealso cref="PlainRank"/>
+        public IEnumerable<Permutation> GetRowsOfPlainChanges ()
+        {
+            if (Picks != Choices)
+                throw new InvalidOperationException ("Choices and Picks must be equal.");
+
+            if (RowCount > 0)
+            {
+                Permutation current = (Permutation) MemberwiseClone();
+
+                long startRank = Rank;
+                for (long plainRank = CalcPlainRank (data);;)
+                {
+                    yield return current;
+
+                    plainRank = (plainRank + 1) % RowCount;
+
+                    CalcPlainUnrank (current.data, plainRank);
+                    current.rank = CalcRank (data, current.Choices);
+
+                    if (current.Rank == startRank)
+                        yield break;
+                }
+            }
+        }
+
+
+        /// <summary>
+        /// Provide a readable form of the <see cref="Permutation"/> sequence.
+        /// </summary>
+        /// <returns>A <b>string</b> that represents the sequence.</returns>
+        /// <remarks>Result is enclosed in braces and separated by commas.</remarks>
+        /// <example>
+        /// <code source="Examples\Permutation\PnExample04\PnExample04.cs" lang="cs" />
+        /// </example>
+        public override string ToString ()
+        {
+            if (RowCount == 0)
+                return ("{ }");
+
+            StringBuilder result = new StringBuilder ("{ ");
+
+            for (int ei = 0;;)
+            {
+                result.Append (this.data[ei]);
+
+                ++ei;
+                if (ei >= Picks)
+                    break;
+
+                result.Append (", ");
+            }
+
+            result.Append (" }");
+
+            return result.ToString();
+        }
+
+        #endregion
+
+        #region Static methods
+
+        /// <summary>
+        /// Apply a <see cref="Permutation"/> sequence to rearrange the supplied list or array.
+        /// </summary>
+        /// <typeparam name="T">Type of items to rearrange.</typeparam>
+        /// <param name="arrangement">New arrangement for items.</param>
+        /// <param name="source">list of items to rearrange.</param>
+        /// <returns>Rearranged objects.</returns>
+        /// <example>
+        /// <code source="Examples\Permutation\PnExample03\PnExample03.cs" lang="cs" />
+        /// </example>
+        /// <exception cref="ArgumentNullException">When <em>arrangement</em> or <em>source</em> is <b>null</b>.</exception>
+        /// <exception cref="ArgumentOutOfRangeException">When length of
+        /// <em>source</em> is less than <see cref="Picks"/>.</exception>
+        public static List<T> Permute<T> (Permutation arrangement, IList<T> source)
+        {
+            if (arrangement == null)
+                throw new ArgumentNullException ("arrangement");
+
+            if (source == null)
+                throw new ArgumentNullException ("source");
+
+            if (source.Count < arrangement.Picks)
+                throw new ArgumentException ("Not enough supplied values.", "source");
+
+            List<T> result = new List<T> (arrangement.Picks);
+
+            for (int ai = 0; ai < arrangement.Picks; ++ai)
+                result.Add (source[arrangement[ai]]);
+
+            return result;
+        }
+
+
+        /// <summary>Indicate whether 2 <see cref="Permutation"/>s are equal.</summary>
+        /// <param name="param1">A <see cref="Permutation"/>.</param>
+        /// <param name="param2">A <see cref="Permutation"/>.</param>
+        /// <returns><b>true</b> if supplied <see cref="Permutation"/>s are equal;
+        /// otherwise, <b>false</b>.</returns>
+        public static bool operator == (Permutation param1, Permutation param2)
+        {
+            if ((object) param1 == null)
+                return (object) param2 == null;
+            else
+                return param1.Equals (param2);
+        }
+
+
+        /// <summary>Indicate whether 2 <see cref="Permutation"/>s are not equal.</summary>
+        /// <param name="param1">A <see cref="Permutation"/>.</param>
+        /// <param name="param2">A <see cref="Permutation"/>.</param>
+        /// <returns><b>true</b> if supplied sequences are not equal;
+        /// otherwise, <b>false</b>.</returns>
+        public static bool operator != (Permutation param1, Permutation param2)
+        { return !(param1 == param2); }
+
+
+        /// <summary>Indicate whether the left <see cref="Permutation"/> is less than
+        /// the right <see cref="Permutation"/>.</summary>
+        /// <param name="param1">A <see cref="Permutation"/>.</param>
+        /// <param name="param2">A <see cref="Permutation"/>.</param>
+        /// <returns><b>true</b> if the left sequence is less than
+        /// the right sequence otherwise, <b>false</b>.</returns>
+        public static bool operator < (Permutation param1, Permutation param2)
+        {
+            if ((object) param1 == null)
+                return (object) param2 != null;
+            else
+                return param1.CompareTo (param2) < 0;
+        }
+
+
+        /// <summary>Indicate whether the left <see cref="Permutation"/> is greater than
+        /// or equal to the right <see cref="Permutation"/>.</summary>
+        /// <param name="param1">A <see cref="Permutation"/>.</param>
+        /// <param name="param2">A <see cref="Permutation"/>.</param>
+        /// <returns><b>true</b> if the left sequence is greater than or equal to
+        /// the right sequence otherwise, <b>false</b>.</returns>
+        public static bool operator >= (Permutation param1, Permutation param2)
+        { return !(param1 < param2); }
+
+
+        /// <summary>Indicate whether the left <see cref="Permutation"/> is greater than
+        /// the right <see cref="Permutation"/>.</summary>
+        /// <param name="param1">A <see cref="Permutation"/>.</param>
+        /// <param name="param2">A <see cref="Permutation"/>.</param>
+        /// <returns><b>true</b> if the left sequence is greater than
+        /// the right sequence otherwise, <b>false</b>.</returns>
+        public static bool operator > (Permutation param1, Permutation param2)
+        {
+            if ((object) param1 == null)
+                return false;
+            else
+                return param1.CompareTo (param2) > 0;
+        }
+
+        /// <summary>Indicate whether the left permutation is less than or equal to
+        /// the right permutation.</summary>
+        /// <param name="param1">A <see cref="Permutation"/>.</param>
+        /// <param name="param2">A <see cref="Permutation"/>.</param>
+        /// <returns><b>true</b> if the left sequence is less than or equal to
+        /// the right sequence otherwise, <b>false</b>.</returns>
+        public static bool operator <= (Permutation param1, Permutation param2)
+        { return !(param1 > param2); }
+
+
+        /// <summary>
+        /// Returns the maximum number of elements that may be in a <see cref="Permutation"/>.
+        /// </summary>
+        /// <returns>
+        /// The maximum number of elements that may be in any <see cref="Permutation"/>
+        /// due to Int64 computational limitations.
+        /// </returns>
+        static public int MaxChoices
+        {
+            get { return Combinatoric.factorial.Length - 1; }
+        }
+
+        #endregion
+    }
+}
Added +612 -0
diff --git a/Product.cs b/Product.cs
new file mode 100644
index 0000000..763d8d3
--- /dev/null
+++ b/Product.cs
@@ -0,0 +1,612 @@
+//
+// KwCombinatorics v3.0.x/v4.0.x
+// Copyright © 2009-2012 Kasey Osborn (Kasewick@gmail.com)
+// Ms-PL - Use and redistribute freely
+//
+// File: Product.cs
+//
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Kw.Combinatorics
+{
+    /// <summary>
+    /// Represents a join of values taken from a supplied array of ranges.
+    /// </summary>
+    /// <remarks>
+    /// <para>
+    /// A cartesian product is a set of sets where each subset is constructed by picking
+    /// 1 element from each of a given number of sets. This process of joining elements to
+    /// form new sets is repeated until all possible distinct joins are made.
+    /// </para>
+    /// <para>
+    /// The <see cref="Product"/> class uses an array of integers as input where each integer
+    /// is the size of each of the composing sets. The joined sets are represented as rows in
+    /// a table where each element is a value in the range of these supplied sizes. Rows are
+    /// constructed by looping thru the rightmost ranges fastest so that the resulting table
+    /// is lexicographically ordered.
+    /// </para>
+    /// <para>
+    /// Use the <see cref="Width"/> property to get the number of joined elements.
+    /// 
+    /// Use the <see cref="RowCount"/> property to get the number of distinct joins
+    /// in the <see cref="Product"/> table.
+    ///
+    /// Use the <see cref="P:Kw.Combinatorics.Product.Item(System.Int32)">indexer</see>
+    /// to get an element of the row.
+    /// 
+    /// Use the <see cref="GetEnumerator">default enumerator</see> to iterate thru
+    /// the elements of a <see cref="Product"/> row.
+    ///
+    /// Use the <see cref="Permute">Permute</see>
+    /// method to rearrange a supplied list based on the values in a row.
+    /// </para>
+    /// <para>
+    /// Use the <see cref="Rank"/> property to get or set the row index in the ordered
+    /// <see cref="Product"/> table of joins.
+    /// 
+    /// Use <see cref="GetRows"/> to iterate thru all possible joins
+    ///  of the<see cref="Product"/> ordered by <see cref="Rank"/>.
+    /// </para>
+    /// <para>
+    /// The default appearance of a <see cref="Product"/> row is a list of integers
+    /// (starting at 0) enclosed in braces. The appearance may be tailored 3 ways:
+    /// <ul>
+    ///   <li>
+    ///     Map each element to a different value using the
+    ///     <see cref="GetEnumerator">default enumerator</see> or the
+    ///     <see cref="P:Kw.Combinatorics.Product.Item(System.Int32)">indexer</see>.
+    ///   </li>
+    ///   <li>
+    ///     Use the <see cref="Permute">Permute</see> method and output the rearranged values.
+    ///   </li>
+    ///   <li>
+    ///     Define a subclass of <see cref="Product"/> and override
+    ///     <see cref="System.Object.ToString">ToString()</see>.
+    ///   </li>
+    /// </ul>
+    /// </para>
+    /// <para>
+    /// For more information about cartesian products, see:
+    /// </para>
+    /// <para>
+    /// <em>http://en.wikipedia.org/wiki/Cartesian_product</em>
+    /// </para>
+    /// </remarks>
+    /// <example>
+    /// <para>
+    /// Iterating thru <c>new Product (new int[] { 2, 3, 2 }).GetRows()</c> produces:
+    /// </para>
+    /// <para>
+    /// <c>{ 0, 0, 0 }</c><br/>
+    /// <c>{ 0, 0, 1 }</c><br/>
+    /// <c>{ 0, 1, 0 }</c><br/>
+    /// <c>{ 0, 1, 1 }</c><br/>
+    /// <c>{ 0, 2, 0 }</c><br/>
+    /// <c>{ 0, 2, 1 }</c><br/>
+    /// <c>{ 1, 0, 0 }</c><br/>
+    /// <c>{ 1, 0, 1 }</c><br/>
+    /// <c>{ 1, 1, 0 }</c><br/>
+    /// <c>{ 1, 1, 1 }</c><br/>
+    /// <c>{ 1, 2, 0 }</c><br/>
+    /// <c>{ 1, 2, 1 }</c>
+    /// </para>
+    /// </example>
+    public class Product :
+        IComparable,
+        System.Collections.IEnumerable,
+        IComparable<Product>,
+        IEquatable<Product>,
+        IEnumerable<int>
+    {
+        private int[] sizes;    // Size of each column.
+        private long[] factors; // Running multiple of sizes.
+        private long rowCount;  // Row count of the table of products.
+        private long rank;      // Row index.
+
+        #region Constructors
+
+        /// <summary>
+        /// Make an empty <see cref="Product"/>.
+        /// </summary>
+        public Product ()
+        {
+            this.sizes = new int[0];
+            this.factors = new long[0];
+            this.rowCount = 0;
+            this.rank = 0;
+        }
+
+
+        /// <summary>
+        /// Make a copy of a <see cref="Product"/>.
+        /// </summary>
+        /// <param name="source">Instance to copy.</param>
+        /// <exception cref="ArgumentNullException">When <em>source</em> is <b>null</b>.</exception>
+        public Product (Product source)
+        {
+            if (source == null)
+                throw new ArgumentNullException ("source");
+
+            this.sizes = new int[source.sizes.Length];
+            this.factors = new long[source.factors.Length];
+
+            source.sizes.CopyTo (this.sizes, 0);
+            source.factors.CopyTo (this.factors, 0);
+            this.rowCount = source.rowCount;
+            this.rank = source.rank;
+        }
+
+
+        /// <summary>
+        /// Make a new <see cref="Product"/> from the supplied
+        /// column <em>sizes</em> of <see cref="Rank"/> 0.
+        /// </summary>
+        /// <param name="sizes">Size of each column.</param>
+        /// <example>
+        /// <code source="Examples\Product\PtExample01\PtExample01.cs" lang="cs" />
+        /// </example>
+        /// <exception cref="ArgumentNullException">
+        /// When <em>sizes</em> is <b>null</b>.
+        /// </exception>
+        /// <exception cref="ArgumentOutOfRangeException">
+        /// When any column size less than 0.
+        /// </exception>
+        /// <exception cref="OverflowException">
+        /// When product is too big.
+        /// </exception>
+        public Product (int[] sizes)
+        {
+            if (sizes == null)
+                throw new ArgumentNullException ("sizes");
+
+            this.sizes = new int[sizes.Length];
+            sizes.CopyTo (this.sizes, 0);
+
+            this.factors = new long[this.sizes.Length];
+            this.rowCount = sizes.Length == 0 ? 0 : 1;
+
+            for (int ei = this.sizes.Length - 1; ei >= 0; --ei)
+            {
+                if (this.sizes[ei] < 0)
+                    throw new ArgumentOutOfRangeException ("sizes", "Value is less than zero.");
+
+                this.factors[ei] = this.rowCount;
+                this.rowCount = checked (this.rowCount * this.sizes[ei]);
+            }
+        }
+
+
+        /// <summary>
+        /// Make a new <see cref="Product"/> from the supplied column <em>sizes</em> of the
+        /// supplied <em>rank</em>.
+        /// </summary>
+        /// <remarks>
+        /// If the supplied <em>rank</em> is out of the range (0..<see cref="RowCount"/>-1),
+        /// it will be normalized to the valid range. For example, a value of -1 will
+        /// produce the last row in the ordered table.
+        /// </remarks>
+        /// <param name="sizes">Size of each column.</param>
+        /// <param name="rank">Initial row index.</param>
+        /// <exception cref="ArgumentNullException"
+        /// >When <em>sizes</em> is <b>null</b>.
+        /// </exception>
+        /// <exception cref="ArgumentOutOfRangeException"
+        /// >When any column size less than 0.
+        /// </exception>
+        public Product (int[] sizes, long rank)
+            : this (sizes)
+        {
+            Rank = rank;
+        }
+
+
+        /// <summary>
+        /// Make a new <see cref="Product"/> of the supplied column <em>sizes</em>
+        /// from the supplied values.
+        /// </summary>
+        /// <param name="sizes">Size of each column.</param>
+        /// <param name="source">Integer values for the columns.</param>
+        /// <example>
+        /// <code source="Examples\Product\PtExample04\PtExample04.cs" lang="cs" />
+        /// </example>
+        /// <exception cref="ArgumentNullException">
+        /// When <em>sizes</em> or <em>source</em> is <b>null</b>.
+        /// </exception>
+        /// <exception cref="ArgumentException">
+        /// When <em>source</em> length does not match <em>sizes</em> length.
+        /// </exception>
+        /// <exception cref="ArgumentOutOfRangeException">
+        /// When any column size less than 0. When <em>source</em> data is not valid.
+        /// </exception>
+        public Product (int[] sizes, int[] source)
+            : this (sizes)
+        {
+            if (source == null)
+                throw new ArgumentNullException ("source");
+
+            if (sizes.Length != source.Length)
+                throw new ArgumentException ("Length is not valid.", "source");
+
+            for (int si = 0; si < source.Length; ++si)
+            {
+                if (source[si] < 0 || source[si] >= sizes[si])
+                    throw new ArgumentOutOfRangeException ("source", "Element is out of range.");
+
+                this.rank = this.rank * sizes[si] + source[si];
+            }
+        }
+
+        #endregion
+
+        #region Properties
+
+        /// <summary>
+        /// Row index of the join in the lexicographically ordered <see cref="Product"/> table.
+        /// </summary>
+        /// <remarks>
+        /// Any assigned value out of range will be normalized to (0..<see cref="RowCount"/>-1).
+        /// </remarks>
+        /// <example>
+        /// <code source="Examples\Product\PtExample04\PtExample04.cs" lang="cs" />
+        /// </example>
+        public long Rank
+        {
+            get
+            {
+                return rank;
+            }
+            set
+            {
+                if (RowCount == 0)
+                    rank = 0;
+                else
+                {
+                    // Normalize the new rank.
+                    if (value < 0)
+                    {
+                        rank = value % RowCount;
+                        if (rank < 0)
+                            rank += RowCount;
+                    }
+                    else
+                        rank = value < RowCount? value : value % RowCount;
+                }
+            }
+        }
+
+
+        /// <summary>
+        /// Count of distinct joins in the <see cref="Product"/> table.
+        /// </summary>
+        public long RowCount
+        {
+            get { return rowCount; }
+        }
+
+
+        /// <summary>
+        /// Number of columns in the <see cref="Product"/>.
+        /// </summary>
+        public int Width
+        {
+            get { return sizes.Length; }
+        }
+
+
+        /// <summary>
+        /// Get an element of the <see cref="Product"/> at the supplied column.
+        /// </summary>
+        /// <param name="index">Index value.</param>
+        /// <returns>Sequence value at <em>index</em>.</returns>
+        /// <example>
+        /// <code source="Examples\Product\PtExample05\PtExample05.cs" lang="cs" />
+        /// </example>
+        /// <exception cref="IndexOutOfRangeException">
+        /// When <em>index</em> not in range (0..<see cref="Width"/>-1).
+        /// </exception>
+        /// <exception cref="DivideByZeroException">
+        /// When <see cref="RowCount"/> is 0.
+        /// </exception>
+        public int this[int index]
+        {
+            get
+            {
+                long rankToElement = this.rank;
+                if (index > 0)
+                    rankToElement %= factors[index - 1];
+
+                return (int) (rankToElement / factors[index]);
+            }
+        }
+
+        #endregion
+
+        #region Instance methods
+
+        /// <summary>Compare 2 <see cref="Product"/>s.</summary>
+        /// <param name="obj">Target of the comparison.</param>
+        /// <returns>
+        /// A signed integer indicating the sort order of this instance to <em>obj</em>.
+        /// </returns>
+        public int CompareTo (object obj)
+        { return CompareTo (obj as Product); }
+
+
+        /// <summary>Compare 2 <see cref="Product"/>s.</summary>
+        /// <param name="other">Target of the comparison.</param>
+        /// <returns>
+        /// A signed integer indicating the sort order of this instance to <em>other</em>.
+        /// </returns>
+        public int CompareTo (Product other)
+        {
+            if ((object) other == null)
+                return 1;
+
+            int result = this.Width - other.Width;
+
+            if (result == 0)
+                if (this.Rank > other.Rank)
+                    result = 1;
+                else if (this.Rank < other.Rank)
+                    result = -1;
+
+            return result;
+        }
+
+
+        /// <summary>
+        /// Copy the entire sequence to the supplied destination.
+        /// </summary>
+        /// <param name="array">Destination of copy.</param>
+        /// <exception cref="ArgumentNullException">When <em>array</em> is <b>null</b>.</exception>
+        /// <exception cref="ArgumentException">When not enough space in <em>array</em>.</exception>
+        public void CopyTo (int[] array)
+        {
+            if (array == null)
+                throw new ArgumentNullException ("array");
+
+            if (array.Length < Width)
+                throw new ArgumentException ("Destination array is not long enough.");
+
+            for (int ei = 0; ei < Width; ++ei)
+                array[ei] = this[ei];
+        }
+
+
+        /// <summary>
+        /// Indicate whether 2 <see cref="Product"/>s have the same value.
+        /// </summary>
+        /// <param name="obj">Target of the comparison.</param>
+        /// <returns>
+        /// <b>true</b> if <em>obj</em> has the same value as this object; otherwise, <b>false</b>.
+        /// </returns>
+        public override bool Equals (object obj)
+        { return Equals (obj as Product); }
+
+
+        /// <summary>
+        /// Indicate whether 2 <see cref="Product"/>s have the same value.
+        /// </summary>
+        /// <param name="other">Target of the comparison.</param>
+        /// <returns>
+        /// <b>true</b> if <em>other</em> has the same value as this object;
+        /// otherwise, <b>false</b>.
+        /// </returns>
+        public bool Equals (Product other)
+        { return (object) other != null && other.Rank == Rank && other.Width == Width; }
+
+
+        /// <summary>Get an object-based enumerator of the elements.</summary>
+        /// <returns>Object-based elemental enumerator.</returns>
+        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
+        { return GetEnumerator(); }
+
+
+        /// <summary>Enumerate all elements of a <see cref="Product"/>.</summary>
+        /// <returns>
+        /// An <c>IEnumerator&lt;int&gt;</c> for this <see cref="Product"/>.
+        /// </returns>
+        /// <example>
+        /// <code source="Examples\Product\PtExample05\PtExample05.cs" lang="cs" />
+        /// </example>
+        public IEnumerator<int> GetEnumerator ()
+        {
+            for (int ei = 0; ei < Width; ++ei)
+                yield return this[ei];
+        }
+
+
+        /// <summary>Get the hash oode of the <see cref="Product"/>.</summary>
+        /// <returns>A 32-bit signed integer hash code.</returns>
+        public override int GetHashCode ()
+        { return unchecked ((int) rank); }
+
+
+        /// <summary>
+        /// Iterate thru all rows of the <see cref="Product"/> table for 
+        /// for every value of <see cref="Rank"/> ascending.
+        /// </summary>
+        /// <returns>An iterator for a <see cref="Product"/> table.</returns>
+        /// <remarks>
+        /// If the start row is not of <see cref="Rank"/> 0, the iteration will wrap around
+        /// so that <see cref="RowCount"/> items are always produced.
+        /// </remarks>
+        /// <example>
+        /// <code source="Examples\Product\PtExample01\PtExample01.cs" lang="cs" />
+        /// </example>
+        public IEnumerable<Product> GetRows ()
+        {
+            if (RowCount > 0)
+            {
+                long startRank = Rank;
+                for (Product current = (Product) MemberwiseClone();;)
+                {
+                    yield return current;
+                    current.Rank = current.Rank + 1;
+                    if (current.Rank == startRank)
+                        break;
+                }
+            }
+        }
+
+
+        /// <summary>
+        /// Get the size of a column.
+        /// </summary>
+        /// <param name="column">Column index.</param>
+        /// <returns>Number of distinct values (starting at 0) that a column may take.</returns>
+        /// <exception cref="IndexOutOfRangeException">
+        /// When <em>column</em> not in range (0..<see cref="Width"/>-1).
+        /// </exception>
+        public int Size (int column)
+        { return this.sizes[column]; }
+
+
+        /// <summary>
+        /// Provide readable form of the <see cref="Product"/> row.
+        /// </summary>
+        /// <returns>A <c>string</c> that represents the sequence.</returns>
+        /// <remarks>Result is enclosed in braces and separated by commas.</remarks>
+        /// <example>
+        /// <code source="Examples\Product\PtExample04\PtExample04.cs" lang="cs" />
+        /// </example>
+        public override string ToString ()
+        {
+            if (RowCount == 0)
+                return ("{ }");
+
+            StringBuilder result = new StringBuilder ("{ ");
+
+            for (int ei = 0; ; )
+            {
+                result.Append (this[ei]);
+
+                ++ei;
+                if (ei >= Width)
+                    break;
+
+                result.Append (", ");
+            }
+
+            result.Append (" }");
+
+            return result.ToString();
+        }
+
+        #endregion
+
+        #region Static methods
+
+        /// <summary>
+        /// Apply a <see cref="Product"/> sequence to select from the supplied lists or arrays.
+        /// </summary>
+        /// <typeparam name="T">Type of items to rearrange.</typeparam>
+        /// <param name="arrangement">New arrangement for items.</param>
+        /// <param name="source">List of List of Items or arrays to rarrange.</param>
+        /// <returns>List of rearranged items.</returns>
+        /// <example>
+        /// <code source="Examples\Product\PtExample03\PtExample03.cs" lang="cs" />
+        /// </example>
+        /// <exception cref="ArgumentNullException">When <em>arrangement</em> or <em>source</em> is <b>null</b>.</exception>
+        /// <exception cref="ArgumentException">When not enough source sets.</exception>
+        /// <exception cref="IndexOutOfRangeException">
+        /// When supplied source list is too small.
+        /// </exception>
+        public static List<T> Permute<T> (Product arrangement, IList<IList<T>> source)
+        {
+            if (arrangement == null)
+                throw new ArgumentNullException ("arrangement");
+
+            if (source == null)
+                throw new ArgumentNullException ("source");
+
+            if (source.Count < arrangement.Width)
+                throw new ArgumentException ("Not enough supplied values.", "source");
+
+            List<T> result = new List<T> (arrangement.Width);
+
+            for (int ai = 0; ai < arrangement.Width; ++ai)
+                result.Add (source[ai][arrangement[ai]]);
+
+            return result;
+        }
+
+
+        /// <summary>Indicate whether 2 <see cref="Product"/>s are equal.</summary>
+        /// <param name="param1">A <see cref="Product"/> sequence.</param>
+        /// <param name="param2">A <see cref="Product"/> sequence.</param>
+        /// <returns><b>true</b> if supplied sequences are equal;
+        /// otherwise, <b>false</b>.</returns>
+        public static bool operator == (Product param1, Product param2)
+        {
+            if ((object) param1 == null)
+                return (object) param2 == null;
+            else
+                return param1.Equals (param2);
+        }
+
+
+        /// <summary>Indicate whether 2 <see cref="Product"/>s are not equal.</summary>
+        /// <param name="param1">A <see cref="Product"/> sequence.</param>
+        /// <param name="param2">A <see cref="Product"/> sequence.</param>
+        /// <returns><b>true</b> if supplied sequences are not equal;
+        /// otherwise, <b>false</b>.</returns>
+        public static bool operator != (Product param1, Product param2)
+        { return !(param1 == param2); }
+
+
+        /// <summary>Indicate whether the left <see cref="Product"/> is less than
+        /// the right <see cref="Product"/>.</summary>
+        /// <param name="param1">A <see cref="Product"/> sequence.</param>
+        /// <param name="param2">A <see cref="Product"/> sequence.</param>
+        /// <returns><b>true</b> if the left sequence is less than
+        /// the right sequence otherwise, <b>false</b>.</returns>
+        public static bool operator < (Product param1, Product param2)
+        {
+            if ((object) param1 == null)
+                return (object) param2 != null;
+            else
+                return param1.CompareTo (param2) < 0;
+        }
+
+
+        /// <summary>Indicate whether the left <see cref="Product"/> is greater than
+        /// or equal to the right <see cref="Product"/>.</summary>
+        /// <param name="param1">A <see cref="Product"/> sequence.</param>
+        /// <param name="param2">A <see cref="Product"/> sequence.</param>
+        /// <returns><b>true</b> if the left sequence is greater than or
+        /// equal to the right sequence otherwise, <b>false</b>.</returns>
+        public static bool operator >= (Product param1, Product param2)
+        { return !(param1 < param2); }
+
+
+        /// <summary>Indicate whether the left <see cref="Product"/> is greater than
+        /// the right <see cref="Product"/>.</summary>
+        /// <param name="param1">A <see cref="Product"/> sequence.</param>
+        /// <param name="param2">A <see cref="Product"/> sequence.</param>
+        /// <returns><b>true</b> if the left sequence is greater than
+        /// the right sequence otherwise, <b>false</b>.</returns>
+        public static bool operator > (Product param1, Product param2)
+        {
+            if ((object) param1 == null)
+                return false;
+            else
+                return param1.CompareTo (param2) > 0;
+        }
+
+
+        /// <summary>Indicate whether the left <see cref="Product"/> is less than or equal to
+        /// the right <see cref="Product"/>.</summary>
+        /// <param name="param1">A <see cref="Product"/> sequence.</param>
+        /// <param name="param2">A <see cref="Product"/> sequence.</param>
+        /// <returns><b>true</b> if the left sequence is less than or equal to
+        /// the right sequence otherwise, <b>false</b>.</returns>
+        public static bool operator <= (Product param1, Product param2)
+        { return !(param1 > param2); }
+
+        #endregion
+    }
+}