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
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
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
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<int></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
+ }
+}
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
+ }
+}
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>
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
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<int></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
+ }
+}
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<int></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
+ }
+}
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<int></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
+ }
+}