├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
├── main
└── kotlin
│ └── com
│ └── github
│ └── shiguruikai
│ └── combinatoricskt
│ ├── CartesianProductGenerator.kt
│ ├── CombinationsGenerator.kt
│ ├── CombinationsWithRepetitionGenerator.kt
│ ├── CombinatorialSequence.kt
│ ├── Combinatorics.kt
│ ├── DerangementGenerator.kt
│ ├── PermutationsGenerator.kt
│ ├── PermutationsWithRepetitionGenerator.kt
│ ├── PowerSetGenerator.kt
│ ├── extensions.kt
│ └── internal
│ ├── Extensions.kt
│ └── Math.kt
└── test
├── java
└── CombinatoricsTest.java
└── kotlin
└── com
└── github
└── shiguruikai
└── combinatoricskt
├── CartesianProductGeneratorTest.kt
├── CombinationsGeneratorTest.kt
├── CombinationsWithRepetitionGeneratorTest.kt
├── CombinatorialSequenceTest.kt
├── CombinatoricsKtTest.kt
├── DerangementGeneratorTest.kt
├── ListComparator.kt
├── PermutationsGeneratorTest.kt
├── PermutationsWithRepetitionGeneratorTest.kt
├── PowerSetGeneratorTest.kt
└── internal
├── ExtensionsKtTest.kt
└── MathTest.kt
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | .idea
3 | out/
4 | build/
5 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | ## 1.6.0 (2020-02-24)
4 | - シーケンス生成クラスの名前を変更
5 |
6 | ## 1.5.0 (2020-02-23)
7 | - `Itertools`を`Combinatorics`に変更
8 |
9 | ## 1.4.0 (2019-03-20)
10 | - Kotlinを1.3.21にアップデート、それに伴いコードを微修正
11 |
12 | ## 1.3.0 (2018-07-18)
13 | - `CombinatorialSequence`の型パラメータを共変に変更
14 | - `CartesianProductGenerator`の変更点
15 | - `repeat`に巨大な数を指定したときなどでオーバーフローしたとき例外を発生させる
16 | - パフォーマンスが向上
17 |
18 | ## 1.2.0 (2018-07-15)
19 | - シーケンス生成のパフォーマンスが向上
20 |
21 | ## 1.1.0 (2018-07-14)
22 |
23 | - 完全順列を生成するクラス`DerangementGenerator`を追加
24 | - Iterable, Array の拡張関数`derangements`を追加
25 | - ソースファイルのコピーライトヘッダーを少し変更
26 | - 順列生成のパフォーマンスを少し向上
27 |
28 | ## 1.0.0 (2018-07-12)
29 | - Maven Central Repository にリリース
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 shiguruikai
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.github.shiguruikai%22%20AND%20a%3A%22combinatoricskt%22)
2 | [](/LICENSE)
3 |
4 | # combinatoricskt
5 |
6 | A combinatorics library for Kotlin.
7 |
8 | Generate the following sequence from Iterable or Array.
9 | Iterable または Array から以下のシーケンスを生成する。
10 |
11 | - [Permutations](/README.md#permutations) (順列)
12 | - [Permutations with Repetition](/README.md#permutations-with-repetition) (重複順列)
13 | - [Derangements](/README.md#derangements) (完全順列)
14 | - [Combinations](/README.md#combinations) (組合せ)
15 | - [Combinations with Repetition](/README.md#combinations-with-repetition) (重複組合せ)
16 | - [Cartesian Product](/README.md#cartesian-product) (デカルト積)
17 | - [Power Set](/README.md#power-set) (冪集合)
18 |
19 | ## Download
20 |
21 | Gradle Groovy DSL
22 | ```groovy
23 | implementation 'com.github.shiguruikai:combinatoricskt:1.6.0'
24 | ```
25 |
26 | Gradle Kotlin DSL
27 | ```kotlin
28 | implementation("com.github.shiguruikai:combinatoricskt:1.6.0")
29 | ```
30 |
31 | Apache Maven
32 | ```xml
33 |
34 | com.github.shiguruikai
35 | combinatoricskt
36 | 1.6.0
37 |
38 | ```
39 |
40 | ## Permutations
41 |
42 | ```kotlin
43 | listOf(1, 2, 3).permutations() // [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
44 | listOf(1, 2, 3).permutations(0) // [[]]
45 | listOf(1, 2, 3).permutations(1) // [[1], [2], [3]]
46 | listOf(1, 2, 3).permutations(2) // [[1, 2], [1, 3], [2, 1], [2, 3], [3, 1], [3, 2]]
47 | listOf(1, 2, 3).permutations(3) // [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
48 | listOf(1, 2, 3).permutations(4) // []
49 | Combinatorics.permutations(listOf(1, 2, 3)) // [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
50 | PermutationsGenerator.indices(3, 2) // [[0, 1], [0, 2], [1, 0], [1, 2], [2, 0], [2, 1]]
51 | ```
52 |
53 | ## Permutations with Repetition
54 |
55 | ```kotlin
56 | listOf(1, 2, 3).permutationsWithRepetition(0) // [[]]
57 | listOf(1, 2, 3).permutationsWithRepetition(1) // [[1], [2], [3]]
58 | listOf(1, 2, 3).permutationsWithRepetition(2) // [[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1], [3, 2], [3, 3]]
59 | listOf(1, 2, 3).permutationsWithRepetition(3) // [[1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 2, 1], [1, 2, 2], [1, 2, 3], [1, 3, 1], [1, 3, 2], [1, 3, 3], [2, 1, 1], [2, 1, 2], [2, 1, 3], [2, 2, 1], [2, 2, 2], [2, 2, 3], [2, 3, 1], [2, 3, 2], [2, 3, 3], [3, 1, 1], [3, 1, 2], [3, 1, 3], [3, 2, 1], [3, 2, 2], [3, 2, 3], [3, 3, 1], [3, 3, 2], [3, 3, 3]]
60 | Combinatorics.permutationsWithRepetition(listOf(1, 2, 3), 2) // [[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1], [3, 2], [3, 3]]
61 | PermutationsWithRepetitionGenerator.indices(3, 2) // [[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2]]
62 | ```
63 |
64 | ## Derangements
65 |
66 | ```kotlin
67 | listOf(1).derangements() // []
68 | listOf(1, 2).derangements() // [[2, 1]]
69 | listOf(1, 2, 3).derangements() // [[2, 3, 1], [3, 1, 2]]
70 | listOf(1, 2, 3, 4).derangements() // [[2, 1, 4, 3], [2, 3, 4, 1], [2, 4, 1, 3], [3, 1, 4, 2], [3, 4, 1, 2], [3, 4, 2, 1], [4, 1, 2, 3], [4, 3, 1, 2], [4, 3, 2, 1]]
71 | Combinatorics.derangements(listOf(1, 2, 3)) // [[2, 3, 1], [3, 1, 2]]
72 | DerangementGenerator.indices(3) // [[1, 2, 0], [2, 0, 1]]
73 | ```
74 |
75 | ## Combinations
76 |
77 | ```kotlin
78 | listOf(1, 2, 3).combinations(0) // [[]]
79 | listOf(1, 2, 3).combinations(1) // [[1], [2], [3]]
80 | listOf(1, 2, 3).combinations(2) // [[1, 2], [1, 3], [2, 3]]
81 | listOf(1, 2, 3).combinations(3) // [[1, 2, 3]]
82 | listOf(1, 2, 3).combinations(4) // []
83 | Combinatorics.combinations(listOf(1, 2, 3), 2) // [[1, 2], [1, 3], [2, 3]]
84 | CombinationsGenerator.indices(3, 2) // [[0, 1], [0, 2], [1, 2]]
85 | ```
86 |
87 | ## Combinations with Repetition
88 |
89 | ```kotlin
90 | listOf(1, 2, 3).combinationsWithRepetition(0) // [[]]
91 | listOf(1, 2, 3).combinationsWithRepetition(1) // [[1], [2], [3]]
92 | listOf(1, 2, 3).combinationsWithRepetition(2) // [[1, 1], [1, 2], [1, 3], [2, 2], [2, 3], [3, 3]]
93 | listOf(1, 2, 3).combinationsWithRepetition(3) // [[1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 2, 2], [1, 2, 3], [1, 3, 3], [2, 2, 2], [2, 2, 3], [2, 3, 3], [3, 3, 3]]
94 | listOf(1, 2, 3).combinationsWithRepetition(4) // [[1, 1, 1, 1], [1, 1, 1, 2], [1, 1, 1, 3], [1, 1, 2, 2], [1, 1, 2, 3], [1, 1, 3, 3], [1, 2, 2, 2], [1, 2, 2, 3], [1, 2, 3, 3], [1, 3, 3, 3], [2, 2, 2, 2], [2, 2, 2, 3], [2, 2, 3, 3], [2, 3, 3, 3], [3, 3, 3, 3]]
95 | Combinatorics.combinationsWithRepetition(listOf(1, 2, 3), 2) // [[1, 1], [1, 2], [1, 3], [2, 2], [2, 3], [3, 3]]
96 | CombinationsWithRepetitionGenerator.indices(3, 2) // [[0, 0], [0, 1], [0, 2], [1, 1], [1, 2], [2, 2]]
97 | ```
98 |
99 | ## Cartesian Product
100 |
101 | ```kotlin
102 | listOf(1, 2).cartesianProduct(listOf(3, 4, 5), listOf(6)) // [[1, 3, 6], [1, 4, 6], [1, 5, 6], [2, 3, 6], [2, 4, 6], [2, 5, 6]]
103 | listOf('A', 'B').cartesianProduct(listOf('X', 'Y')) // [[A, X], [A, Y], [B, X], [B, Y]]
104 | listOf('A', 'B').cartesianProduct(listOf('X', 'Y'), repeat = 2) // [[A, X, A, X], [A, X, A, Y], [A, X, B, X], [A, X, B, Y], [A, Y, A, X], [A, Y, A, Y], [A, Y, B, X], [A, Y, B, Y], [B, X, A, X], [B, X, A, Y], [B, X, B, X], [B, X, B, Y], [B, Y, A, X], [B, Y, A, Y], [B, Y, B, X], [B, Y, B, Y]]
105 | Combinatorics.cartesianProduct(listOf("Java"), listOf(8, 11)) // [[Java, 8], [Java, 11]]
106 | CartesianProductGenerator.indices(2, 2, repeat = 1) // [[0, 0], [0, 1], [1, 0], [1, 1]]
107 | ```
108 |
109 | ## Power Set
110 |
111 | ```kotlin
112 | listOf(1, 2, 3).powerset() // [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
113 | Combinatorics.powerset(listOf('A', 'B')) // [[], [A], [B], [A, B]]
114 | PowerSetGenerator.indices(3) // [[], [0], [1], [0, 1], [2], [0, 2], [1, 2], [0, 1, 2]]
115 | ```
116 |
117 | ## License
118 |
119 | [MIT](/LICENSE)
120 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java-library'
3 | id 'maven-publish'
4 | id 'signing'
5 | id 'org.jetbrains.kotlin.jvm' version '1.3.61'
6 | id 'org.jetbrains.dokka' version '0.10.1'
7 | }
8 |
9 | group 'com.github.shiguruikai'
10 | version '1.6.0'
11 |
12 | repositories {
13 | mavenCentral()
14 | jcenter()
15 | }
16 |
17 | dependencies {
18 | implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
19 |
20 | testImplementation 'org.junit.jupiter:junit-jupiter:5.6.0'
21 | }
22 |
23 | compileKotlin {
24 | kotlinOptions.jvmTarget = '1.8'
25 | }
26 | compileTestKotlin {
27 | kotlinOptions.jvmTarget = '1.8'
28 | }
29 |
30 | test {
31 | useJUnitPlatform()
32 | }
33 |
34 | jar {
35 | manifest {
36 | attributes(
37 | 'Implementation-Title': project.name,
38 | 'Implementation-Version': project.version
39 | )
40 | }
41 | }
42 |
43 | dokka {
44 | outputFormat = 'html'
45 | outputDirectory = "$buildDir/dokka"
46 | configuration {
47 | jdkVersion = 8
48 | reportUndocumented = true
49 | }
50 | }
51 |
52 | task dokkaJavadoc(type: org.jetbrains.dokka.gradle.DokkaTask) {
53 | outputFormat = 'javadoc'
54 | outputDirectory = javadoc.destinationDir
55 | configuration {
56 | jdkVersion = 8
57 | reportUndocumented = true
58 | }
59 | }
60 |
61 | task javadocJar(type: Jar, dependsOn: dokkaJavadoc) {
62 | archiveClassifier.set('javadoc')
63 | from javadoc.destinationDir
64 | }
65 |
66 |
67 | task sourcesJar(type: Jar, dependsOn: classes) {
68 | archiveClassifier.set('sources')
69 | from sourceSets.main.allSource
70 | }
71 |
72 | java {
73 | withJavadocJar()
74 | withSourcesJar()
75 | }
76 |
77 | publishing {
78 | publications {
79 | maven(MavenPublication) {
80 | from components.java
81 | pom {
82 | name = project.name
83 | description = 'A combinatorics library for Kotlin'
84 | url = 'https://github.com/shiguruikai/combinatoricskt'
85 | licenses {
86 | license {
87 | name = 'MIT License'
88 | url = 'https://github.com/shiguruikai/combinatoricskt/blob/master/LICENSE'
89 | distribution = 'repo'
90 | }
91 | }
92 | developers {
93 | developer {
94 | id = 'shiguruikai'
95 | name = 'Shigurui Kai'
96 | email = 'kai.shigurui@gmail.com'
97 | }
98 | }
99 | scm {
100 | connection = 'scm:git:https://github.com/shiguruikai/combinatoricskt.git'
101 | developerConnection = 'scm:git:https://github.com/shiguruikai/combinatoricskt.git'
102 | url = 'https://github.com/shiguruikai/combinatoricskt'
103 | }
104 | }
105 | }
106 | }
107 | repositories {
108 | maven {
109 | def releasesRepoUrl = 'https://oss.sonatype.org/service/local/staging/deploy/maven2'
110 | def snapshotsRepoUrl = 'https://oss.sonatype.org/content/repositories/snapshots'
111 | url project.version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
112 | credentials {
113 | username = sonatypeUsername
114 | password = sonatypePassword
115 | }
116 | }
117 | }
118 | }
119 |
120 | signing {
121 | useGpgCmd()
122 | sign publishing.publications.maven
123 | }
124 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | systemProp.org.gradle.internal.publish.checksums.insecure=true
2 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shiguruikai/combinatoricskt/39fad45833e2f1808367044c62805657a31e22ff/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 | # Determine the Java command to use to start the JVM.
86 | if [ -n "$JAVA_HOME" ] ; then
87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
88 | # IBM's JDK on AIX uses strange locations for the executables
89 | JAVACMD="$JAVA_HOME/jre/sh/java"
90 | else
91 | JAVACMD="$JAVA_HOME/bin/java"
92 | fi
93 | if [ ! -x "$JAVACMD" ] ; then
94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
95 |
96 | Please set the JAVA_HOME variable in your environment to match the
97 | location of your Java installation."
98 | fi
99 | else
100 | JAVACMD="java"
101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
102 |
103 | Please set the JAVA_HOME variable in your environment to match the
104 | location of your Java installation."
105 | fi
106 |
107 | # Increase the maximum file descriptors if we can.
108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
109 | MAX_FD_LIMIT=`ulimit -H -n`
110 | if [ $? -eq 0 ] ; then
111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
112 | MAX_FD="$MAX_FD_LIMIT"
113 | fi
114 | ulimit -n $MAX_FD
115 | if [ $? -ne 0 ] ; then
116 | warn "Could not set maximum file descriptor limit: $MAX_FD"
117 | fi
118 | else
119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
120 | fi
121 | fi
122 |
123 | # For Darwin, add options to specify how the application appears in the dock
124 | if $darwin; then
125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
126 | fi
127 |
128 | # For Cygwin or MSYS, switch paths to Windows format before running java
129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
132 | JAVACMD=`cygpath --unix "$JAVACMD"`
133 |
134 | # We build the pattern for arguments to be converted via cygpath
135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
136 | SEP=""
137 | for dir in $ROOTDIRSRAW ; do
138 | ROOTDIRS="$ROOTDIRS$SEP$dir"
139 | SEP="|"
140 | done
141 | OURCYGPATTERN="(^($ROOTDIRS))"
142 | # Add a user-defined pattern to the cygpath arguments
143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
145 | fi
146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
147 | i=0
148 | for arg in "$@" ; do
149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
151 |
152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
154 | else
155 | eval `echo args$i`="\"$arg\""
156 | fi
157 | i=`expr $i + 1`
158 | done
159 | case $i in
160 | 0) set -- ;;
161 | 1) set -- "$args0" ;;
162 | 2) set -- "$args0" "$args1" ;;
163 | 3) set -- "$args0" "$args1" "$args2" ;;
164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
170 | esac
171 | fi
172 |
173 | # Escape application args
174 | save () {
175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
176 | echo " "
177 | }
178 | APP_ARGS=`save "$@"`
179 |
180 | # Collect all arguments for the java command, following the shell quoting and substitution rules
181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
182 |
183 | exec "$JAVACMD" "$@"
184 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto init
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto init
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :init
68 | @rem Get command-line arguments, handling Windows variants
69 |
70 | if not "%OS%" == "Windows_NT" goto win9xME_args
71 |
72 | :win9xME_args
73 | @rem Slurp the command line arguments.
74 | set CMD_LINE_ARGS=
75 | set _SKIP=2
76 |
77 | :win9xME_args_slurp
78 | if "x%~1" == "x" goto execute
79 |
80 | set CMD_LINE_ARGS=%*
81 |
82 | :execute
83 | @rem Setup the command line
84 |
85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
86 |
87 | @rem Execute Gradle
88 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
89 |
90 | :end
91 | @rem End local scope for the variables with windows NT shell
92 | if "%ERRORLEVEL%"=="0" goto mainEnd
93 |
94 | :fail
95 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
96 | rem the _cmd.exe /c_ return code!
97 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
98 | exit /b 1
99 |
100 | :mainEnd
101 | if "%OS%"=="Windows_NT" endlocal
102 |
103 | :omega
104 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'combinatoricskt'
2 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/shiguruikai/combinatoricskt/CartesianProductGenerator.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 shiguruikai
3 | *
4 | * Licensed under the MIT license
5 | * https://github.com/shiguruikai/combinatoricskt/blob/master/LICENSE
6 | */
7 |
8 | package com.github.shiguruikai.combinatoricskt
9 |
10 | import com.github.shiguruikai.combinatoricskt.internal.mapToArray
11 | import com.github.shiguruikai.combinatoricskt.internal.times
12 | import java.math.BigInteger
13 |
14 | /**
15 | * The class [CartesianProductGenerator] contains methods for generating cartesian product.
16 | */
17 | object CartesianProductGenerator {
18 |
19 | private inline fun build(sizes: IntArray,
20 | repeat: Int,
21 | crossinline transform: (IntArray) -> R): CombinatorialSequence {
22 | val totalSize = sizes.fold(BigInteger.ONE) { acc, size -> acc * size.toBigInteger() }.pow(repeat)
23 |
24 | val iterator = object : Iterator {
25 | val dimensions = sizes * repeat
26 | val indices = IntArray(dimensions.size)
27 | val lastIndex = indices.lastIndex
28 | var hasNext = true
29 |
30 | override fun hasNext(): Boolean = hasNext
31 |
32 | override fun next(): R {
33 | if (!hasNext()) throw NoSuchElementException()
34 | val nextValue = transform(indices)
35 | for (i in lastIndex downTo 0) {
36 | if (indices[i] >= dimensions[i] - 1) {
37 | indices[i] = 0
38 | } else {
39 | indices[i]++
40 | return nextValue
41 | }
42 | }
43 | hasNext = false
44 | return nextValue
45 | }
46 | }
47 |
48 | return CombinatorialSequence(totalSize, iterator)
49 | }
50 |
51 | /**
52 | * Returns a sequence of cartesian product of [dimensions].
53 | *
54 | * To compute the cartesian product of [dimensions] with itself,
55 | * specify the number of repetitions with the [repeat] named argument.
56 | *
57 | * @throws IllegalArgumentException if [repeat] is negative, or [dimensions] contains negative.
58 | */
59 | @JvmStatic
60 | fun indices(vararg dimensions: Int, repeat: Int = 1): CombinatorialSequence {
61 | require(repeat >= 0) { "repeat must be non-negative, was $repeat" }
62 |
63 | if (repeat == 0) {
64 | return CombinatorialSequence(BigInteger.ONE, sequenceOf(intArrayOf()))
65 | }
66 |
67 | dimensions.forEach {
68 | require(it >= 0) { "dimensions must not contain negative, was ${dimensions.contentToString()}" }
69 | if (it == 0) {
70 | return CombinatorialSequence(BigInteger.ZERO, emptySequence())
71 | }
72 | }
73 |
74 | return build(dimensions.copyOf(), repeat) { it.copyOf() }
75 | }
76 |
77 | /**
78 | * Returns a sequence of cartesian product of the elements of [iterables].
79 | *
80 | * To compute the cartesian product of [iterables] with itself,
81 | * specify the number of repetitions with the [repeat] named argument.
82 | *
83 | * @throws IllegalArgumentException if [repeat] is negative.
84 | */
85 | @JvmStatic
86 | fun generate(vararg iterables: Iterable, repeat: Int = 1): CombinatorialSequence> {
87 | require(repeat >= 0) { "repeat must be non-negative, was $repeat" }
88 |
89 | if (repeat == 0) {
90 | return CombinatorialSequence(BigInteger.ONE, sequenceOf(emptyList()))
91 | }
92 |
93 | val sizes = mutableListOf()
94 | val pools = iterables.mapToArray { iterable ->
95 | iterable.toList().also {
96 | if (it.isEmpty()) {
97 | return CombinatorialSequence(BigInteger.ZERO, emptySequence())
98 | }
99 | sizes += it.size
100 | }
101 | }
102 |
103 | return if (repeat == 1) {
104 | build(sizes.toIntArray(), repeat) { indices ->
105 | indices.mapIndexed { index, it -> pools[index][it] }
106 | }
107 | } else {
108 | build(sizes.toIntArray(), repeat) { indices ->
109 | var index = 0
110 | indices.map {
111 | pools[index++][it].also {
112 | if (index >= pools.size) {
113 | index = 0
114 | }
115 | }
116 | }
117 | }
118 | }
119 | }
120 |
121 | /**
122 | * Returns a sequence of cartesian product of the elements of [arrays].
123 | *
124 | * To compute the cartesian product of [arrays] with itself,
125 | * specify the number of repetitions with the [repeat] named argument.
126 | *
127 | * @throws IllegalArgumentException if [repeat] is negative.
128 | */
129 | inline fun generate(vararg arrays: Array, repeat: Int = 1): CombinatorialSequence> {
130 | require(repeat >= 0) { "repeat must be non-negative, was $repeat" }
131 |
132 | if (repeat == 0) {
133 | return CombinatorialSequence(BigInteger.ONE, sequenceOf(emptyArray()))
134 | }
135 |
136 | var total = BigInteger.ONE
137 | val pools = arrays.map {
138 | if (it.isEmpty()) {
139 | return CombinatorialSequence(BigInteger.ZERO, emptySequence())
140 | }
141 | total *= it.size.toBigInteger()
142 | it.copyOf().asSequence()
143 | } * repeat
144 | total = total.pow(repeat)
145 |
146 | var sequence = sequenceOf(emptyArray())
147 | pools.forEach { pool ->
148 | sequence = sequence.flatMap { a -> pool.map { b -> a + b } }
149 | }
150 |
151 | return CombinatorialSequence(total, sequence)
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/shiguruikai/combinatoricskt/CombinationsGenerator.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 shiguruikai
3 | *
4 | * Licensed under the MIT license
5 | * https://github.com/shiguruikai/combinatoricskt/blob/master/LICENSE
6 | */
7 |
8 | package com.github.shiguruikai.combinatoricskt
9 |
10 | import com.github.shiguruikai.combinatoricskt.internal.combinations
11 | import com.github.shiguruikai.combinatoricskt.internal.mapToArray
12 | import java.math.BigInteger
13 |
14 | /**
15 | * The class [CombinationsGenerator] contains methods for generating combinations.
16 | */
17 | object CombinationsGenerator {
18 |
19 | @PublishedApi
20 | internal inline fun build(n: Int, r: Int,
21 | crossinline transform: (IntArray) -> R): CombinatorialSequence {
22 | val totalSize = combinations(n, r)
23 |
24 | val iterator = object : Iterator {
25 | val indices = IntArray(r) { it }
26 | var hasNext = true
27 |
28 | override fun hasNext(): Boolean = hasNext
29 |
30 | override fun next(): R {
31 | if (!hasNext()) throw NoSuchElementException()
32 | val nextValue = transform(indices)
33 | for (i in r - 1 downTo 0) {
34 | if (indices[i] != i + n - r) {
35 | var v = indices[i]
36 | for (j in i until r) {
37 | indices[j] = ++v
38 | }
39 | return nextValue
40 | }
41 | }
42 | hasNext = false
43 | return nextValue
44 | }
45 | }
46 |
47 | return CombinatorialSequence(totalSize, iterator)
48 | }
49 |
50 | /**
51 | * Returns a sequence of [r] number of combinations of [n] elements.
52 | *
53 | * @throws IllegalArgumentException if [r] is negative.
54 | */
55 | @JvmStatic
56 | fun indices(n: Int, r: Int): CombinatorialSequence {
57 | require(r >= 0) { "r must be non-negative, was $r" }
58 |
59 | if (r == 0) {
60 | return CombinatorialSequence(BigInteger.ONE, sequenceOf(intArrayOf()))
61 | } else if (r > n) {
62 | return CombinatorialSequence(BigInteger.ZERO, emptySequence())
63 | }
64 |
65 | return build(n, r) { it.copyOf() }
66 | }
67 |
68 | /**
69 | * Returns a sequence of combinations of [length] of the elements of [iterable].
70 | *
71 | * @throws IllegalArgumentException if [length] is negative.
72 | */
73 | @JvmStatic
74 | fun generate(iterable: Iterable, length: Int): CombinatorialSequence> {
75 | require(length >= 0) { "length must be non-negative, was $length" }
76 |
77 | if (length == 0) {
78 | return CombinatorialSequence(BigInteger.ONE, sequenceOf(emptyList()))
79 | }
80 |
81 | val pool = iterable.toList()
82 | val n = pool.size
83 |
84 | if (length > n) {
85 | return CombinatorialSequence(BigInteger.ZERO, emptySequence())
86 | }
87 |
88 | return build(n, length) { ints -> ints.map { pool[it] } }
89 | }
90 |
91 | /**
92 | * Returns a sequence of combinations of [length] of the elements of [array].
93 | *
94 | * @throws IllegalArgumentException if [length] is negative.
95 | */
96 | inline fun generate(array: Array, length: Int): CombinatorialSequence> {
97 | require(length >= 0) { "length must be non-negative, was $length" }
98 |
99 | val n = array.size
100 |
101 | if (length == 0) {
102 | return CombinatorialSequence(BigInteger.ONE, sequenceOf(emptyArray()))
103 | } else if (length > n) {
104 | return CombinatorialSequence(BigInteger.ZERO, emptySequence())
105 | }
106 |
107 | val pool = array.copyOf()
108 |
109 | return build(n, length) { ints -> ints.mapToArray { pool[it] } }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/shiguruikai/combinatoricskt/CombinationsWithRepetitionGenerator.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 shiguruikai
3 | *
4 | * Licensed under the MIT license
5 | * https://github.com/shiguruikai/combinatoricskt/blob/master/LICENSE
6 | */
7 |
8 | package com.github.shiguruikai.combinatoricskt
9 |
10 | import com.github.shiguruikai.combinatoricskt.internal.combinationsWithRepetition
11 | import com.github.shiguruikai.combinatoricskt.internal.mapToArray
12 | import java.math.BigInteger
13 |
14 | /**
15 | * The class [CombinationsWithRepetitionGenerator] contains methods for generating combinations with repetition.
16 | */
17 | object CombinationsWithRepetitionGenerator {
18 |
19 | @PublishedApi
20 | internal inline fun build(n: Int, r: Int,
21 | crossinline transform: (IntArray) -> R): CombinatorialSequence {
22 | val totalSize = combinationsWithRepetition(n, r)
23 |
24 | val iterator = object : Iterator {
25 | val indices = IntArray(r)
26 | var hasNext = true
27 |
28 | override fun hasNext(): Boolean = hasNext
29 |
30 | override fun next(): R {
31 | if (!hasNext()) throw NoSuchElementException()
32 | val nextValue = transform(indices)
33 | for (i in r - 1 downTo 0) {
34 | if (indices[i] != n - 1) {
35 | val v = indices[i] + 1
36 | for (j in i until r) {
37 | indices[j] = v
38 | }
39 | return nextValue
40 | }
41 | }
42 | hasNext = false
43 | return nextValue
44 | }
45 | }
46 |
47 | return CombinatorialSequence(totalSize, iterator)
48 | }
49 |
50 | /**
51 | * Returns a sequence of [r] number of combinations with repetition of [n] elements.
52 | *
53 | * @throws IllegalArgumentException if [r] is negative.
54 | */
55 | @JvmStatic
56 | fun indices(n: Int, r: Int): CombinatorialSequence {
57 | require(r >= 0) { "r must be non-negative, was $r" }
58 |
59 | if (r == 0) {
60 | return CombinatorialSequence(BigInteger.ONE, sequenceOf(intArrayOf()))
61 | } else if (n < 1) {
62 | return CombinatorialSequence(BigInteger.ZERO, emptySequence())
63 | }
64 |
65 | return build(n, r) { it.copyOf() }
66 | }
67 |
68 | /**
69 | * Returns a sequence of combinations with repetition of [length] of the elements of [iterable].
70 | *
71 | * @throws IllegalArgumentException if [length] is negative.
72 | */
73 | @JvmStatic
74 | fun generate(iterable: Iterable, length: Int): CombinatorialSequence> {
75 | require(length >= 0) { "length must be non-negative, was $length" }
76 |
77 | if (length == 0) {
78 | return CombinatorialSequence(BigInteger.ONE, sequenceOf(emptyList()))
79 | }
80 |
81 | val pool = iterable.toList()
82 | val n = pool.size
83 |
84 | if (n < 1) {
85 | return CombinatorialSequence(BigInteger.ZERO, emptySequence())
86 | }
87 |
88 | return build(n, length) { ints -> ints.map { pool[it] } }
89 | }
90 |
91 | /**
92 | * Returns a sequence of combinations with repetition of [length] of the elements of [array].
93 | *
94 | * @throws IllegalArgumentException if [length] is negative.
95 | */
96 | inline fun generate(array: Array, length: Int): CombinatorialSequence> {
97 | require(length >= 0) { "length must be non-negative, was $length" }
98 |
99 | val n = array.size
100 |
101 | if (length == 0) {
102 | return CombinatorialSequence(BigInteger.ONE, sequenceOf(emptyArray()))
103 | } else if (n < 1) {
104 | return CombinatorialSequence(BigInteger.ZERO, emptySequence())
105 | }
106 |
107 | val pool = array.copyOf()
108 |
109 | return build(n, length) { ints -> ints.mapToArray { pool[it] } }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/shiguruikai/combinatoricskt/CombinatorialSequence.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 shiguruikai
3 | *
4 | * Licensed under the MIT license
5 | * https://github.com/shiguruikai/combinatoricskt/blob/master/LICENSE
6 | */
7 |
8 | package com.github.shiguruikai.combinatoricskt
9 |
10 | import java.math.BigInteger
11 | import java.util.LinkedList
12 | import java.util.Spliterator
13 | import java.util.Spliterators
14 | import java.util.stream.Stream
15 | import java.util.stream.StreamSupport
16 |
17 | /**
18 | * This [CombinatorialSequence] class has the [totalSize] property of the total number of sequences.
19 | * This sequence can be iterated only once.
20 | *
21 | * @property totalSize Total number of elements of this sequence.
22 | *
23 | * @constructor Construct from sequence.
24 | *
25 | * @see kotlin.sequences.Sequence
26 | * @see kotlin.sequences.constrainOnce
27 | */
28 | class CombinatorialSequence(
29 | val totalSize: BigInteger,
30 | sequence: Sequence
31 | ) : Sequence by sequence.constrainOnce() {
32 |
33 | /**
34 | * Construct from iterator.
35 | */
36 | constructor(totalSize: BigInteger, iterator: Iterator) : this(totalSize, iterator.asSequence())
37 |
38 | /**
39 | * Returns a [List] containing all elements.
40 | *
41 | * The operation is _terminal_.
42 | */
43 | fun toList(): List = toMutableList()
44 |
45 | /**
46 | * Returns a [MutableList] containing all elements.
47 | *
48 | * The operation is _terminal_.
49 | */
50 | fun toMutableList(): MutableList = if (totalSize <= MAX_ARRAY_SIZE) {
51 | toCollection(ArrayList(totalSize.intValueExact()))
52 | } else {
53 | toCollection(LinkedList())
54 | }
55 |
56 | /**
57 | * @see [kotlin.streams.asStream]
58 | */
59 | fun asStream(): Stream = if (totalSize <= Long.MAX_VALUE.toBigInteger()) {
60 | StreamSupport.stream(Spliterators.spliterator(
61 | iterator(), totalSize.longValueExact(), Spliterator.ORDERED), false)
62 | } else {
63 | StreamSupport.stream(Spliterators.spliteratorUnknownSize(
64 | iterator(), Spliterator.ORDERED), false)
65 | }
66 |
67 | companion object {
68 |
69 | /**
70 | * The value is 2147483639.
71 | *
72 | * @see java.util.ArrayList.MAX_ARRAY_SIZE
73 | */
74 | private val MAX_ARRAY_SIZE = (Int.MAX_VALUE - 8).toBigInteger()
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/shiguruikai/combinatoricskt/Combinatorics.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 shiguruikai
3 | *
4 | * Licensed under the MIT license
5 | * https://github.com/shiguruikai/combinatoricskt/blob/master/LICENSE
6 | */
7 |
8 | @file:JvmName("Combinatorics")
9 |
10 | package com.github.shiguruikai.combinatoricskt
11 |
12 | /**
13 | * The class [Combinatorics] contains methods for generating combinatorial sequence.
14 | */
15 | object Combinatorics {
16 |
17 | /**
18 | * Returns a sequence of permutations of [length] of the elements of [iterable].
19 | *
20 | * If [length] is not specified or is null, the default for [length] is the length of [iterable].
21 | *
22 | * @throws IllegalArgumentException if [length] is negative.
23 | */
24 | @JvmStatic
25 | @JvmOverloads
26 | fun permutations(iterable: Iterable, length: Int? = null): CombinatorialSequence> =
27 | PermutationsGenerator.generate(iterable, length)
28 |
29 | /**
30 | * Returns a sequence of permutations with repetition of [length] of the elements of [iterable].
31 | *
32 | * @throws IllegalArgumentException if [length] is negative.
33 | */
34 | @JvmStatic
35 | fun permutationsWithRepetition(iterable: Iterable, length: Int): CombinatorialSequence> =
36 | PermutationsWithRepetitionGenerator.generate(iterable, length)
37 |
38 | /**
39 | * Returns a sequence of derangement of the elements of [iterable].
40 | */
41 | @JvmStatic
42 | fun derangements(iterable: Iterable): CombinatorialSequence> =
43 | DerangementGenerator.generate(iterable)
44 |
45 | /**
46 | * Returns a sequence of combinations of [length] of the elements of [iterable].
47 | *
48 | * @throws IllegalArgumentException if [length] is negative.
49 | */
50 | @JvmStatic
51 | fun combinations(iterable: Iterable, length: Int): CombinatorialSequence> =
52 | CombinationsGenerator.generate(iterable, length)
53 |
54 | /**
55 | * Returns a sequence of combinations with repetition of [length] of the elements of [iterable].
56 | *
57 | * @throws IllegalArgumentException if [length] is negative.
58 | */
59 | @JvmStatic
60 | fun combinationsWithRepetition(iterable: Iterable, length: Int): CombinatorialSequence> =
61 | CombinationsWithRepetitionGenerator.generate(iterable, length)
62 |
63 | /**
64 | * Returns a sequence of cartesian product of the elements of [iterables].
65 | *
66 | * To compute the cartesian product of [iterables] with itself,
67 | * specify the number of repetitions with the [repeat] named argument.
68 | *
69 | * @throws IllegalArgumentException if [repeat] is negative.
70 | */
71 | @SafeVarargs
72 | @JvmStatic
73 | @JvmOverloads
74 | fun cartesianProduct(vararg iterables: Iterable, repeat: Int = 1): CombinatorialSequence> =
75 | CartesianProductGenerator.generate(*iterables, repeat = repeat)
76 |
77 | /**
78 | * Returns a sequence of power set of the elements of [iterable].
79 | */
80 | @JvmStatic
81 | fun powerset(iterable: Iterable): CombinatorialSequence> =
82 | PowerSetGenerator.generate(iterable)
83 |
84 | /**
85 | * Returns a sequence of permutations of [length] of the elements of [array].
86 | *
87 | * If [length] is not specified or is null, the default for [length] is the length of [array].
88 | *
89 | * @throws IllegalArgumentException if [length] is negative.
90 | */
91 | inline fun permutations(array: Array, length: Int? = null): CombinatorialSequence> =
92 | PermutationsGenerator.generate(array, length)
93 |
94 | /**
95 | * Returns a sequence of permutations with repetition of [length] of the elements of [array].
96 | *
97 | * @throws IllegalArgumentException if [length] is negative.
98 | */
99 | inline fun permutationsWithRepetition(array: Array, length: Int): CombinatorialSequence> =
100 | PermutationsWithRepetitionGenerator.generate(array, length = length)
101 |
102 | /**
103 | * Returns a sequence of derangement of the elements of [array].
104 | */
105 | inline fun derangements(array: Array): CombinatorialSequence> =
106 | DerangementGenerator.generate(array)
107 |
108 | /**
109 | * Returns a sequence of combinations of [length] of the elements of [array].
110 | *
111 | * @throws IllegalArgumentException if [length] is negative.
112 | */
113 | inline fun combinations(array: Array, length: Int): CombinatorialSequence> =
114 | CombinationsGenerator.generate(array, length)
115 |
116 | /**
117 | * Returns a sequence of combinations with repetition of [length] of the elements of [array].
118 | *
119 | * @throws IllegalArgumentException if [length] is negative.
120 | */
121 | inline fun combinationsWithRepetition(array: Array, length: Int): CombinatorialSequence> =
122 | CombinationsWithRepetitionGenerator.generate(array, length)
123 |
124 | /**
125 | * Returns a sequence of cartesian product of the elements of [arrays].
126 | *
127 | * To compute the cartesian product of [arrays] with itself,
128 | * specify the number of repetitions with the [repeat] named argument.
129 | *
130 | * @throws IllegalArgumentException if [repeat] is negative.
131 | */
132 | inline fun cartesianProduct(vararg arrays: Array, repeat: Int = 1): CombinatorialSequence> =
133 | CartesianProductGenerator.generate(*arrays, repeat = repeat)
134 |
135 | /**
136 | * Returns a sequence of power set of the elements of [array].
137 | */
138 | inline fun powerset(array: Array): CombinatorialSequence> =
139 | PowerSetGenerator.generate(array)
140 | }
141 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/shiguruikai/combinatoricskt/DerangementGenerator.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 shiguruikai
3 | *
4 | * Licensed under the MIT license
5 | * https://github.com/shiguruikai/combinatoricskt/blob/master/LICENSE
6 | */
7 |
8 | package com.github.shiguruikai.combinatoricskt
9 |
10 | import com.github.shiguruikai.combinatoricskt.internal.mapToArray
11 | import com.github.shiguruikai.combinatoricskt.internal.subFactorial
12 | import com.github.shiguruikai.combinatoricskt.internal.swap
13 | import java.math.BigInteger
14 |
15 | /**
16 | * The class [DerangementGenerator] contains methods for generating derangement.
17 | */
18 | object DerangementGenerator {
19 |
20 | @PublishedApi
21 | internal inline fun build(n: Int,
22 | crossinline transform: (IntArray) -> R): CombinatorialSequence {
23 | val totalSize = subFactorial(n)
24 |
25 | val iterator = object : Iterator {
26 | val indices = IntArray(n) { it }
27 | val lastIndex = n - 1
28 | var hasNext = true
29 |
30 | fun nextPerm() {
31 | var i = lastIndex
32 | while (i > 0 && indices[i - 1] >= indices[i]) {
33 | i--
34 | }
35 | if (i <= 0) {
36 | hasNext = false
37 | return
38 | }
39 | var j = lastIndex
40 | while (indices[j] <= indices[i - 1]) {
41 | j--
42 | }
43 | indices.swap(i - 1, j)
44 | j = lastIndex
45 | while (i < j) {
46 | indices.swap(i, j)
47 | i++
48 | j--
49 | }
50 | }
51 |
52 | override fun hasNext(): Boolean {
53 | while (hasNext) {
54 | var i = 0
55 | if (indices.all { it != i++ }) {
56 | break
57 | }
58 | nextPerm()
59 | }
60 | return hasNext
61 | }
62 |
63 | override fun next(): R {
64 | if (!hasNext()) throw NoSuchElementException()
65 | val nextValue = transform(indices)
66 | nextPerm()
67 | return nextValue
68 | }
69 | }
70 |
71 | return CombinatorialSequence(totalSize, iterator)
72 | }
73 |
74 | /**
75 | * Returns a sequence of derangement of [n] elements.
76 | *
77 | * @throws IllegalArgumentException if [n] is negative.
78 | */
79 | @JvmStatic
80 | fun indices(n: Int): CombinatorialSequence {
81 | require(n >= 0) { "n must be non-negative, was $n" }
82 |
83 | if (n == 0) {
84 | return CombinatorialSequence(BigInteger.ONE, sequenceOf(intArrayOf()))
85 | } else if (n == 1) {
86 | return CombinatorialSequence(BigInteger.ZERO, sequenceOf())
87 | }
88 |
89 | return build(n) { it.copyOf() }
90 | }
91 |
92 | /**
93 | * Returns a sequence of derangement of the elements of [iterable].
94 | */
95 | @JvmStatic
96 | fun generate(iterable: Iterable): CombinatorialSequence> {
97 | val pool = iterable.toList()
98 | val n = pool.size
99 |
100 | if (n == 0) {
101 | return CombinatorialSequence(BigInteger.ONE, sequenceOf(emptyList()))
102 | } else if (n == 1) {
103 | return CombinatorialSequence(BigInteger.ZERO, sequenceOf())
104 | }
105 |
106 | return build(n) { ints -> ints.map { pool[it] } }
107 | }
108 |
109 | /**
110 | * Returns a sequence of derangement of the elements of [array].
111 | */
112 | inline fun generate(array: Array): CombinatorialSequence> {
113 | val n = array.size
114 |
115 | if (n == 0) {
116 | return CombinatorialSequence(BigInteger.ONE, sequenceOf(emptyArray()))
117 | } else if (n == 1) {
118 | return CombinatorialSequence(BigInteger.ZERO, sequenceOf())
119 | }
120 |
121 | val pool = array.copyOf()
122 |
123 | return build(n) { ints -> ints.mapToArray { pool[it] } }
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/shiguruikai/combinatoricskt/PermutationsGenerator.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 shiguruikai
3 | *
4 | * Licensed under the MIT license
5 | * https://github.com/shiguruikai/combinatoricskt/blob/master/LICENSE
6 | */
7 |
8 | package com.github.shiguruikai.combinatoricskt
9 |
10 | import com.github.shiguruikai.combinatoricskt.internal.mapToArray
11 | import com.github.shiguruikai.combinatoricskt.internal.mapToList
12 | import com.github.shiguruikai.combinatoricskt.internal.permutations
13 | import com.github.shiguruikai.combinatoricskt.internal.swap
14 | import java.math.BigInteger
15 |
16 | /**
17 | * The class [PermutationsGenerator] contains methods for generating permutations.
18 | */
19 | object PermutationsGenerator {
20 |
21 | @PublishedApi
22 | internal inline fun build(n: Int, r: Int,
23 | crossinline transform: (IntArray) -> R): CombinatorialSequence {
24 | val totalSize = permutations(n, r)
25 |
26 | val iterator = if (n == r) {
27 | object : Iterator {
28 | val indices = IntArray(n) { it }
29 | val lastIndex = n - 1
30 | var hasNext = true
31 |
32 | override fun hasNext(): Boolean = hasNext
33 |
34 | override fun next(): R {
35 | if (!hasNext()) throw NoSuchElementException()
36 | val nextValue = transform(indices)
37 | var i = lastIndex
38 | while (i > 0 && indices[i - 1] >= indices[i]) {
39 | i--
40 | }
41 | if (i <= 0) {
42 | hasNext = false
43 | return nextValue
44 | }
45 | var j = lastIndex
46 | while (indices[j] <= indices[i - 1]) {
47 | j--
48 | }
49 | indices.swap(i - 1, j)
50 | j = lastIndex
51 | while (i < j) {
52 | indices.swap(i, j)
53 | i++
54 | j--
55 | }
56 | return nextValue
57 | }
58 | }
59 | } else {
60 | object : Iterator {
61 | val indices = IntArray(n) { it }
62 | val cycles = IntArray(r) { n - it }
63 | var hasNext = true
64 |
65 | override fun hasNext(): Boolean = hasNext
66 |
67 | override fun next(): R {
68 | if (!hasNext()) throw NoSuchElementException()
69 | val nextValue = transform(indices)
70 | for (i in r - 1 downTo 0) {
71 | cycles[i]--
72 | if (cycles[i] == 0) {
73 | val first = indices[i]
74 | for (j in i until n - 1) {
75 | indices[j] = indices[j + 1]
76 | }
77 | indices[n - 1] = first
78 | cycles[i] = n - i
79 | } else {
80 | indices.swap(i, n - cycles[i])
81 | return nextValue
82 | }
83 | }
84 | hasNext = false
85 | return nextValue
86 | }
87 | }
88 | }
89 |
90 | return CombinatorialSequence(totalSize, iterator)
91 | }
92 |
93 | /**
94 | * Returns a sequence of [r] number of permutations [n] elements.
95 | *
96 | * If [r] is not specified or is null, the default for [r] is the length of [n].
97 | *
98 | * @throws IllegalArgumentException if [r] is negative.
99 | */
100 | @JvmStatic
101 | fun indices(n: Int, r: Int? = null): CombinatorialSequence {
102 | if (r != null) {
103 | require(r >= 0) { "r must be non-negative, was $r" }
104 | }
105 |
106 | val r1 = r ?: n
107 |
108 | if (r1 > n) {
109 | return CombinatorialSequence(BigInteger.ZERO, emptySequence())
110 | } else if (r1 == 0) {
111 | return CombinatorialSequence(BigInteger.ONE, sequenceOf(intArrayOf()))
112 | }
113 |
114 | return build(n, r1) { it.copyOf(r1) }
115 | }
116 |
117 | /**
118 | * Returns a sequence of permutations of [length] of the elements of [iterable].
119 | *
120 | * If [length] is not specified or is null, the default for [length] is the length of [iterable].
121 | *
122 | * @throws IllegalArgumentException if [length] is negative.
123 | */
124 | @JvmStatic
125 | fun generate(iterable: Iterable, length: Int? = null): CombinatorialSequence> {
126 | if (length != null) {
127 | require(length >= 0) { "length must be non-negative, was $length" }
128 | }
129 |
130 | val pool = iterable.toList()
131 | val n = pool.size
132 | val r = length ?: n
133 |
134 | if (r > n) {
135 | return CombinatorialSequence(BigInteger.ZERO, emptySequence())
136 | } else if (r == 0) {
137 | return CombinatorialSequence(BigInteger.ONE, sequenceOf(emptyList()))
138 | }
139 |
140 | return build(n, r) { indices -> indices.mapToList(r) { pool[it] } }
141 | }
142 |
143 | /**
144 | * Returns a sequence of permutations of [length] of the elements of [array].
145 | *
146 | * If [length] is not specified or is null, the default for [length] is the length of [array].
147 | *
148 | * @throws IllegalArgumentException if [length] is negative.
149 | */
150 | inline fun generate(array: Array, length: Int? = null): CombinatorialSequence> {
151 | if (length != null) {
152 | require(length >= 0) { "length must be non-negative, was $length" }
153 | }
154 |
155 | val n = array.size
156 | val r = length ?: n
157 |
158 | if (r > n) {
159 | return CombinatorialSequence(BigInteger.ZERO, emptySequence())
160 | } else if (r == 0) {
161 | return CombinatorialSequence(BigInteger.ONE, sequenceOf(emptyArray()))
162 | }
163 |
164 | val pool = array.copyOf()
165 |
166 | return build(n, r) { indices -> indices.mapToArray(r) { pool[it] } }
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/shiguruikai/combinatoricskt/PermutationsWithRepetitionGenerator.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 shiguruikai
3 | *
4 | * Licensed under the MIT license
5 | * https://github.com/shiguruikai/combinatoricskt/blob/master/LICENSE
6 | */
7 |
8 | package com.github.shiguruikai.combinatoricskt
9 |
10 | import com.github.shiguruikai.combinatoricskt.internal.mapToArray
11 | import java.math.BigInteger
12 |
13 | /**
14 | * The class [PermutationsWithRepetitionGenerator] contains methods for generating permutations with repetition.
15 | */
16 | object PermutationsWithRepetitionGenerator {
17 |
18 | @PublishedApi
19 | internal inline fun build(n: Int, r: Int,
20 | crossinline transform: (IntArray) -> R): CombinatorialSequence {
21 | val totalSize = n.toBigInteger().pow(r)
22 |
23 | val iterator = if (totalSize <= Long.MAX_VALUE.toBigInteger()) {
24 | object : Iterator {
25 | val indices = IntArray(r)
26 | var t = totalSize.longValueExact()
27 |
28 | override fun hasNext(): Boolean = t > 0
29 |
30 | override fun next(): R {
31 | if (!hasNext()) throw NoSuchElementException()
32 | t--
33 | val nextValue = transform(indices)
34 | for (i in r - 1 downTo 0) {
35 | if (indices[i] >= n - 1) {
36 | indices[i] = 0
37 | } else {
38 | indices[i]++
39 | break
40 | }
41 | }
42 | return nextValue
43 | }
44 | }
45 | } else {
46 | object : Iterator {
47 | val indices = IntArray(r)
48 | var t = totalSize
49 |
50 | override fun hasNext(): Boolean = t > BigInteger.ZERO
51 |
52 | override fun next(): R {
53 | if (!hasNext()) throw NoSuchElementException()
54 | t--
55 | val nextValue = transform(indices)
56 | for (i in r - 1 downTo 0) {
57 | if (indices[i] >= n - 1) {
58 | indices[i] = 0
59 | } else {
60 | indices[i]++
61 | break
62 | }
63 | }
64 | return nextValue
65 | }
66 | }
67 | }
68 |
69 | return CombinatorialSequence(totalSize, iterator)
70 | }
71 |
72 | /**
73 | * Returns a sequence of [r] number of permutations with repetition of [n] elements.
74 | *
75 | * @throws IllegalArgumentException if [r] is negative.
76 | */
77 | @JvmStatic
78 | fun indices(n: Int, r: Int): CombinatorialSequence {
79 | require(r >= 0) { "r must be non-negative, was $r" }
80 |
81 | if (r == 0) {
82 | return CombinatorialSequence(BigInteger.ONE, sequenceOf(intArrayOf()))
83 | }
84 |
85 | return build(n, r) { it.copyOf() }
86 | }
87 |
88 | /**
89 | * Returns a sequence of permutations with repetition of [length] of the elements of [iterable].
90 | *
91 | * @throws IllegalArgumentException if [length] is negative.
92 | */
93 | @JvmStatic
94 | fun generate(iterable: Iterable, length: Int): CombinatorialSequence> {
95 | require(length >= 0) { "length must be non-negative, was $length" }
96 |
97 | if (length == 0) {
98 | return CombinatorialSequence(BigInteger.ONE, sequenceOf(emptyList()))
99 | }
100 |
101 | val pool = iterable.toList()
102 |
103 | return build(pool.size, length) { ints -> ints.map { pool[it] } }
104 | }
105 |
106 | /**
107 | * Returns a sequence of permutations with repetition of [length] of the elements of [array].
108 | *
109 | * @throws IllegalArgumentException if [length] is negative.
110 | */
111 | inline fun generate(array: Array, length: Int): CombinatorialSequence> {
112 | require(length >= 0) { "length must be non-negative, was $length" }
113 |
114 | if (length == 0) {
115 | return CombinatorialSequence(BigInteger.ONE, sequenceOf(emptyArray()))
116 | }
117 |
118 | val pool = array.copyOf()
119 |
120 | return build(pool.size, length) { ints -> ints.mapToArray { pool[it] } }
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/shiguruikai/combinatoricskt/PowerSetGenerator.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 shiguruikai
3 | *
4 | * Licensed under the MIT license
5 | * https://github.com/shiguruikai/combinatoricskt/blob/master/LICENSE
6 | */
7 |
8 | package com.github.shiguruikai.combinatoricskt
9 |
10 | import java.util.BitSet
11 |
12 | /**
13 | * The class [PowerSetGenerator] contains methods for generating power set.
14 | */
15 | object PowerSetGenerator {
16 |
17 | @PublishedApi
18 | internal inline fun build(
19 | poolSize: Int,
20 | crossinline initial: (size: Int) -> T,
21 | crossinline transform: (index: Int, acc: T, poolIndex: Int) -> Unit
22 | ): CombinatorialSequence {
23 | val totalSize = 2.toBigInteger().pow(poolSize)
24 |
25 | val iterator = object : Iterator {
26 | val bitLength = totalSize.bitLength()
27 | val bitSet = BitSet(bitLength)
28 | val lastIndex = bitLength - 1
29 | var hasNext = true
30 |
31 | override fun hasNext(): Boolean = hasNext
32 |
33 | override fun next(): R {
34 | if (!hasNext()) throw NoSuchElementException()
35 | val acc = initial(bitSet.cardinality())
36 | var i = bitSet.nextSetBit(0)
37 | var index = 0
38 | while (i != -1) {
39 | transform(index++, acc, i)
40 | i = bitSet.nextSetBit(i + 1)
41 | }
42 |
43 | i = bitSet.nextClearBit(0)
44 | if (i >= lastIndex) {
45 | hasNext = false
46 | } else {
47 | bitSet.flip(0, i + 1)
48 | }
49 |
50 | return acc
51 | }
52 | }
53 |
54 | return CombinatorialSequence(totalSize, iterator)
55 | }
56 |
57 | /**
58 | * Returns a sequence of power set of [n] elements.
59 | *
60 | * @throws IllegalArgumentException if [n] is negative.
61 | */
62 | @JvmStatic
63 | fun indices(n: Int): CombinatorialSequence {
64 | require(n >= 0) { "n must be non-negative, was $n" }
65 |
66 | return build(n, { IntArray(it) }, { index, acc, poolIndex -> acc[index] = poolIndex })
67 | }
68 |
69 | /**
70 | * Returns a sequence of power set of the elements of [iterable].
71 | */
72 | @JvmStatic
73 | fun generate(iterable: Iterable): CombinatorialSequence> {
74 | val pool = iterable.toList()
75 |
76 | return build(pool.size, { ArrayList(it) }, { _, acc, poolIndex -> acc += pool[poolIndex] })
77 | }
78 |
79 | /**
80 | * Returns a sequence of power set of the elements of [array].
81 | */
82 | inline fun generate(array: Array): CombinatorialSequence> {
83 | val pool = array.copyOf()
84 |
85 | @Suppress("UNCHECKED_CAST")
86 | return build(pool.size, { arrayOfNulls(it) }, { index, acc, poolIndex -> acc[index] = pool[poolIndex] })
87 | as CombinatorialSequence>
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/shiguruikai/combinatoricskt/extensions.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 shiguruikai
3 | *
4 | * Licensed under the MIT license
5 | * https://github.com/shiguruikai/combinatoricskt/blob/master/LICENSE
6 | */
7 |
8 | @file:Suppress("NOTHING_TO_INLINE")
9 |
10 | package com.github.shiguruikai.combinatoricskt
11 |
12 | inline fun Iterable.permutations(length: Int? = null) = Combinatorics.permutations(this, length)
13 |
14 | inline fun Iterable.permutationsWithRepetition(length: Int) = Combinatorics.permutationsWithRepetition(this, length)
15 |
16 | inline fun Iterable.derangements() = Combinatorics.derangements(this)
17 |
18 | inline fun Iterable.combinations(length: Int) = Combinatorics.combinations(this, length)
19 |
20 | inline fun Iterable.combinationsWithRepetition(length: Int) = Combinatorics.combinationsWithRepetition(this, length)
21 |
22 | inline fun Iterable.cartesianProduct(vararg others: Iterable, repeat: Int = 1) = Combinatorics.cartesianProduct(this, *others, repeat = repeat)
23 |
24 | inline fun Iterable.powerset() = Combinatorics.powerset(this)
25 |
26 | inline fun Array.permutations(length: Int? = null) = Combinatorics.permutations(this, length)
27 |
28 | inline fun Array.permutationsWithRepetition(length: Int) = Combinatorics.permutationsWithRepetition(this, length)
29 |
30 | inline fun Array.derangements() = Combinatorics.derangements(this)
31 |
32 | inline fun Array.combinations(length: Int) = Combinatorics.combinations(this, length)
33 |
34 | inline fun Array.combinationsWithRepetition(length: Int) = Combinatorics.combinationsWithRepetition(this, length)
35 |
36 | inline fun Array.cartesianProduct(vararg others: Array, repeat: Int = 1) = Combinatorics.cartesianProduct(this, *others, repeat = repeat)
37 |
38 | inline fun Array.powerset() = Combinatorics.powerset(this)
39 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/shiguruikai/combinatoricskt/internal/Extensions.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 shiguruikai
3 | *
4 | * Licensed under the MIT license
5 | * https://github.com/shiguruikai/combinatoricskt/blob/master/LICENSE
6 | */
7 |
8 | package com.github.shiguruikai.combinatoricskt.internal
9 |
10 | @PublishedApi
11 | internal operator fun List.times(n: Int): List {
12 | if (n <= 0) return emptyList()
13 | if (n == 1) return this
14 |
15 | val newSize = Math.multiplyExact(size, n)
16 | val result = ArrayList(newSize)
17 | repeat(n) {
18 | result.addAll(this)
19 | }
20 | return result
21 | }
22 |
23 | @PublishedApi
24 | internal operator fun IntArray.times(n: Int): IntArray {
25 | if (n <= 0) return intArrayOf()
26 | if (n == 1) return this
27 |
28 | val newSize = Math.multiplyExact(size, n)
29 | val result = IntArray(newSize)
30 | repeat(n) {
31 | System.arraycopy(this, 0, result, size * it, size)
32 | }
33 | return result
34 | }
35 |
36 | @PublishedApi
37 | internal inline fun IntArray.mapToList(newSize: Int = size, transform: (Int) -> R): List {
38 | val result = ArrayList(newSize)
39 | repeat(newSize) {
40 | result += transform(this[it])
41 | }
42 | return result
43 | }
44 |
45 | @PublishedApi
46 | internal inline fun IntArray.mapToArray(newSize: Int = size, transform: (Int) -> R): Array {
47 | val result = arrayOfNulls(newSize)
48 | repeat(newSize) {
49 | result[it] = transform(this[it])
50 | }
51 | @Suppress("UNCHECKED_CAST")
52 | return result as Array
53 | }
54 |
55 | @PublishedApi
56 | internal inline fun Array.mapToArray(newSize: Int = size, transform: (T) -> R): Array {
57 | val result = arrayOfNulls(newSize)
58 | repeat(newSize) {
59 | result[it] = transform(this[it])
60 | }
61 | @Suppress("UNCHECKED_CAST")
62 | return result as Array
63 | }
64 |
65 | @Suppress("NOTHING_TO_INLINE")
66 | @PublishedApi
67 | internal inline fun IntArray.swap(i: Int, j: Int) {
68 | this[i] = this[j].also { this[j] = this[i] }
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/shiguruikai/combinatoricskt/internal/Math.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 shiguruikai
3 | *
4 | * Licensed under the MIT license
5 | * https://github.com/shiguruikai/combinatoricskt/blob/master/LICENSE
6 | */
7 |
8 | @file:JvmName("Math")
9 |
10 | package com.github.shiguruikai.combinatoricskt.internal
11 |
12 | import java.math.BigInteger
13 | import kotlin.math.max
14 |
15 | @PublishedApi
16 | internal fun factorial(n: Int): BigInteger {
17 | require(n >= 0) { "n must be non-negative, was $n" }
18 | return factorialHelper(n)
19 | }
20 |
21 | @PublishedApi
22 | internal fun permutations(n: Int, r: Int): BigInteger {
23 | require(n >= 0 && r >= 0 && n >= r)
24 | return permutationsHelper(n, r)
25 | }
26 |
27 | @PublishedApi
28 | internal fun combinations(n: Int, r: Int): BigInteger {
29 | require(n >= 0 && r >= 0 && n >= r)
30 | return permutationsHelper(n, r) / factorialHelper(r)
31 | }
32 |
33 | @PublishedApi
34 | internal fun combinationsWithRepetition(n: Int, r: Int): BigInteger {
35 | require(n >= 1 && r >= 0)
36 | return factorialHelper(n + r - 1) / factorialHelper(r) / factorialHelper(n - 1)
37 | }
38 |
39 | @PublishedApi
40 | internal fun subFactorial(n: Int): BigInteger {
41 | require(n >= 0) { "n must be non-negative, was $n" }
42 | if (n == 0) return BigInteger.ONE
43 | var prev = BigInteger.ONE
44 | var acc = BigInteger.ZERO
45 | for (i in 2..n) {
46 | val next = (i - 1).toBigInteger() * (acc + prev)
47 | prev = acc
48 | acc = next
49 | }
50 | return acc
51 | }
52 |
53 | private fun factorialHelper(n: Int): BigInteger {
54 | var acc = BigInteger.ONE
55 | for (i in 2..n) {
56 | acc *= i.toBigInteger()
57 | }
58 | return acc
59 | }
60 |
61 | private fun permutationsHelper(n: Int, r: Int): BigInteger {
62 | var acc = BigInteger.ONE
63 | for (i in max(2, n - r + 1)..n) {
64 | acc *= i.toBigInteger()
65 | }
66 | return acc
67 | }
68 |
--------------------------------------------------------------------------------
/src/test/java/CombinatoricsTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 shiguruikai
3 | *
4 | * Licensed under the MIT license
5 | * https://github.com/shiguruikai/combinatoricskt/blob/master/LICENSE
6 | */
7 |
8 | import com.github.shiguruikai.combinatoricskt.Combinatorics;
9 | import org.junit.jupiter.api.Assertions;
10 | import org.junit.jupiter.api.Test;
11 |
12 | import java.util.Arrays;
13 | import java.util.List;
14 | import java.util.stream.Collectors;
15 |
16 | public class CombinatoricsTest {
17 |
18 | private List> asList(T[][] array) {
19 | return Arrays.stream(array).map(Arrays::asList).collect(Collectors.toList());
20 | }
21 |
22 | @Test
23 | void permutations() {
24 | Assertions.assertEquals(
25 | asList(new Integer[][]{{1, 2, 3}, {1, 3, 2}, {2, 1, 3}, {2, 3, 1}, {3, 1, 2}, {3, 2, 1}}),
26 | Combinatorics.permutations(Arrays.asList(1, 2, 3)).toList()
27 | );
28 |
29 | Assertions.assertEquals(
30 | asList(new Integer[][]{{1, 2}, {1, 3}, {2, 1}, {2, 3}, {3, 1}, {3, 2}}),
31 | Combinatorics.permutations(Arrays.asList(1, 2, 3), 2).toList()
32 | );
33 | }
34 |
35 | @Test
36 | void permutationsWithRepetition() {
37 | Assertions.assertEquals(
38 | asList(new Integer[][]{{1}, {2}, {3}}),
39 | Combinatorics.permutationsWithRepetition(Arrays.asList(1, 2, 3), 1).toList()
40 | );
41 |
42 | Assertions.assertEquals(
43 | asList(new Integer[][]{{1, 1}, {1, 2}, {1, 3}, {2, 1}, {2, 2}, {2, 3}, {3, 1}, {3, 2}, {3, 3}}),
44 | Combinatorics.permutationsWithRepetition(Arrays.asList(1, 2, 3), 2).toList()
45 | );
46 | }
47 |
48 | @Test
49 | void derangements() {
50 | Assertions.assertEquals(
51 | asList(new Integer[][]{{2, 3, 1}, {3, 1, 2}}),
52 | Combinatorics.derangements(Arrays.asList(1, 2, 3)).toList()
53 | );
54 | }
55 |
56 | @Test
57 | void combinations() {
58 | Assertions.assertEquals(
59 | asList(new Integer[][]{{1}, {2}, {3}}),
60 | Combinatorics.combinations(Arrays.asList(1, 2, 3), 1).toList()
61 | );
62 |
63 | Assertions.assertEquals(
64 | asList(new Integer[][]{{1, 2}, {1, 3}, {2, 3}}),
65 | Combinatorics.combinations(Arrays.asList(1, 2, 3), 2).toList()
66 | );
67 |
68 | Assertions.assertEquals(
69 | asList(new Integer[][]{{1, 2, 3}}),
70 | Combinatorics.combinations(Arrays.asList(1, 2, 3), 3).toList()
71 | );
72 | }
73 |
74 | @Test
75 | void combinationsWithRepetition() {
76 | Assertions.assertEquals(
77 | asList(new Integer[][]{{1}, {2}, {3}}),
78 | Combinatorics.combinationsWithRepetition(Arrays.asList(1, 2, 3), 1).toList()
79 | );
80 |
81 | Assertions.assertEquals(
82 | asList(new Integer[][]{{1, 1}, {1, 2}, {1, 3}, {2, 2}, {2, 3}, {3, 3}}),
83 | Combinatorics.combinationsWithRepetition(Arrays.asList(1, 2, 3), 2).toList()
84 | );
85 | }
86 |
87 | @Test
88 | void cartesianProduct() {
89 | Assertions.assertEquals(
90 | asList(new Object[][]{{0, 'a'}, {0, 'b'}, {1, 'a'}, {1, 'b'}}),
91 | Combinatorics.cartesianProduct(Arrays.asList(0, 1), Arrays.asList('a', 'b')).toList()
92 | );
93 |
94 | Assertions.assertEquals(
95 | asList(new Integer[][]{{1, 1}, {1, 2}, {1, 3}, {2, 1}, {2, 2}, {2, 3}, {3, 1}, {3, 2}, {3, 3}}),
96 | Combinatorics.cartesianProduct(new List[]{Arrays.asList(1, 2, 3)}, 2).toList()
97 | );
98 | }
99 |
100 | @Test
101 | void powerset() {
102 | Assertions.assertEquals(
103 | asList(new Integer[][]{{}, {1}, {2}, {1, 2}, {3}, {1, 3}, {2, 3}, {1, 2, 3}}),
104 | Combinatorics.powerset(Arrays.asList(1, 2, 3)).toList()
105 | );
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/test/kotlin/com/github/shiguruikai/combinatoricskt/CartesianProductGeneratorTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 shiguruikai
3 | *
4 | * Licensed under the MIT license
5 | * https://github.com/shiguruikai/combinatoricskt/blob/master/LICENSE
6 | */
7 |
8 | package com.github.shiguruikai.combinatoricskt
9 |
10 | import com.github.shiguruikai.combinatoricskt.internal.times
11 | import org.junit.jupiter.api.Assertions.assertEquals
12 | import org.junit.jupiter.api.Test
13 | import java.math.BigInteger
14 | import kotlin.random.Random
15 |
16 | internal class CartesianProductGeneratorTest {
17 |
18 | private val emptyList = emptyList()
19 | private val emptyArray = emptyArray()
20 | private fun IntRange.toArray(): Array = toList().toTypedArray()
21 |
22 | @Test
23 | fun test_cartesianProduct_empty() {
24 | assertEquals("[]", emptyList.cartesianProduct().toList().toString())
25 | assertEquals("[]", emptyArray.cartesianProduct().toList().toString())
26 | }
27 |
28 | @Test
29 | fun test_cartesianProduct_size() {
30 | assertEquals(0, (0..3).cartesianProduct((0..4), emptyList).totalSize.intValueExact())
31 | assertEquals(0, (0..3).cartesianProduct((0..4), emptyList).count())
32 | assertEquals(60, (0..3).cartesianProduct((0..4), (0..2), repeat = 1).totalSize.intValueExact())
33 | assertEquals(60, (0..3).cartesianProduct((0..4), (0..2), repeat = 1).count())
34 | assertEquals(3600, (0..3).cartesianProduct((0..4), (0..2), repeat = 2).totalSize.intValueExact())
35 | assertEquals(3600, (0..3).cartesianProduct((0..4), (0..2), repeat = 2).count())
36 |
37 | assertEquals(0, (0..3).toArray().cartesianProduct((0..4).toArray(), arrayOf()).totalSize.intValueExact())
38 | assertEquals(0, (0..3).toArray().cartesianProduct((0..4).toArray(), arrayOf()).count())
39 | assertEquals(60, (0..3).toArray().cartesianProduct((0..4).toArray(), (0..2).toArray(), repeat = 1).totalSize.intValueExact())
40 | assertEquals(60, (0..3).toArray().cartesianProduct((0..4).toArray(), (0..2).toArray(), repeat = 1).count())
41 | assertEquals(3600, (0..3).toArray().cartesianProduct((0..4).toArray(), (0..2).toArray(), repeat = 2).totalSize.intValueExact())
42 | assertEquals(3600, (0..3).toArray().cartesianProduct((0..4).toArray(), (0..2).toArray(), repeat = 2).count())
43 |
44 | assertEquals(0, CartesianProductGenerator.indices(4, 5, 0).totalSize.intValueExact())
45 | assertEquals(0, CartesianProductGenerator.indices(4, 5, 0).count())
46 | assertEquals(60, CartesianProductGenerator.indices(4, 5, 3, repeat = 1).totalSize.intValueExact())
47 | assertEquals(60, CartesianProductGenerator.indices(4, 5, 3, repeat = 1).count())
48 | assertEquals(3600, CartesianProductGenerator.indices(4, 5, 3, repeat = 2).totalSize.intValueExact())
49 | assertEquals(3600, CartesianProductGenerator.indices(4, 5, 3, repeat = 2).count())
50 | }
51 |
52 | @Test
53 | fun test_cartesianProduct() {
54 | assertEquals(
55 | "[[0, a], [0, b], [1, a], [1, b], [2, a], [2, b]]",
56 | (0..2).cartesianProduct('a'..'b').toList().toString())
57 |
58 | assertEquals(
59 | "[[0, a, x], [0, b, x], [1, a, x], [1, b, x]]",
60 | (0..1).cartesianProduct('a'..'b', listOf('x')).toList().toString())
61 |
62 | assertEquals(
63 | "[[1, 4], [1, 5], [1, 6], [2, 4], [2, 5], [2, 6], [3, 4], [3, 5], [3, 6]]",
64 | CartesianProductGenerator.generate(
65 | arrayOf(1, 2, 3), arrayOf(4, 5, 6)).toList().map { it.contentToString() }.toString())
66 |
67 | assertEquals(
68 | "[[1, 4], [1, 5], [1, 6], [2, 4], [2, 5], [2, 6], [3, 4], [3, 5], [3, 6]]",
69 | arrayOf(1, 2, 3).cartesianProduct(arrayOf(4, 5, 6)).toList().map { it.contentToString() }.toString())
70 |
71 | assertEquals(480, (0..3).cartesianProduct((0..4), (0..5), (0..3)).count())
72 | assertEquals(0, (0..3).cartesianProduct(emptyList, (0..4), (0..5), (0..3)).count())
73 |
74 | assertEquals(
75 | (0..4).cartesianProduct((0..4)).toList(),
76 | (0..4).permutationsWithRepetition(2).toList())
77 | assertEquals(
78 | (0..4).cartesianProduct((0..4), (0..4)).toList(),
79 | (0..4).permutationsWithRepetition(3).toList())
80 |
81 | fun product1(vararg iterables: Iterable, repeat: Int = 1): Sequence> {
82 | val pools = iterables.map { it.toList() } * repeat
83 | val n = pools.size
84 | if (n == 0) {
85 | return sequenceOf(emptyList())
86 | }
87 | if (pools.any { it.isEmpty() }) {
88 | return emptySequence()
89 | }
90 | val indices = IntArray(n)
91 |
92 | return sequence {
93 | loop@ while (true) {
94 | yield(indices.mapIndexed { index, it -> pools[index][it] })
95 |
96 | for (i in n - 1 downTo 0) {
97 | indices[i]++
98 | if (indices[i] == pools[i].size) {
99 | indices[i] = 0
100 | } else {
101 | continue@loop
102 | }
103 | }
104 | break
105 | }
106 | }
107 | }
108 |
109 | fun product2(vararg iterables: Iterable, repeat: Int = 1): Sequence> {
110 | if (repeat == 0) {
111 | return CombinatorialSequence(BigInteger.ONE, sequenceOf(emptyList()))
112 | }
113 |
114 | val pools = iterables.map { it.toList().asSequence() } * repeat
115 |
116 | var sequence = sequenceOf(emptyList())
117 | pools.forEach { pool ->
118 | sequence = sequence.flatMap { a -> pool.map { b -> a + b } }
119 | }
120 |
121 | return sequence
122 | }
123 |
124 | val argTypes: Array> = arrayOf(emptyList, 'a'..'c', 0..0, ('a'..'d').toSet(), 0..3, listOf(8, 9))
125 | val random = Random
126 | repeat(20) {
127 | val iterables = (0..2).map { argTypes[random.nextInt(argTypes.size)] }.toTypedArray()
128 | val arrays = iterables.map { it.toList().toTypedArray() }.toTypedArray()
129 | val repeat = random.nextInt(4)
130 | val expectedSize = iterables.fold(BigInteger.ONE) { acc, iter -> acc * iter.count().toBigInteger() }.pow(repeat)
131 | val list = CartesianProductGenerator.generate(*iterables, repeat = repeat).toList()
132 |
133 | assertEquals(expectedSize, list.size.toBigInteger())
134 | assertEquals(list, product1(*iterables, repeat = repeat).toList())
135 | assertEquals(list, product2(*iterables, repeat = repeat).toList())
136 | assertEquals(list,
137 | CartesianProductGenerator.generate(*arrays, repeat = repeat).map { it.toList() }.toList())
138 |
139 | val indices = CartesianProductGenerator.indices(*iterables.map { it.count() }.toIntArray(), repeat = repeat).toList()
140 | val repeated = iterables.map { it.toList() } * repeat
141 | assertEquals(list, indices.map { it.zip(repeated) { a, b -> b[a] } })
142 | }
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/src/test/kotlin/com/github/shiguruikai/combinatoricskt/CombinationsGeneratorTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 shiguruikai
3 | *
4 | * Licensed under the MIT license
5 | * https://github.com/shiguruikai/combinatoricskt/blob/master/LICENSE
6 | */
7 |
8 | package com.github.shiguruikai.combinatoricskt
9 |
10 | import com.github.shiguruikai.combinatoricskt.internal.combinations
11 | import org.junit.jupiter.api.Assertions.assertArrayEquals
12 | import org.junit.jupiter.api.Assertions.assertEquals
13 | import org.junit.jupiter.api.Assertions.assertIterableEquals
14 | import org.junit.jupiter.api.Assertions.assertTrue
15 | import org.junit.jupiter.api.Test
16 | import org.junit.jupiter.api.assertThrows
17 |
18 | internal class CombinationsGeneratorTest {
19 |
20 | private val emptyList = emptyList()
21 | private val emptyArray = emptyArray()
22 |
23 | @Test
24 | fun test_combinations_empty() {
25 | assertEquals("[[]]", emptyList.combinations(0).toList().toString())
26 | assertEquals("[]", emptyList.combinations(100).toList().toString())
27 | assertIterableEquals(listOf(emptyList), emptyList.combinations(0).toList())
28 | assertIterableEquals(emptyList, emptyList.combinations(100).toList())
29 |
30 | assertEquals("[[]]", emptyArray.combinations(0).toList().map { it.toList() }.toString())
31 | assertEquals("[]", emptyArray.combinations(100).toList().map { it.toList() }.toString())
32 | assertArrayEquals(arrayOf(emptyArray), emptyArray.combinations(0).toList().toTypedArray())
33 | assertArrayEquals(emptyArray, emptyArray.combinations(100).toList().toTypedArray())
34 | }
35 |
36 | @Test
37 | fun test_combinations_exception() {
38 | assertThrows { ('a'..'c').combinations(-1) }
39 | assertThrows { emptyList.combinations(-1) }
40 | }
41 |
42 | @Test
43 | fun test_combinations() {
44 | val array = arrayOf(0, 1, 2)
45 | val expected = listOf(
46 | "[[]]",
47 | "[[0], [1], [2]]",
48 | "[[0, 1], [0, 2], [1, 2]]",
49 | "[[0, 1, 2]]",
50 | "[]")
51 |
52 | expected.forEachIndexed { index, it ->
53 | assertEquals(it, array.toList().combinations(index).toList().toString())
54 | assertEquals(it, array.combinations(index).map { it.toList() }.toList().toString())
55 | assertEquals(it, CombinationsGenerator.indices(array.size, index).map { ints -> ints.map { array[it] } }.toList().toString())
56 | }
57 |
58 | // combinations() は permutations() のシーケンスから、
59 | // 要素が(入力プールの位置に応じた順序で)ソート順になっていない項目をフィルタリングしたものと同じ
60 | fun Iterable.combinations2(length: Int): Sequence> = sequence {
61 | val pool = toList()
62 | pool.indices.permutations(length)
63 | .filter { it.sorted() == it }
64 | .forEach { list -> yield(list.map { pool[it] }) }
65 | }
66 |
67 | fun Iterable.combinations3(length: Int): Sequence> = sequence {
68 | val pool = toList()
69 | pool.indices.combinationsWithRepetition(length)
70 | .filter { it.toSet().size == length }
71 | .forEach { list -> yield(list.map { pool[it] }) }
72 | }
73 |
74 | val comparator = ListComparator()
75 |
76 | for (n in 0 until 7) {
77 | val values = (0 until n).map { 5 * it - 12 }
78 | for (r in 0 until n + 1) {
79 | val result = values.combinations(r).toList()
80 |
81 | assertEquals(result, values.toTypedArray().combinations(r).map { it.toList() }.toList())
82 | assertEquals(result, CombinationsGenerator.indices(n, r).map { ints -> ints.map { values[it] } }.toList())
83 |
84 | assertEquals(result.count(), if (r > n) 0 else combinations(n, r).intValueExact())
85 | assertEquals(result.count(), result.toSet().count())
86 | assertEquals(result, result.sortedWith(comparator))
87 |
88 | for (c in result) {
89 | assertEquals(c.count(), r)
90 | assertEquals(c.toSet().count(), r)
91 | assertEquals(c, c.sorted())
92 | assertTrue(c.all { it in values })
93 | assertEquals(c, values.filter { it in c })
94 | }
95 |
96 | assertEquals(result, values.combinations2(r).toList())
97 | assertEquals(result, values.combinations3(r).toList())
98 | }
99 | }
100 | }
101 | }
102 |
103 |
--------------------------------------------------------------------------------
/src/test/kotlin/com/github/shiguruikai/combinatoricskt/CombinationsWithRepetitionGeneratorTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 shiguruikai
3 | *
4 | * Licensed under the MIT license
5 | * https://github.com/shiguruikai/combinatoricskt/blob/master/LICENSE
6 | */
7 |
8 | package com.github.shiguruikai.combinatoricskt
9 |
10 | import com.github.shiguruikai.combinatoricskt.internal.combinationsWithRepetition
11 | import org.junit.jupiter.api.Assertions.assertArrayEquals
12 | import org.junit.jupiter.api.Assertions.assertEquals
13 | import org.junit.jupiter.api.Assertions.assertIterableEquals
14 | import org.junit.jupiter.api.Assertions.assertTrue
15 | import org.junit.jupiter.api.Test
16 | import org.junit.jupiter.api.assertThrows
17 | import java.math.BigInteger
18 |
19 | internal class CombinationsWithRepetitionGeneratorTest {
20 |
21 | private val emptyList = emptyList()
22 | private val emptyArray = emptyArray()
23 |
24 | @Test
25 | fun test_combinationsWithRepetition_empty() {
26 | assertEquals("[[]]", emptyList.combinationsWithRepetition(0).toList().toString())
27 | assertEquals("[]", emptyList.combinationsWithRepetition(100).toList().toString())
28 | assertIterableEquals(listOf(emptyList), emptyList.combinationsWithRepetition(0).toList())
29 | assertIterableEquals(emptyList, emptyList.combinationsWithRepetition(100).toList())
30 |
31 | assertEquals("[[]]", emptyArray.combinationsWithRepetition(0).toList().map { it.toList() }.toString())
32 | assertEquals("[]", emptyArray.combinationsWithRepetition(100).toList().map { it.toList() }.toString())
33 | assertArrayEquals(arrayOf(emptyArray), emptyArray.combinationsWithRepetition(0).toList().toTypedArray())
34 | assertArrayEquals(emptyArray, emptyArray.combinationsWithRepetition(100).toList().toTypedArray())
35 | }
36 |
37 | @Test
38 | fun test_combinationsWithRepetition_exception() {
39 | assertThrows { ('a'..'c').combinationsWithRepetition(-1).toList() }
40 | assertThrows { emptyList.combinationsWithRepetition(-1).toList() }
41 | }
42 |
43 | @Test
44 | fun test_combinationsWithRepetition() {
45 | assertEquals(
46 | "[[]]",
47 | (0..2).combinationsWithRepetition(0).toList().toString())
48 | assertEquals(
49 | "[[0], [1], [2]]",
50 | (0..2).combinationsWithRepetition(1).toList().toString())
51 | assertEquals(
52 | "[[0, 0], [0, 1], [0, 2], [1, 1], [1, 2], [2, 2]]",
53 | (0..2).combinationsWithRepetition(2).toList().toString())
54 | assertEquals("[" +
55 | "[0, 0, 0], [0, 0, 1], [0, 0, 2], [0, 1, 1], [0, 1, 2], " +
56 | "[0, 2, 2], [1, 1, 1], [1, 1, 2], [1, 2, 2], [2, 2, 2]]",
57 | (0..2).combinationsWithRepetition(3).toList().toString())
58 | assertEquals("[" +
59 | "[0, 0, 0, 0], [0, 0, 0, 1], [0, 0, 0, 2], [0, 0, 1, 1], [0, 0, 1, 2], " +
60 | "[0, 0, 2, 2], [0, 1, 1, 1], [0, 1, 1, 2], [0, 1, 2, 2], [0, 2, 2, 2], " +
61 | "[1, 1, 1, 1], [1, 1, 1, 2], [1, 1, 2, 2], [1, 2, 2, 2], [2, 2, 2, 2]]",
62 | (0..2).combinationsWithRepetition(4).toList().toString())
63 |
64 | // permutationsWithRepetition() は permutationsWithRepetition() のシーケンスから、
65 | // 要素が(入力プールの位置に応じた順序で)ソート順になっていない項目をフィルタリングしたものと同じ
66 | fun Iterable.cwr2(length: Int): List> = sequence {
67 | val pool = toList()
68 | pool.indices.permutationsWithRepetition(length)
69 | .filter { it.sorted() == it }
70 | .forEach { list -> yield(list.map { pool[it] }) }
71 | }.toList()
72 |
73 | fun numcombs(n: Int, r: Int): BigInteger = when (n) {
74 | 0 -> if (r > 0) BigInteger.ZERO else BigInteger.ONE
75 | else -> combinationsWithRepetition(n, r)
76 | }
77 |
78 | val comparator = ListComparator()
79 |
80 | for (n in 0 until 7) {
81 | val values = (0 until n).map { 5 * it - 12 }
82 | for (r in 0 until n + 1) {
83 | val result = values.combinationsWithRepetition(r).toList()
84 |
85 | assertEquals(result, values.toTypedArray().combinationsWithRepetition(r).map { it.toList() }.toList())
86 | assertEquals(result, CombinationsWithRepetitionGenerator.indices(n, r).map { ints -> ints.map { values[it] } }.toList())
87 |
88 | assertEquals(result.count(), numcombs(n, r).intValueExact())
89 | assertEquals(result.count(), result.toSet().count())
90 | assertEquals(result, result.sortedWith(comparator))
91 |
92 | val regularCombs = values.combinations(r).toList()
93 | if (n == 0 || r <= 1) {
94 | assertEquals(result, regularCombs)
95 | } else {
96 | assertTrue(result.toSet().containsAll(regularCombs.toSet()))
97 | }
98 |
99 | for (c in result) {
100 | assertEquals(c.count(), r)
101 | val noruns = c.groupBy { it }.map { it.key }
102 | assertEquals(noruns.count(), noruns.count())
103 | assertEquals(c, c.sorted())
104 | assertTrue(c.all { it in values })
105 | assertEquals(noruns, values.filter { it in c })
106 | }
107 |
108 | assertEquals(result, values.cwr2(r))
109 | }
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/test/kotlin/com/github/shiguruikai/combinatoricskt/CombinatorialSequenceTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 shiguruikai
3 | *
4 | * Licensed under the MIT license
5 | * https://github.com/shiguruikai/combinatoricskt/blob/master/LICENSE
6 | */
7 |
8 | package com.github.shiguruikai.combinatoricskt
9 |
10 | import org.junit.jupiter.api.Assertions.assertEquals
11 | import org.junit.jupiter.api.Test
12 | import java.math.BigInteger.ONE
13 | import java.math.BigInteger.ZERO
14 | import kotlin.streams.toList
15 |
16 | internal class CombinatorialSequenceTest {
17 |
18 | @Test
19 | fun toList() {
20 | assertEquals(listOf("test"), CombinatorialSequence(ZERO, sequenceOf("test")).toList())
21 | assertEquals(listOf("test"), CombinatorialSequence(ZERO, sequenceOf("test")).toList())
22 | assertEquals(listOf('a', 'b', 'c'), CombinatorialSequence(ZERO, ('a'..'c').asSequence()).toList())
23 | }
24 |
25 | @Test
26 | fun toMutableList() {
27 | assertEquals(listOf("test"), CombinatorialSequence(ZERO, sequenceOf("test")).toMutableList())
28 | assertEquals(listOf("test"), CombinatorialSequence(ZERO, sequenceOf("test")).toMutableList())
29 | assertEquals(listOf('a', 'b', 'c'), CombinatorialSequence(ZERO, ('a'..'c').asSequence()).toMutableList())
30 | }
31 |
32 | @Test
33 | fun asStream() {
34 | assertEquals(listOf("test"), CombinatorialSequence(ZERO, sequenceOf("test")).asStream().toList())
35 | assertEquals(listOf("test"), CombinatorialSequence(ZERO, sequenceOf("test")).asStream().toList())
36 | assertEquals(listOf('a', 'b', 'c'), CombinatorialSequence(ZERO, ('a'..'c').asSequence()).asStream().toList())
37 | }
38 |
39 | @Test
40 | fun getTotalSize() {
41 | assertEquals(ZERO, CombinatorialSequence(ZERO, sequenceOf("test")).totalSize)
42 | assertEquals(ONE, CombinatorialSequence(ONE, sequenceOf("test")).totalSize)
43 | assertEquals(123.toBigInteger(), CombinatorialSequence(123.toBigInteger(), sequenceOf("test")).totalSize)
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/test/kotlin/com/github/shiguruikai/combinatoricskt/CombinatoricsKtTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 shiguruikai
3 | *
4 | * Licensed under the MIT license
5 | * https://github.com/shiguruikai/combinatoricskt/blob/master/LICENSE
6 | */
7 |
8 | package com.github.shiguruikai.combinatoricskt
9 |
10 | import com.github.shiguruikai.combinatoricskt.internal.combinations
11 | import com.github.shiguruikai.combinatoricskt.internal.combinationsWithRepetition
12 | import com.github.shiguruikai.combinatoricskt.internal.factorial
13 | import com.github.shiguruikai.combinatoricskt.internal.permutations
14 | import com.github.shiguruikai.combinatoricskt.internal.subFactorial
15 | import org.junit.jupiter.api.Assertions.assertDoesNotThrow
16 | import org.junit.jupiter.api.Assertions.assertEquals
17 | import org.junit.jupiter.api.DisplayName
18 | import org.junit.jupiter.api.Test
19 | import org.junit.jupiter.api.assertThrows
20 | import java.math.BigInteger
21 |
22 | internal class CombinatoricsKtTest {
23 |
24 | @Test
25 | @DisplayName("CombinatorialSequence の totalSize と実際のサイズが等しいかテスト")
26 | fun test_check_size() {
27 | fun CombinatorialSequence.testCheckSize(expected: BigInteger) {
28 | assertEquals(expected, totalSize)
29 | assertEquals(expected, count().toBigInteger())
30 | }
31 |
32 | val list = ('A'..'D').toList()
33 | for (n in list.indices) {
34 | val values = list.take(n)
35 | for (r in list.indices) {
36 | values.permutations(r).testCheckSize(if (n >= 0 && r >= 0 && n >= r) permutations(n, r) else BigInteger.ZERO)
37 | values.permutationsWithRepetition(r).testCheckSize(n.toBigInteger().pow(r))
38 | values.derangements().testCheckSize(subFactorial(n))
39 | values.combinations(r).testCheckSize(if (n >= 0 && r >= 0 && n >= r) combinations(n, r) else BigInteger.ZERO)
40 | values.combinationsWithRepetition(r).testCheckSize(when {
41 | n >= 1 && r >= 0 -> combinationsWithRepetition(n, r)
42 | r == 0 -> BigInteger.ONE
43 | else -> BigInteger.ZERO
44 | })
45 | values.cartesianProduct().testCheckSize(n.toBigInteger())
46 | values.cartesianProduct(values).testCheckSize((n * n).toBigInteger())
47 | values.cartesianProduct(values, repeat = r).testCheckSize((n * n).toBigInteger().pow(r))
48 | values.powerset().testCheckSize(2.toBigInteger().pow(n))
49 | }
50 | }
51 | }
52 |
53 | @Test
54 | @DisplayName("組合せの関係をテスト")
55 | fun test_combinatorics() {
56 | val sortedList: List = ('A'..'F').sorted()
57 | val comparator = ListComparator()
58 |
59 | for (n in sortedList.indices) {
60 | val s = sortedList.take(n)
61 | for (r in sortedList.indices) {
62 | val pwr = s.permutationsWithRepetition(r).toList()
63 | val cwr = s.combinationsWithRepetition(r).toList()
64 | val perm = s.permutations(r).toList()
65 | val comb = s.combinations(r).toList()
66 |
67 | // サイズをチェックする
68 | assertEquals(
69 | pwr.count().toBigInteger(),
70 | n.toBigInteger().pow(r))
71 | assertEquals(
72 | cwr.count().toBigInteger(),
73 | when {
74 | n > 0 -> factorial(n + r - 1) / factorial(r) / factorial(n - 1)
75 | r == 0 -> BigInteger.ONE
76 | else -> BigInteger.ZERO
77 | })
78 | assertEquals(
79 | perm.count().toBigInteger(),
80 | if (r > n) BigInteger.ZERO else factorial(n) / factorial(n - r))
81 | assertEquals(
82 | comb.count().toBigInteger(),
83 | if (r > n) BigInteger.ZERO else factorial(n) / factorial(r) / factorial(n - r))
84 |
85 | // 辞書順に並んでいるかチェックする
86 | assertEquals(pwr, pwr.sortedWith(comparator))
87 | assertEquals(cwr, cwr.sortedWith(comparator))
88 | assertEquals(perm, perm.sortedWith(comparator))
89 | assertEquals(comb, comb.sortedWith(comparator))
90 |
91 | // 相互関係をチェックする
92 | assertEquals(cwr, pwr.filter { it.sorted() == it })
93 | assertEquals(perm, pwr.filter { it.toSet().count() == r })
94 | assertEquals(comb, perm.filter { it.sorted() == it })
95 | assertEquals(comb, cwr.filter { it.toSet().count() == r })
96 | assertEquals(comb, cwr.toSet().filter { it in perm })
97 | assertEquals(comb, perm.toSet().filter { it in cwr })
98 | assertEquals(comb, cwr.intersect(perm).sortedWith(comparator))
99 | }
100 | }
101 | }
102 |
103 | @Test
104 | @DisplayName("Sequence の再消費で IllegalStateException が発生するかテスト")
105 | fun test_sequence_can_be_consumed_only_once() {
106 | fun Sequence.testReuse() {
107 | //assertTrue(this is kotlin.sequences.ConstrainedOnceSequence)
108 | assertDoesNotThrow { count() }
109 | assertThrows { count() }
110 | }
111 |
112 | val range = 0..4
113 | val n = 2
114 |
115 | range.permutations(n).testReuse()
116 | range.permutationsWithRepetition(n).testReuse()
117 | range.derangements().testReuse()
118 | range.combinations(n).testReuse()
119 | range.combinationsWithRepetition(n).testReuse()
120 | range.cartesianProduct().testReuse()
121 | range.cartesianProduct(range).testReuse()
122 | range.cartesianProduct(range, repeat = n).testReuse()
123 | range.powerset().testReuse()
124 | }
125 |
126 | @Test
127 | @DisplayName("次の要素がない Iterator で next() を呼んだとき NoSuchElementException が発生するかテスト")
128 | fun test_iterator() {
129 | fun CombinatorialSequence.testIterator() {
130 | val iterator = iterator()
131 | while (iterator.hasNext()) {
132 | iterator.next()
133 | }
134 | assertThrows { iterator.next() }
135 | }
136 |
137 | for (n in 0..4) {
138 | for (r in 0..4) {
139 | PermutationsGenerator.indices(n, r).testIterator()
140 | PermutationsWithRepetitionGenerator.indices(n, r).testIterator()
141 | DerangementGenerator.indices(n).testIterator()
142 | CombinationsGenerator.indices(n, r).testIterator()
143 | CombinationsWithRepetitionGenerator.indices(n, r).testIterator()
144 | CartesianProductGenerator.indices(n, r).testIterator()
145 | CartesianProductGenerator.indices(n, r, repeat = 2).testIterator()
146 | PowerSetGenerator.indices(n).testIterator()
147 | }
148 | }
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/src/test/kotlin/com/github/shiguruikai/combinatoricskt/DerangementGeneratorTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 shiguruikai
3 | *
4 | * Licensed under the MIT license
5 | * https://github.com/shiguruikai/combinatoricskt/blob/master/LICENSE
6 | */
7 |
8 | package com.github.shiguruikai.combinatoricskt
9 |
10 | import org.junit.jupiter.api.Assertions.assertEquals
11 | import org.junit.jupiter.api.Test
12 |
13 | internal class DerangementGeneratorTest {
14 |
15 | private val emptyList = emptyList()
16 | private val emptyArray = emptyArray()
17 |
18 | @Test
19 | fun test_derangements_empty() {
20 | assertEquals("[[]]", emptyList.derangements().toList().toString())
21 | assertEquals("[[]]", emptyArray.derangements().map { it.toList() }.toList().toString())
22 |
23 | }
24 |
25 | @Test
26 | fun test_derangements() {
27 | val array = arrayOf(0, 1, 2, 3)
28 | val expected = listOf(
29 | "[[]]",
30 | "[]",
31 | "[[1, 0]]",
32 | "[[1, 2, 0], [2, 0, 1]]",
33 | "[[1, 0, 3, 2], [1, 2, 3, 0], [1, 3, 0, 2], [2, 0, 3, 1], [2, 3, 0, 1], [2, 3, 1, 0], [3, 0, 1, 2], [3, 2, 0, 1], [3, 2, 1, 0]]")
34 |
35 | expected.forEachIndexed { index, it ->
36 | assertEquals(it, DerangementGenerator.indices(index).map { it.toList() }.toList().toString())
37 | assertEquals(it, array.take(index).derangements().toList().toString())
38 | assertEquals(it, array.copyOf(index).derangements().map { it.toList() }.toList().toString())
39 | }
40 |
41 | val range = 0..6
42 | for (r in range) {
43 | val x = range.take(r)
44 | assertEquals(
45 | x.permutations().filter { list -> var i = 0; list.all { it != i++ } }.toList(),
46 | x.derangements().toList())
47 | assertEquals(x.derangements().totalSize, x.derangements().toList().size.toBigInteger())
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/test/kotlin/com/github/shiguruikai/combinatoricskt/ListComparator.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 shiguruikai
3 | *
4 | * Licensed under the MIT license
5 | * https://github.com/shiguruikai/combinatoricskt/blob/master/LICENSE
6 | */
7 |
8 | package com.github.shiguruikai.combinatoricskt
9 |
10 | class ListComparator> : Comparator> {
11 | override fun compare(a: List, b: List): Int {
12 | if (a === b) return 0
13 |
14 | a.asSequence().zip(b.asSequence()).forEach { (oa, ob) ->
15 | if (oa !== ob) {
16 | val v = oa.compareTo(ob)
17 | if (v != 0) {
18 | return v
19 | }
20 | }
21 | }
22 |
23 | return a.size - b.size
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/test/kotlin/com/github/shiguruikai/combinatoricskt/PermutationsGeneratorTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 shiguruikai
3 | *
4 | * Licensed under the MIT license
5 | * https://github.com/shiguruikai/combinatoricskt/blob/master/LICENSE
6 | */
7 |
8 | package com.github.shiguruikai.combinatoricskt
9 |
10 | import com.github.shiguruikai.combinatoricskt.internal.permutations
11 | import com.github.shiguruikai.combinatoricskt.internal.swap
12 | import org.junit.jupiter.api.Assertions.assertArrayEquals
13 | import org.junit.jupiter.api.Assertions.assertEquals
14 | import org.junit.jupiter.api.Assertions.assertIterableEquals
15 | import org.junit.jupiter.api.Assertions.assertTrue
16 | import org.junit.jupiter.api.Test
17 | import org.junit.jupiter.api.assertThrows
18 |
19 | internal class PermutationsGeneratorTest {
20 |
21 | private val emptyList = emptyList()
22 | private val emptyArray = emptyArray()
23 |
24 | @Test
25 | fun test_permutations_empty() {
26 | assertEquals("[[]]", emptyList.permutations().toList().toString())
27 | assertEquals("[[]]", emptyList.permutations(0).toList().toString())
28 | assertEquals("[]", emptyList.permutations(100).toList().toString())
29 | assertIterableEquals(listOf(emptyList), emptyList.permutations().toList())
30 | assertIterableEquals(listOf(emptyList), emptyList.permutations(0).toList())
31 | assertIterableEquals(emptyList, emptyList.permutations(100).toList())
32 |
33 | assertEquals("[[]]", emptyArray.permutations().toList().map { it.toList() }.toString())
34 | assertEquals("[[]]", emptyArray.permutations(0).toList().map { it.toList() }.toString())
35 | assertEquals("[]", emptyArray.permutations(100).toList().map { it.toList() }.toString())
36 | assertArrayEquals(arrayOf(emptyArray), emptyArray.permutations().toList().toTypedArray())
37 | assertArrayEquals(arrayOf(emptyArray), emptyArray.permutations(0).toList().toTypedArray())
38 | assertArrayEquals(emptyArray, emptyArray.permutations(100).toList().toTypedArray())
39 | }
40 |
41 | @Test
42 | fun test_permutations_exception() {
43 | assertThrows { ('a'..'c').permutations(-1).toList() }
44 | assertThrows { emptyList.permutations(-1).toList() }
45 | }
46 |
47 | @Test
48 | fun test_permutations() {
49 |
50 | assertEquals(
51 | (0..2).permutations().toList().toString(),
52 | (0..2).permutations((0..2).count()).toList().toString())
53 |
54 | val array = arrayOf(0, 1, 2)
55 | val expected = listOf(
56 | "[[]]",
57 | "[[0], [1], [2]]",
58 | "[[0, 1], [0, 2], [1, 0], [1, 2], [2, 0], [2, 1]]",
59 | "[[0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0, 1], [2, 1, 0]]",
60 | "[]")
61 | expected.forEachIndexed { index, it ->
62 | assertEquals(it, array.toList().permutations(index).toList().toString())
63 | assertEquals(it, array.permutations(index).map { it.toList() }.toList().toString())
64 | }
65 |
66 | // permutations() は permutationsWithRepetition() のシーケンスから、
67 | // 重複 (入力プールの位置に応じた順序で)を除くようフィルタリングしたものと同じ
68 | fun Iterable.permutations2(length: Int? = null): Sequence> = sequence {
69 | val pool = toList()
70 | val r = length ?: pool.size
71 | pool.indices.permutationsWithRepetition(r)
72 | .filter { it.toSet().size == r }
73 | .forEach { list -> yield(list.map { pool[it] }) }
74 | }
75 |
76 | // https://www.wikiwand.com/en/Heap%27s_algorithm
77 | // 辞書順で取得できない
78 | fun Iterable.permutations3(): Sequence> = sequence {
79 | val pool = toList()
80 | val n = pool.size
81 | val indices = IntArray(n) { it }
82 | val cycles = IntArray(n)
83 | yield(indices.map { pool[it] })
84 | var i = 0
85 | while (i < n) {
86 | if (cycles[i] < i) {
87 | if (i and 1 == 0) {
88 | indices.swap(0, i)
89 | } else {
90 | indices.swap(cycles[i], i)
91 | }
92 | yield(indices.map { pool[it] })
93 | cycles[i]++
94 | i = 0
95 | } else {
96 | cycles[i] = 0
97 | i++
98 | }
99 | }
100 | }
101 |
102 | val comparator = ListComparator()
103 |
104 | for (n in 0 until 7) {
105 | val values = (0 until n).map { 5 * it - 12 }
106 | for (r in 0 until n + 1) {
107 | val result = values.permutations(r).toList()
108 |
109 | assertEquals(result, values.toTypedArray().permutations(r).map { it.toList() }.toList())
110 | assertEquals(result, PermutationsGenerator.indices(n, r).map { ints -> ints.map { values[it] } }.toList())
111 |
112 | assertEquals(result.count(), if (r > n) 0 else permutations(n, r).intValueExact())
113 | assertEquals(result.count(), result.toSet().count())
114 | assertEquals(result, result.sortedWith(comparator))
115 |
116 | for (p in result) {
117 | assertEquals(p.count(), r)
118 | assertEquals(p.toSet().count(), r)
119 | assertTrue(p.all { it in values })
120 | }
121 |
122 | assertEquals(result, values.permutations2(r).toList())
123 |
124 | if (result.size == r) {
125 | assertEquals(result, values.permutations3().sortedWith(comparator).toList())
126 | }
127 |
128 | if (r == n) {
129 | assertEquals(result, values.permutations(null).toList())
130 | assertEquals(result, values.permutations().toList())
131 | }
132 | }
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/test/kotlin/com/github/shiguruikai/combinatoricskt/PermutationsWithRepetitionGeneratorTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 shiguruikai
3 | *
4 | * Licensed under the MIT license
5 | * https://github.com/shiguruikai/combinatoricskt/blob/master/LICENSE
6 | */
7 |
8 | package com.github.shiguruikai.combinatoricskt
9 |
10 | import org.junit.jupiter.api.Assertions.assertArrayEquals
11 | import org.junit.jupiter.api.Assertions.assertEquals
12 | import org.junit.jupiter.api.Assertions.assertIterableEquals
13 | import org.junit.jupiter.api.Test
14 | import org.junit.jupiter.api.assertThrows
15 |
16 | internal class PermutationsWithRepetitionGeneratorTest {
17 |
18 | private val emptyList = emptyList()
19 | private val emptyArray = emptyArray()
20 |
21 | @Test
22 | fun test_permutationsWithRepetition_empty() {
23 | assertEquals("[[]]", emptyList.permutationsWithRepetition(0).toList().toString())
24 | assertEquals("[]", emptyList.permutationsWithRepetition(100).toList().toString())
25 | assertIterableEquals(listOf(emptyList), emptyList.permutationsWithRepetition(0).toList())
26 | assertIterableEquals(emptyList, emptyList.permutationsWithRepetition(100).toList())
27 |
28 | assertEquals("[[]]", emptyArray.permutationsWithRepetition(0).toList().map { it.toList() }.toString())
29 | assertEquals("[]", emptyArray.permutationsWithRepetition(100).toList().map { it.toList() }.toString())
30 | assertArrayEquals(arrayOf(emptyArray), emptyArray.permutationsWithRepetition(0).toList().toTypedArray())
31 | assertArrayEquals(emptyArray, emptyArray.permutationsWithRepetition(100).toList().toTypedArray())
32 | }
33 |
34 | @Test
35 | fun test_permutationsWithRepetition_exception() {
36 | assertThrows { ('a'..'c').permutationsWithRepetition(-1) }
37 | assertThrows { emptyList.permutationsWithRepetition(-1) }
38 | }
39 |
40 | @Test
41 | fun test_permutationsWithRepetition() {
42 | val array = arrayOf(0, 1, 2)
43 | val expected = listOf(
44 | "[[]]",
45 | "[[0], [1], [2]]",
46 | "[[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2]]",
47 | "[" +
48 | "[0, 0, 0], [0, 0, 1], [0, 0, 2], [0, 1, 0], [0, 1, 1], [0, 1, 2], [0, 2, 0], [0, 2, 1], [0, 2, 2], " +
49 | "[1, 0, 0], [1, 0, 1], [1, 0, 2], [1, 1, 0], [1, 1, 1], [1, 1, 2], [1, 2, 0], [1, 2, 1], [1, 2, 2], " +
50 | "[2, 0, 0], [2, 0, 1], [2, 0, 2], [2, 1, 0], [2, 1, 1], [2, 1, 2], [2, 2, 0], [2, 2, 1], [2, 2, 2]]",
51 | "[" +
52 | "[0, 0, 0, 0], [0, 0, 0, 1], [0, 0, 0, 2], [0, 0, 1, 0], [0, 0, 1, 1], [0, 0, 1, 2], [0, 0, 2, 0], [0, 0, 2, 1], [0, 0, 2, 2], " +
53 | "[0, 1, 0, 0], [0, 1, 0, 1], [0, 1, 0, 2], [0, 1, 1, 0], [0, 1, 1, 1], [0, 1, 1, 2], [0, 1, 2, 0], [0, 1, 2, 1], [0, 1, 2, 2], " +
54 | "[0, 2, 0, 0], [0, 2, 0, 1], [0, 2, 0, 2], [0, 2, 1, 0], [0, 2, 1, 1], [0, 2, 1, 2], [0, 2, 2, 0], [0, 2, 2, 1], [0, 2, 2, 2], " +
55 | "[1, 0, 0, 0], [1, 0, 0, 1], [1, 0, 0, 2], [1, 0, 1, 0], [1, 0, 1, 1], [1, 0, 1, 2], [1, 0, 2, 0], [1, 0, 2, 1], [1, 0, 2, 2], " +
56 | "[1, 1, 0, 0], [1, 1, 0, 1], [1, 1, 0, 2], [1, 1, 1, 0], [1, 1, 1, 1], [1, 1, 1, 2], [1, 1, 2, 0], [1, 1, 2, 1], [1, 1, 2, 2], " +
57 | "[1, 2, 0, 0], [1, 2, 0, 1], [1, 2, 0, 2], [1, 2, 1, 0], [1, 2, 1, 1], [1, 2, 1, 2], [1, 2, 2, 0], [1, 2, 2, 1], [1, 2, 2, 2], " +
58 | "[2, 0, 0, 0], [2, 0, 0, 1], [2, 0, 0, 2], [2, 0, 1, 0], [2, 0, 1, 1], [2, 0, 1, 2], [2, 0, 2, 0], [2, 0, 2, 1], [2, 0, 2, 2], " +
59 | "[2, 1, 0, 0], [2, 1, 0, 1], [2, 1, 0, 2], [2, 1, 1, 0], [2, 1, 1, 1], [2, 1, 1, 2], [2, 1, 2, 0], [2, 1, 2, 1], [2, 1, 2, 2], " +
60 | "[2, 2, 0, 0], [2, 2, 0, 1], [2, 2, 0, 2], [2, 2, 1, 0], [2, 2, 1, 1], [2, 2, 1, 2], [2, 2, 2, 0], [2, 2, 2, 1], [2, 2, 2, 2]]")
61 |
62 | expected.forEachIndexed { index, it ->
63 | assertEquals(it, array.toList().permutationsWithRepetition(index).toList().toString())
64 | assertEquals(it, array.permutationsWithRepetition(index).map { it.toList() }.toList().toString())
65 | assertEquals(it, PermutationsWithRepetitionGenerator.indices(array.size, index).map { it.toList() }.toList().toString())
66 | }
67 |
68 | assertArrayEquals(
69 | array.cartesianProduct(repeat = 4).toList().toTypedArray(),
70 | array.permutationsWithRepetition(4).toList().toTypedArray())
71 | assertArrayEquals(
72 | array.permutationsWithRepetition(4).toList().toTypedArray(),
73 | array.cartesianProduct(array, array, array).toList().toTypedArray())
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/test/kotlin/com/github/shiguruikai/combinatoricskt/PowerSetGeneratorTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 shiguruikai
3 | *
4 | * Licensed under the MIT license
5 | * https://github.com/shiguruikai/combinatoricskt/blob/master/LICENSE
6 | */
7 |
8 | package com.github.shiguruikai.combinatoricskt
9 |
10 | import org.junit.jupiter.api.Assertions.assertEquals
11 | import org.junit.jupiter.api.Assertions.assertTrue
12 | import org.junit.jupiter.api.Test
13 |
14 | internal class PowerSetGeneratorTest {
15 |
16 | private val emptyList = emptyList()
17 | private val emptyArray = emptyArray()
18 |
19 | @Test
20 | fun test_powerset_empty() {
21 | assertEquals("[[]]", emptyList.powerset().toList().toString())
22 | assertEquals("[[]]", emptyArray.powerset().toList().map { it.contentToString() }.toString())
23 | }
24 |
25 | @Test
26 | fun test_powerset() {
27 | val array = arrayOf(0, 1, 2)
28 | val expected = listOf(
29 | "[[]]",
30 | "[[], [0]]",
31 | "[[], [0], [1], [0, 1]]",
32 | "[[], [0], [1], [0, 1], [2], [0, 2], [1, 2], [0, 1, 2]]")
33 | expected.forEachIndexed { index, it ->
34 | assertEquals(it, array.take(index).powerset().toList().toString())
35 | assertEquals(it, array.copyOf(index).powerset().map { it.toList() }.toList().toString())
36 | }
37 |
38 | fun Iterable.powerset1(): CombinatorialSequence> {
39 | val pool = toList()
40 | val totalSize = 2.toBigInteger().pow(pool.size)
41 |
42 | val iterator = iterator {
43 | val bitLength = totalSize.bitLength()
44 | val max = totalSize.intValueExact() - 1
45 | var i = 0
46 | while (true) {
47 | val acc = ArrayList(Integer.bitCount(i))
48 | for (index in 0..bitLength) {
49 | if ((1 shl index) and i != 0) {
50 | acc += pool[index]
51 | }
52 | }
53 | yield(acc)
54 |
55 | if (i >= max) {
56 | break
57 | }
58 | i++
59 | }
60 | }
61 |
62 | return CombinatorialSequence(totalSize, iterator)
63 | }
64 |
65 | // ソート順かつサイズ順で生成
66 | fun Iterable.powerset2(): Sequence> {
67 | val pool = toList()
68 |
69 | var sequence: Sequence> = emptySequence()
70 | for (r in 0..pool.size) {
71 | sequence += pool.combinations(r)
72 | }
73 |
74 | return sequence
75 | }
76 |
77 | fun Iterable.powerset3(): Sequence> {
78 |
79 | tailrec fun powerset3Tailrec(left: Collection, acc: Sequence>): Sequence> = when {
80 | left.isEmpty() -> acc
81 | else -> powerset3Tailrec(left.drop(1), acc + acc.map { it + left.first() })
82 | }
83 |
84 | return powerset3Tailrec(toList(), sequenceOf(listOf()))
85 | }
86 |
87 | val comparator = ListComparator()
88 |
89 | val range = 'a'..'f'
90 | for (i in 0..7) {
91 | val v = range.take(i)
92 | val ps = v.powerset()
93 | val psList = ps.toList()
94 | val ps1 = v.powerset1()
95 | val ps2 = v.powerset2()
96 | val ps3 = v.powerset3()
97 |
98 | val size = 2.toBigInteger().pow(v.size)
99 |
100 | assertTrue(ps.totalSize == size && size == ps1.totalSize)
101 | assertEquals(psList, ps1.toList())
102 | assertEquals(psList.sortedWith(comparator).sortedBy { it.size }, ps2.toList())
103 | assertEquals(psList, ps3.toList())
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/test/kotlin/com/github/shiguruikai/combinatoricskt/internal/ExtensionsKtTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 shiguruikai
3 | *
4 | * Licensed under the MIT license
5 | * https://github.com/shiguruikai/combinatoricskt/blob/master/LICENSE
6 | */
7 |
8 | package com.github.shiguruikai.combinatoricskt.internal
9 |
10 | import org.junit.jupiter.api.Assertions.assertArrayEquals
11 | import org.junit.jupiter.api.Assertions.assertIterableEquals
12 | import org.junit.jupiter.api.Assertions.assertTrue
13 | import org.junit.jupiter.api.Test
14 |
15 | internal class ExtensionsKtTest {
16 |
17 | @Test
18 | fun test_List_times() {
19 | val list = listOf(1, 2, 3)
20 | assertTrue(list === list * 1)
21 | assertIterableEquals(emptyList(), list * 0)
22 | assertIterableEquals(list, list * 1)
23 | assertIterableEquals(list + list, list * 2)
24 | assertIterableEquals(list + list + list, list * 3)
25 | }
26 |
27 | @Test
28 | fun test_IntArray_times() {
29 | val ints = intArrayOf(1, 2, 3)
30 | assertTrue(ints === ints * 1)
31 | assertArrayEquals(intArrayOf(), ints * 0)
32 | assertArrayEquals(ints, ints * 1)
33 | assertArrayEquals(ints + ints, ints * 2)
34 | assertArrayEquals(ints + ints + ints, ints * 3)
35 | }
36 |
37 | @Test
38 | fun test_IntArray_mapToList() {
39 | assertIterableEquals(listOf(1, 2, 3), intArrayOf(1, 2, 3, 4, 5).mapToList(3) { it })
40 | assertIterableEquals(listOf("1", "2", "3"), intArrayOf(1, 2, 3).mapToList { it.toString() })
41 | }
42 |
43 | @Test
44 | fun test_IntArray_mapToArray() {
45 | assertArrayEquals(arrayOf(1, 2, 3), intArrayOf(1, 2, 3, 4, 5).mapToArray(3) { it })
46 | assertArrayEquals(arrayOf("1", "2", "3"), intArrayOf(1, 2, 3).mapToArray { it.toString() })
47 | }
48 |
49 | @Test
50 | fun test_Array_mapToArray() {
51 | assertArrayEquals(arrayOf(1, 2, 3), arrayOf(1, 2, 3, 4, 5).mapToArray(3) { it })
52 | assertArrayEquals(arrayOf("1", "2", "3"), arrayOf(1, 2, 3).mapToArray { it.toString() })
53 | }
54 |
55 | @Test
56 | fun test_IntArray_swap() {
57 | assertArrayEquals(intArrayOf(1, 0), intArrayOf(0, 1).apply { swap(0, 1) })
58 | assertArrayEquals(intArrayOf(1, 3, 2), intArrayOf(1, 2, 3).apply { swap(2, 1) })
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/test/kotlin/com/github/shiguruikai/combinatoricskt/internal/MathTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 shiguruikai
3 | *
4 | * Licensed under the MIT license
5 | * https://github.com/shiguruikai/combinatoricskt/blob/master/LICENSE
6 | */
7 |
8 | package com.github.shiguruikai.combinatoricskt.internal
9 |
10 | import org.junit.jupiter.api.Assertions.assertDoesNotThrow
11 | import org.junit.jupiter.api.Assertions.assertEquals
12 | import org.junit.jupiter.api.Test
13 | import org.junit.jupiter.api.assertThrows
14 | import java.math.BigInteger
15 |
16 | internal class MathTest {
17 |
18 | @Test
19 | fun test_factorial() {
20 | for (n in -2..2) {
21 | if (n < 0) {
22 | assertThrows