Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
* Added a new `automata-serialization-mata` module for serializing (explicit) NFAs in the `.mata` format as used by the [mata library](https://github.com/VeriFIT/mata).
* `automata-modelchecking-m3c` now supports ARM-based macOS systems.
* `automata-modelchecking-m3c` can now be included in jlink images.
* Added `KWay{State,Transition}CoverTestsIterator`s to `automata-util` as a new means for conformance testing.
* Added `CollectionUtil#allCombintationsIterator` and `CollectionUtil#allPermutationsIterator` for computing k-combinations and k-permutations.

### Changed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,17 @@ public static void heapsort(int[] arr, int[] keys) {
}
}

private static void swap(int[] arr, int i, int j) {
/**
* Swaps the two elements at the given position in-place.
*
* @param arr
* the array
* @param i
* the first index
* @param j
* the second index
*/
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,84 +15,85 @@
*/
package net.automatalib.common.util.collection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.AbstractList;
import java.util.Collection;
import java.util.List;
import java.util.NoSuchElementException;

/**
* An iterator that iterates over the cartesian product of its given source domains. Each intermediate combination of
* elements is computed lazily.
* <p>
* <b>Note:</b> Subsequent calls to the {@link #next()} method return a reference to the same list, and only update the
* contents of the list. If you plan to reuse intermediate results, you'll need to explicitly copy them.
* Iterator for computing all k-combinations of a given collection. Implementation is based on <a
* href="https://hmkcode.com/calculate-find-all-possible-combinations-of-an-array-using-java/">https://hmkcode.com/calculate-find-all-possible-combinations-of-an-array-using-java/</a>.
*
* @param <T>
* type of elements
* element type
*/
final class AllCombinationsIterator<T> implements Iterator<List<T>> {
final class AllCombinationsIterator<T> extends AbstractSimplifiedIterator<List<T>> {

private final Iterable<? extends T>[] iterables;
private final Iterator<? extends T>[] iterators;
private final List<T> current;
private boolean first = true;
private boolean empty;
private final int[] pointers;
private final int n;
private final int k;

@SuppressWarnings("unchecked")
@SafeVarargs
AllCombinationsIterator(Iterable<? extends T>... iterables) {
this.iterables = iterables;
this.iterators = new Iterator[iterables.length];
this.current = new ArrayList<>(iterables.length);
for (int i = 0; i < iterators.length; i++) {
Iterator<? extends T> it = iterables[i].iterator();
if (!it.hasNext()) {
empty = true;
break;
}
this.iterators[i] = it;
this.current.add(it.next());
}
}
private int r; // index for combination array
private int i; // index for elements array

@Override
public boolean hasNext() {
if (empty) {
return false;
AllCombinationsIterator(Collection<? extends T> elements, int k) {
if (k < 0 || k > elements.size()) {
throw new IllegalArgumentException("k is not within its expected bounds of 0 and " + elements.size());
}

for (Iterator<? extends T> it : iterators) {
if (it.hasNext()) {
return true;
}
}
this.n = elements.size();
this.k = k;

// we only read this array
@SuppressWarnings({"unchecked", "PMD.ClassCastExceptionWithToArray"})
final T[] pool = (T[]) elements.toArray();
this.pointers = new int[k];

return first;
// always use same instance which is modified in-place
super.nextValue = new MappedList<>(pool, this.pointers, this.k);
}

@Override
public List<T> next() {
if (empty) {
throw new NoSuchElementException();
} else if (first) {
first = false;
return current;
protected boolean calculateNext() {
while (r >= 0) {
if (i <= n + r - k) { // forward step if i < (n + (r-K))
pointers[r] = i;
if (r == k - 1) { // if combination array is full print and increment i;
i++;
return true;
} else { // if combination is not full yet, select next element
i = pointers[r] + 1;
r++;
}
} else { // backward step
r--;
if (r >= 0) {
i = pointers[r] + 1;
}
}
}
return false;
}

for (int i = 0; i < iterators.length; i++) {
Iterator<? extends T> it = iterators[i];
static class MappedList<T> extends AbstractList<T> {

if (iterators[i].hasNext()) {
current.set(i, it.next());
return current;
}
private final T[] pool;
private final int[] pointers;
private final int k;

it = iterables[i].iterator();
iterators[i] = it;
current.set(i, it.next());
MappedList(T[] pool, int[] pointers, int k) {
this.pool = pool;
this.pointers = pointers;
this.k = k;
}

throw new NoSuchElementException();
}
@Override
public T get(int index) {
return pool[pointers[index]];
}

@Override
public int size() {
return k;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/* Copyright (C) 2013-2025 TU Dortmund University
* This file is part of AutomataLib <https://automatalib.net>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.automatalib.common.util.collection;

import java.util.Collection;
import java.util.List;

import net.automatalib.common.util.array.ArrayUtil;
import net.automatalib.common.util.collection.AllCombinationsIterator.MappedList;

/**
* Iterator for computing all k-permutations of a given collection. Implementation is based on <a
* href="https://docs.python.org/3/library/itertools.html#itertools.permutations">itertools.permutations</a>.
*
* @param <T>
* element type
*/
final class AllPermutationsIterator<T> extends AbstractSimplifiedIterator<List<T>> {

private final int n;
private final int[] indices;
private final int[] cycles;
private final int r;

private boolean initial;

AllPermutationsIterator(Collection<? extends T> elements, int k) {
if (k < 0 || k > elements.size()) {
throw new IllegalArgumentException("k is not within its expected bounds of 0 and " + elements.size());
}

this.n = elements.size();
this.r = k;

// we only read this array
@SuppressWarnings({"unchecked", "PMD.ClassCastExceptionWithToArray"})
final T[] pool = (T[]) elements.toArray();
this.indices = new int[this.n];
this.cycles = new int[k];

for (int j = 0; j < n; j++) {
indices[j] = j;
if (j < k) {
cycles[j] = n - j;
}
}

// always use same instance which is modified in-place
super.nextValue = new MappedList<>(pool, this.indices, k);
}

@Override
protected boolean calculateNext() {
if (!initial) { // the first element is the unaltered one
initial = true;
return true;
}

for (int i = r - 1; i >= 0; i--) {
cycles[i] -= 1;
if (cycles[i] == 0) {
int old = indices[i];
System.arraycopy(indices, i + 1, indices, i, n - i - 1);
indices[n - 1] = old;
cycles[i] = n - i;
} else {
int j = cycles[i];
ArrayUtil.swap(indices, i, n - j);
return true;
}
}
return false;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/* Copyright (C) 2013-2025 TU Dortmund University
* This file is part of AutomataLib <https://automatalib.net>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.automatalib.common.util.collection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

/**
* An iterator that iterates over the cartesian product of its given source domains. Each intermediate combination of
* elements is computed lazily.
* <p>
* <b>Note:</b> Subsequent calls to the {@link #next()} method return a reference to the same list, and only update the
* contents of the list. If you plan to reuse intermediate results, you'll need to explicitly copy them.
*
* @param <T>
* type of elements
*/
final class CartesianProductIterator<T> implements Iterator<List<T>> {

private final Iterable<? extends T>[] iterables;
private final Iterator<? extends T>[] iterators;
private final List<T> current;
private boolean first;
private boolean empty;

@SuppressWarnings("unchecked")
@SafeVarargs
CartesianProductIterator(Iterable<? extends T>... iterables) {
this.iterables = iterables;
this.iterators = new Iterator[iterables.length];
this.current = new ArrayList<>(iterables.length);
this.first = true;

for (int i = 0; i < iterators.length; i++) {
Iterator<? extends T> it = iterables[i].iterator();
if (!it.hasNext()) {
empty = true;
break;
}
this.iterators[i] = it;
this.current.add(it.next());
}
}

@Override
public boolean hasNext() {
if (empty) {
return false;
}

for (Iterator<? extends T> it : iterators) {
if (it.hasNext()) {
return true;
}
}

return first;
}

@Override
public List<T> next() {
if (empty) {
throw new NoSuchElementException();
} else if (first) {
first = false;
return current;
}

for (int i = 0; i < iterators.length; i++) {
Iterator<? extends T> it = iterators[i];

if (iterators[i].hasNext()) {
current.set(i, it.next());
return current;
}

it = iterables[i].iterator();
iterators[i] = it;
current.set(i, it.next());
}

throw new NoSuchElementException();
}

}
Loading