From 896059e8f814c4f47ca766557b1df512f86a889e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Skj=C3=B8lberg?= Date: Mon, 16 Sep 2024 01:17:10 +0200 Subject: [PATCH] Various improvements --- .../skjolber/packing/api/StackableItem.java | 3 + .../packing/api/StackableItemGroup.java | 15 +- ...actStackableItemGroupIteratorBuilder.java} | 4 +- ...eItemGroupPermutationRotationIterator.java | 118 ++++ ...AbstractStackableItemIteratorBuilder.java} | 4 +- ...kableItemPermutationRotationIterator.java} | 27 +- ...ItemGroupPermutationRotationIterator.java} | 161 ++---- ...kableItemPermutationRotationIterator.java} | 39 +- .../iterator/IndexedStackableItem.java | 12 +- ....java => MutableIndexedStackableItem.java} | 4 +- ...kableItemPermutationRotationIterator.java} | 35 +- ...rallelPermutationRotationIteratorList.java | 1 + ...eItemGroupPermutationRotationIterator.java | 502 ++++++++++++++++++ ...mGroupPermutationRotationIteratorList.java | 465 ++++++++++++++++ ...kableItemPermutationRotationIterator.java} | 2 +- ...eItemPermutationRotationIteratorTest.java} | 44 +- ...GroupPermutationRotationIteratorTest.java} | 21 +- ...leItemPermutationRotationIteratorTest.java | 12 - ...adablePermutationRotationIteratorTest.java | 14 +- ...upPermutationRotationIteratorListTest.java | 307 +++++++++++ ...mGroupPermutationRotationIteratorTest.java | 54 ++ ...leItemPermutationRotationIteratorTest.java | 12 + 22 files changed, 1616 insertions(+), 240 deletions(-) rename core/src/main/java/com/github/skjolber/packing/iterator/{AbstractLoadableItemGroupIteratorBuilder.java => AbstractStackableItemGroupIteratorBuilder.java} (93%) create mode 100644 core/src/main/java/com/github/skjolber/packing/iterator/AbstractStackableItemGroupPermutationRotationIterator.java rename core/src/main/java/com/github/skjolber/packing/iterator/{AbstractLoadableIteratorBuilder.java => AbstractStackableItemIteratorBuilder.java} (92%) rename core/src/main/java/com/github/skjolber/packing/iterator/{AbstractLoadablePermutationRotationIterator.java => AbstractStackableItemPermutationRotationIterator.java} (67%) rename core/src/main/java/com/github/skjolber/packing/iterator/{DefaultLoadableItemGroupPermutationRotationIterator.java => DefaultStackableItemGroupPermutationRotationIterator.java} (65%) rename core/src/main/java/com/github/skjolber/packing/iterator/{DefaultLoadableItemPermutationRotationIterator.java => DefaultStackableItemPermutationRotationIterator.java} (83%) rename core/src/main/java/com/github/skjolber/packing/iterator/{MutableLoadableItem.java => MutableIndexedStackableItem.java} (66%) rename core/src/main/java/com/github/skjolber/packing/iterator/{MutableLoadableItemPermutationRotationIterator.java => MutableIndexedStackableItemPermutationRotationIterator.java} (84%) create mode 100644 core/src/main/java/com/github/skjolber/packing/iterator/ParallelStackableItemGroupPermutationRotationIterator.java create mode 100644 core/src/main/java/com/github/skjolber/packing/iterator/ParallelStackableItemGroupPermutationRotationIteratorList.java rename core/src/main/java/com/github/skjolber/packing/iterator/{LoadableItemPermutationRotationIterator.java => StackableItemPermutationRotationIterator.java} (97%) rename core/src/test/java/com/github/skjolber/packing/iterator/{AbstractLoadablePermutationRotationIteratorTest.java => AbstractStackableItemPermutationRotationIteratorTest.java} (91%) rename core/src/test/java/com/github/skjolber/packing/iterator/{DefaultLoadableItemGroupPermutationRotationIteratorTest.java => DefaultStackableItemGroupPermutationRotationIteratorTest.java} (93%) delete mode 100644 core/src/test/java/com/github/skjolber/packing/iterator/LoadableItemPermutationRotationIteratorTest.java create mode 100644 core/src/test/java/com/github/skjolber/packing/iterator/ParallelStackableItemGroupPermutationRotationIteratorListTest.java create mode 100644 core/src/test/java/com/github/skjolber/packing/iterator/ParallelStackableItemGroupPermutationRotationIteratorTest.java create mode 100644 core/src/test/java/com/github/skjolber/packing/iterator/StackableItemPermutationRotationIteratorTest.java diff --git a/api/src/main/java/com/github/skjolber/packing/api/StackableItem.java b/api/src/main/java/com/github/skjolber/packing/api/StackableItem.java index abd0226e..aff03dcc 100644 --- a/api/src/main/java/com/github/skjolber/packing/api/StackableItem.java +++ b/api/src/main/java/com/github/skjolber/packing/api/StackableItem.java @@ -49,5 +49,8 @@ public void decrement(int value) { this.count = this.count - value; } + public StackableItem clone() { + return new StackableItem(stackable, count); + } } diff --git a/api/src/main/java/com/github/skjolber/packing/api/StackableItemGroup.java b/api/src/main/java/com/github/skjolber/packing/api/StackableItemGroup.java index 3bd78f63..fcab9dba 100644 --- a/api/src/main/java/com/github/skjolber/packing/api/StackableItemGroup.java +++ b/api/src/main/java/com/github/skjolber/packing/api/StackableItemGroup.java @@ -1,5 +1,6 @@ package com.github.skjolber.packing.api; +import java.util.ArrayList; import java.util.List; /** @@ -44,7 +45,8 @@ public StackableItem get(int i) { return items.get(i); } - public int loadableItemsCount() { + + public int stackableItemsCount() { int count = 0; for (StackableItem loadableItem : items) { count += loadableItem.getCount(); @@ -72,4 +74,15 @@ public void removeEmpty() { } } } + + public StackableItemGroup clone() { + List items = new ArrayList<>(); + + for (StackableItem stackableItem : this.items) { + items.add(stackableItem.clone()); + } + + return new StackableItemGroup(id, items); + } + } \ No newline at end of file diff --git a/core/src/main/java/com/github/skjolber/packing/iterator/AbstractLoadableItemGroupIteratorBuilder.java b/core/src/main/java/com/github/skjolber/packing/iterator/AbstractStackableItemGroupIteratorBuilder.java similarity index 93% rename from core/src/main/java/com/github/skjolber/packing/iterator/AbstractLoadableItemGroupIteratorBuilder.java rename to core/src/main/java/com/github/skjolber/packing/iterator/AbstractStackableItemGroupIteratorBuilder.java index bfffa8db..b8b94dc0 100644 --- a/core/src/main/java/com/github/skjolber/packing/iterator/AbstractLoadableItemGroupIteratorBuilder.java +++ b/core/src/main/java/com/github/skjolber/packing/iterator/AbstractStackableItemGroupIteratorBuilder.java @@ -18,7 +18,7 @@ * "https://www.sitepoint.com/self-types-with-javas-generics/">https://www.sitepoint.com/self-types-with-javas-generics/ */ -public abstract class AbstractLoadableItemGroupIteratorBuilder> { +public abstract class AbstractStackableItemGroupIteratorBuilder> { protected int maxLoadWeight = -1; protected Predicate filter; @@ -103,6 +103,6 @@ protected List toMatrix() { return results; } - public abstract LoadableItemPermutationRotationIterator build(); + public abstract StackableItemPermutationRotationIterator build(); } diff --git a/core/src/main/java/com/github/skjolber/packing/iterator/AbstractStackableItemGroupPermutationRotationIterator.java b/core/src/main/java/com/github/skjolber/packing/iterator/AbstractStackableItemGroupPermutationRotationIterator.java new file mode 100644 index 00000000..b896f34d --- /dev/null +++ b/core/src/main/java/com/github/skjolber/packing/iterator/AbstractStackableItemGroupPermutationRotationIterator.java @@ -0,0 +1,118 @@ +package com.github.skjolber.packing.iterator; + +import java.util.List; + +import com.github.skjolber.packing.api.StackableItem; +import com.github.skjolber.packing.api.StackableItemGroup; + +public abstract class AbstractStackableItemGroupPermutationRotationIterator extends AbstractStackableItemPermutationRotationIterator { + + protected List groups; + + public AbstractStackableItemGroupPermutationRotationIterator(IndexedStackableItem[] matrix, List groups) { + super(matrix); + this.groups = groups; + } + + /** + * Return number of permutations for boxes which fit within this container. + * + * @return permutation count + */ + + public long countPermutations() { + // reduce permutations for boxes which are duplicated + + // could be further bounded by looking at how many boxes (i.e. n x the smallest) which actually + // fit within the container volume + long n = 1; + + for (StackableItemGroup loadableItemGroup : groups) { + + List items = loadableItemGroup.getItems(); + + int count = loadableItemGroup.stackableItemsCount(); + + int maxCount = 0; + for (StackableItem value : items) { + if(value != null) { + if(maxCount < value.getCount()) { + maxCount = value.getCount(); + } + } + } + + if(maxCount > 1) { + int[] factors = new int[maxCount]; + for (StackableItem value : items) { + if(value != null) { + for (int k = 0; k < value.getCount(); k++) { + factors[k]++; + } + } + } + + for (long i = 0; i < count; i++) { + if(Long.MAX_VALUE / (i + 1) <= n) { + return -1L; + } + + n = n * (i + 1); + + for (int k = 1; k < maxCount; k++) { + while (factors[k] > 0 && n % (k + 1) == 0) { + n = n / (k + 1); + + factors[k]--; + } + } + } + + for (int k = 1; k < maxCount; k++) { + while (factors[k] > 0) { + n = n / (k + 1); + + factors[k]--; + } + } + } else { + for (long i = 0; i < count; i++) { + if(Long.MAX_VALUE / (i + 1) <= n) { + return -1L; + } + n = n * (i + 1); + } + } + } + return n; + } + + + @Override + public void removePermutations(List removed) { + + for (Integer i : removed) { + IndexedStackableItem loadableItem = stackableItems[i]; + + loadableItem.decrement(); + + if(loadableItem.isEmpty()) { + stackableItems[i] = null; + } + } + + // go through all groups and clean up + for(int i = 0; i < groups.size(); i++) { + StackableItemGroup group = groups.get(i); + + group.removeEmpty(); + if(group.isEmpty()) { + groups.remove(i); + i--; + } + } + } + + + +} diff --git a/core/src/main/java/com/github/skjolber/packing/iterator/AbstractLoadableIteratorBuilder.java b/core/src/main/java/com/github/skjolber/packing/iterator/AbstractStackableItemIteratorBuilder.java similarity index 92% rename from core/src/main/java/com/github/skjolber/packing/iterator/AbstractLoadableIteratorBuilder.java rename to core/src/main/java/com/github/skjolber/packing/iterator/AbstractStackableItemIteratorBuilder.java index 4eb8433e..1f7021ee 100644 --- a/core/src/main/java/com/github/skjolber/packing/iterator/AbstractLoadableIteratorBuilder.java +++ b/core/src/main/java/com/github/skjolber/packing/iterator/AbstractStackableItemIteratorBuilder.java @@ -17,7 +17,7 @@ * "https://www.sitepoint.com/self-types-with-javas-generics/">https://www.sitepoint.com/self-types-with-javas-generics/ */ -public abstract class AbstractLoadableIteratorBuilder> { +public abstract class AbstractStackableItemIteratorBuilder> { protected int maxLoadWeight = -1; protected Predicate filter; @@ -89,6 +89,6 @@ protected IndexedStackableItem[] toMatrix() { return results; } - public abstract LoadableItemPermutationRotationIterator build(); + public abstract StackableItemPermutationRotationIterator build(); } diff --git a/core/src/main/java/com/github/skjolber/packing/iterator/AbstractLoadablePermutationRotationIterator.java b/core/src/main/java/com/github/skjolber/packing/iterator/AbstractStackableItemPermutationRotationIterator.java similarity index 67% rename from core/src/main/java/com/github/skjolber/packing/iterator/AbstractLoadablePermutationRotationIterator.java rename to core/src/main/java/com/github/skjolber/packing/iterator/AbstractStackableItemPermutationRotationIterator.java index 93d3d95a..0cd464b3 100644 --- a/core/src/main/java/com/github/skjolber/packing/iterator/AbstractLoadablePermutationRotationIterator.java +++ b/core/src/main/java/com/github/skjolber/packing/iterator/AbstractStackableItemPermutationRotationIterator.java @@ -6,17 +6,17 @@ import com.github.skjolber.packing.api.StackValue; import com.github.skjolber.packing.api.StackableItem; -public abstract class AbstractLoadablePermutationRotationIterator implements LoadableItemPermutationRotationIterator { +public abstract class AbstractStackableItemPermutationRotationIterator implements StackableItemPermutationRotationIterator { - protected final IndexedStackableItem[] loadableItems; // by index + protected final IndexedStackableItem[] stackableItems; // by index protected int[] reset; - public AbstractLoadablePermutationRotationIterator(IndexedStackableItem[] matrix) { - this.loadableItems = matrix; + public AbstractStackableItemPermutationRotationIterator(IndexedStackableItem[] matrix) { + this.stackableItems = matrix; } public IndexedStackableItem[] getMatrix() { - return loadableItems; + return stackableItems; } /** @@ -26,7 +26,7 @@ public IndexedStackableItem[] getMatrix() { */ public int boxItemLength() { - return loadableItems.length; + return stackableItems.length; } public long getMinStackableArea(int offset) { @@ -62,7 +62,7 @@ public List get(PermutationRotationState state, int length) { List results = new ArrayList(length); for (int i = 0; i < length; i++) { - results.add(loadableItems[permutations[i]].getStackable().getStackValue(rotations[i])); + results.add(stackableItems[permutations[i]].getStackable().getStackValue(rotations[i])); } return results; } @@ -70,4 +70,17 @@ public List get(PermutationRotationState state, int length) { public abstract int length(); public abstract StackValue getStackValue(int index); + + + protected int[] getFrequencies() { + int[] frequencies = new int[stackableItems.length]; + + for (int i = 0; i < stackableItems.length; i++) { + if(stackableItems[i] != null) { + frequencies[i] = stackableItems[i].getCount(); + } + } + return frequencies; + } + } diff --git a/core/src/main/java/com/github/skjolber/packing/iterator/DefaultLoadableItemGroupPermutationRotationIterator.java b/core/src/main/java/com/github/skjolber/packing/iterator/DefaultStackableItemGroupPermutationRotationIterator.java similarity index 65% rename from core/src/main/java/com/github/skjolber/packing/iterator/DefaultLoadableItemGroupPermutationRotationIterator.java rename to core/src/main/java/com/github/skjolber/packing/iterator/DefaultStackableItemGroupPermutationRotationIterator.java index 51698aa6..f0dbdefb 100644 --- a/core/src/main/java/com/github/skjolber/packing/iterator/DefaultLoadableItemGroupPermutationRotationIterator.java +++ b/core/src/main/java/com/github/skjolber/packing/iterator/DefaultStackableItemGroupPermutationRotationIterator.java @@ -8,15 +8,15 @@ import com.github.skjolber.packing.api.StackableItem; import com.github.skjolber.packing.api.StackableItemGroup; -public class DefaultLoadableItemGroupPermutationRotationIterator extends AbstractLoadablePermutationRotationIterator { +public class DefaultStackableItemGroupPermutationRotationIterator extends AbstractStackableItemGroupPermutationRotationIterator { public static Builder newBuilder() { return new Builder(); } - public static class Builder extends AbstractLoadableItemGroupIteratorBuilder { + public static class Builder extends AbstractStackableItemGroupIteratorBuilder { - public DefaultLoadableItemGroupPermutationRotationIterator build() { + public DefaultStackableItemGroupPermutationRotationIterator build() { if(maxLoadWeight == -1) { throw new IllegalStateException(); } @@ -31,7 +31,7 @@ public DefaultLoadableItemGroupPermutationRotationIterator build() { matrix.addAll(loadableItemGroup.getItems()); } - return new DefaultLoadableItemGroupPermutationRotationIterator(groups, matrix.toArray(new IndexedStackableItem[matrix.size()])); + return new DefaultStackableItemGroupPermutationRotationIterator(groups, matrix.toArray(new IndexedStackableItem[matrix.size()])); } } @@ -44,12 +44,8 @@ public DefaultLoadableItemGroupPermutationRotationIterator build() { // minimum volume from index i and above protected long[] minStackableVolume; - protected List groups; - - public DefaultLoadableItemGroupPermutationRotationIterator(List groups, IndexedStackableItem[] matrix) { - super(matrix); - - this.groups = groups; + public DefaultStackableItemGroupPermutationRotationIterator(List groups, IndexedStackableItem[] matrix) { + super(matrix, groups); int count = 0; @@ -65,18 +61,18 @@ public DefaultLoadableItemGroupPermutationRotationIterator(List= offset; i--) { - long volume = loadableItems[permutations[i]].getStackable().getStackValue(rotations[i]).getVolume(); + long volume = stackableItems[permutations[i]].getStackable().getStackValue(rotations[i]).getVolume(); if(volume < minStackableVolume[i + 1]) { minStackableVolume[i] = volume; @@ -152,27 +148,8 @@ protected long[] getMinStackableVolume() { public void removePermutations(List removed) { - for (Integer i : removed) { - IndexedStackableItem loadableItem = loadableItems[i]; - - loadableItem.decrement(); - - if(loadableItem.isEmpty()) { - loadableItems[i] = null; - } - } - - // go through all groups and clean up - for(int i = 0; i < groups.size(); i++) { - StackableItemGroup group = groups.get(i); - - group.removeEmpty(); - if(group.isEmpty()) { - groups.remove(i); - i--; - } - } - + super.removePermutations(removed); + initiatePermutation(rotations.length - removed.size()); } @@ -185,7 +162,7 @@ public int nextRotation() { public int nextRotation(int maxIndex) { // next rotation for (int i = maxIndex; i >= 0; i--) { - if(rotations[i] < loadableItems[permutations[i]].getStackable().getStackValues().length - 1) { + if(rotations[i] < stackableItems[permutations[i]].getStackable().getStackValues().length - 1) { rotations[i]++; System.arraycopy(reset, 0, rotations, i + 1, rotations.length - (i + 1)); @@ -209,7 +186,7 @@ protected void resetRotations() { public long countRotations() { long n = 1; for (int i = 0; i < permutations.length; i++) { - IndexedStackableItem value = loadableItems[permutations[i]]; + IndexedStackableItem value = stackableItems[permutations[i]]; if(Long.MAX_VALUE / value.getStackable().getStackValues().length <= n) { return -1L; } @@ -219,94 +196,20 @@ public long countRotations() { return n; } - /** - * Return number of permutations for boxes which fit within this container. - * - * @return permutation count - */ - - public long countPermutations() { - // reduce permutations for boxes which are duplicated - - // could be further bounded by looking at how many boxes (i.e. n x the smallest) which actually - // fit within the container volume - long n = 1; - - for (StackableItemGroup loadableItemGroup : groups) { - - List items = loadableItemGroup.getItems(); - - int count = loadableItemGroup.loadableItemsCount(); - - int maxCount = 0; - for (StackableItem value : items) { - if(value != null) { - if(maxCount < value.getCount()) { - maxCount = value.getCount(); - } - } - } - - if(maxCount > 1) { - int[] factors = new int[maxCount]; - for (StackableItem value : items) { - if(value != null) { - for (int k = 0; k < value.getCount(); k++) { - factors[k]++; - } - } - } - - for (long i = 0; i < count; i++) { - if(Long.MAX_VALUE / (i + 1) <= n) { - return -1L; - } - - n = n * (i + 1); - - for (int k = 1; k < maxCount; k++) { - while (factors[k] > 0 && n % (k + 1) == 0) { - n = n / (k + 1); - - factors[k]--; - } - } - } - - for (int k = 1; k < maxCount; k++) { - while (factors[k] > 0) { - n = n / (k + 1); - - factors[k]--; - } - } - } else { - for (long i = 0; i < count; i++) { - if(Long.MAX_VALUE / (i + 1) <= n) { - return -1L; - } - n = n * (i + 1); - } - } - } - return n; - } - public int nextPermutation(int maxIndex) { - + int[] permutations = this.permutations; + int limit = permutations.length; for(int g = groups.size() - 1; g >= 0; g--) { StackableItemGroup loadableItemGroup = groups.get(g); // Find longest non-increasing suffix - int startIndex = limit - loadableItemGroup.loadableItemsCount(); + int startIndex = limit - loadableItemGroup.stackableItemsCount(); if(startIndex <= maxIndex && maxIndex < limit) { while (maxIndex >= startIndex) { - int[] permutations = this.permutations; - int current = permutations[maxIndex]; // find the lexicographically next item to the right of the max index @@ -355,26 +258,23 @@ public int nextPermutation(int maxIndex) { // skip to next group limit = startIndex; } - return -1; } - public int nextPermutation() { resetRotations(); + int[] permutations = this.permutations; + int endIndex = permutations.length - 1; for(int g = groups.size() - 1; g >= 0; g--) { - StackableItemGroup loadableItemGroup = groups.get(g); - int[] permutations = this.permutations; - // Find longest non-increasing suffix int i = endIndex; - int startIndex = endIndex - loadableItemGroup.loadableItemsCount() + 1; + int startIndex = endIndex - loadableItemGroup.stackableItemsCount() + 1; while (i > startIndex && permutations[i - 1] >= permutations[i]) i--; @@ -425,8 +325,6 @@ public int nextPermutation() { i++; j--; } - - g++; calculateMinStackableVolume(head); @@ -446,10 +344,15 @@ public PermutationRotationState getState() { } public IndexedStackableItem getPermutation(int index) { - return loadableItems[permutations[index]]; + return stackableItems[permutations[index]]; } protected int[] getRotations() { return rotations; } + + public List getGroups() { + return groups; + } + } diff --git a/core/src/main/java/com/github/skjolber/packing/iterator/DefaultLoadableItemPermutationRotationIterator.java b/core/src/main/java/com/github/skjolber/packing/iterator/DefaultStackableItemPermutationRotationIterator.java similarity index 83% rename from core/src/main/java/com/github/skjolber/packing/iterator/DefaultLoadableItemPermutationRotationIterator.java rename to core/src/main/java/com/github/skjolber/packing/iterator/DefaultStackableItemPermutationRotationIterator.java index 86a4da6e..83007c20 100644 --- a/core/src/main/java/com/github/skjolber/packing/iterator/DefaultLoadableItemPermutationRotationIterator.java +++ b/core/src/main/java/com/github/skjolber/packing/iterator/DefaultStackableItemPermutationRotationIterator.java @@ -5,15 +5,15 @@ import com.github.skjolber.packing.api.StackValue; -public class DefaultLoadableItemPermutationRotationIterator extends AbstractLoadablePermutationRotationIterator { +public class DefaultStackableItemPermutationRotationIterator extends AbstractStackableItemPermutationRotationIterator { public static Builder newBuilder() { return new Builder(); } - public static class Builder extends AbstractLoadableIteratorBuilder { + public static class Builder extends AbstractStackableItemIteratorBuilder { - public DefaultLoadableItemPermutationRotationIterator build() { + public DefaultStackableItemPermutationRotationIterator build() { if(maxLoadWeight == -1) { throw new IllegalStateException(); } @@ -23,9 +23,8 @@ public DefaultLoadableItemPermutationRotationIterator build() { IndexedStackableItem[] matrix = toMatrix(); - return new DefaultLoadableItemPermutationRotationIterator(matrix); + return new DefaultStackableItemPermutationRotationIterator(matrix); } - } protected int[] rotations; // 2^n or 6^n @@ -36,7 +35,7 @@ public DefaultLoadableItemPermutationRotationIterator build() { // minimum volume from index i and above protected long[] minStackableVolume; - public DefaultLoadableItemPermutationRotationIterator(IndexedStackableItem[] matrix) { + public DefaultStackableItemPermutationRotationIterator(IndexedStackableItem[] matrix) { super(matrix); int count = 0; @@ -53,18 +52,18 @@ public DefaultLoadableItemPermutationRotationIterator(IndexedStackableItem[] mat } public StackValue getStackValue(int index) { - return loadableItems[permutations[index]].getStackable().getStackValue(rotations[index]); + return stackableItems[permutations[index]].getStackable().getStackValue(rotations[index]); } public void removePermutations(int count) { // discard a number of items from the front for(int i = 0; i < count; i++) { - IndexedStackableItem loadableItem = loadableItems[permutations[i]]; + IndexedStackableItem loadableItem = stackableItems[permutations[i]]; loadableItem.decrement(); if(loadableItem.isEmpty()) { - loadableItems[i] = null; + stackableItems[i] = null; } } @@ -79,8 +78,8 @@ protected void initiatePermutation(int remainingCount) { int[] permutations = new int[rotations.length]; int offset = 0; - for (int j = 0; j < loadableItems.length; j++) { - IndexedStackableItem value = loadableItems[j]; + for (int j = 0; j < stackableItems.length; j++) { + IndexedStackableItem value = stackableItems[j]; if(value != null && !value.isEmpty()) { for (int k = 0; k < value.getCount(); k++) { permutations[offset] = j; @@ -97,12 +96,12 @@ protected void initiatePermutation(int remainingCount) { } protected void calculateMinStackableVolume(int offset) { - StackValue last = loadableItems[permutations[permutations.length - 1]].getStackable().getStackValue(rotations[permutations.length - 1]); + StackValue last = stackableItems[permutations[permutations.length - 1]].getStackable().getStackValue(rotations[permutations.length - 1]); minStackableVolume[permutations.length - 1] = last.getVolume(); for (int i = permutations.length - 2; i >= offset; i--) { - long volume = loadableItems[permutations[i]].getStackable().getStackValue(rotations[i]).getVolume(); + long volume = stackableItems[permutations[i]].getStackable().getStackValue(rotations[i]).getVolume(); if(volume < minStackableVolume[i + 1]) { minStackableVolume[i] = volume; @@ -128,12 +127,12 @@ protected long[] getMinStackableVolume() { public void removePermutations(List removed) { for (Integer i : removed) { - IndexedStackableItem loadableItem = loadableItems[i]; + IndexedStackableItem loadableItem = stackableItems[i]; loadableItem.decrement(); if(loadableItem.isEmpty()) { - loadableItems[i] = null; + stackableItems[i] = null; } } @@ -149,7 +148,7 @@ public int nextRotation() { public int nextRotation(int maxIndex) { // next rotation for (int i = maxIndex; i >= 0; i--) { - if(rotations[i] < loadableItems[permutations[i]].getStackable().getStackValues().length - 1) { + if(rotations[i] < stackableItems[permutations[i]].getStackable().getStackValues().length - 1) { rotations[i]++; System.arraycopy(reset, 0, rotations, i + 1, rotations.length - (i + 1)); @@ -173,7 +172,7 @@ protected void resetRotations() { public long countRotations() { long n = 1; for (int i = 0; i < permutations.length; i++) { - IndexedStackableItem value = loadableItems[permutations[i]]; + IndexedStackableItem value = stackableItems[permutations[i]]; if(Long.MAX_VALUE / value.getStackable().getStackValues().length <= n) { return -1L; } @@ -196,7 +195,7 @@ public long countPermutations() { // fit within the container volume int maxCount = 0; - for (IndexedStackableItem value : loadableItems) { + for (IndexedStackableItem value : stackableItems) { if(value != null) { if(maxCount < value.getCount()) { maxCount = value.getCount(); @@ -207,7 +206,7 @@ public long countPermutations() { long n = 1; if(maxCount > 1) { int[] factors = new int[maxCount]; - for (IndexedStackableItem value : loadableItems) { + for (IndexedStackableItem value : stackableItems) { if(value != null) { for (int k = 0; k < value.getCount(); k++) { factors[k]++; @@ -345,7 +344,7 @@ public PermutationRotationState getState() { } public IndexedStackableItem getPermutation(int index) { - return loadableItems[permutations[index]]; + return stackableItems[permutations[index]]; } protected int[] getRotations() { diff --git a/core/src/main/java/com/github/skjolber/packing/iterator/IndexedStackableItem.java b/core/src/main/java/com/github/skjolber/packing/iterator/IndexedStackableItem.java index 7b6deadf..1b0d720e 100644 --- a/core/src/main/java/com/github/skjolber/packing/iterator/IndexedStackableItem.java +++ b/core/src/main/java/com/github/skjolber/packing/iterator/IndexedStackableItem.java @@ -2,7 +2,6 @@ import com.github.skjolber.packing.api.Stackable; import com.github.skjolber.packing.api.StackableItem; -import com.github.skjolber.packing.api.packager.BoundedStackable; /** * @@ -12,10 +11,12 @@ public class IndexedStackableItem extends StackableItem { + private static final long serialVersionUID = 1L; + protected int index; - public IndexedStackableItem(Stackable loadable, int count, int index) { - super(loadable, count); + public IndexedStackableItem(Stackable stackable, int count, int index) { + super(stackable, count); this.index = index; } @@ -26,5 +27,10 @@ public boolean isEmpty() { public int getIndex() { return index; } + + @Override + public IndexedStackableItem clone() { + return new IndexedStackableItem(stackable, count, index); + } } diff --git a/core/src/main/java/com/github/skjolber/packing/iterator/MutableLoadableItem.java b/core/src/main/java/com/github/skjolber/packing/iterator/MutableIndexedStackableItem.java similarity index 66% rename from core/src/main/java/com/github/skjolber/packing/iterator/MutableLoadableItem.java rename to core/src/main/java/com/github/skjolber/packing/iterator/MutableIndexedStackableItem.java index 7ae036d4..f25db169 100644 --- a/core/src/main/java/com/github/skjolber/packing/iterator/MutableLoadableItem.java +++ b/core/src/main/java/com/github/skjolber/packing/iterator/MutableIndexedStackableItem.java @@ -1,10 +1,10 @@ package com.github.skjolber.packing.iterator; -public class MutableLoadableItem extends IndexedStackableItem { +public class MutableIndexedStackableItem extends IndexedStackableItem { public final IndexedStackableItem source; - public MutableLoadableItem(IndexedStackableItem loadableItem) { + public MutableIndexedStackableItem(IndexedStackableItem loadableItem) { super(loadableItem.getStackable(), loadableItem.getCount(), loadableItem.getIndex()); this.source = loadableItem; diff --git a/core/src/main/java/com/github/skjolber/packing/iterator/MutableLoadableItemPermutationRotationIterator.java b/core/src/main/java/com/github/skjolber/packing/iterator/MutableIndexedStackableItemPermutationRotationIterator.java similarity index 84% rename from core/src/main/java/com/github/skjolber/packing/iterator/MutableLoadableItemPermutationRotationIterator.java rename to core/src/main/java/com/github/skjolber/packing/iterator/MutableIndexedStackableItemPermutationRotationIterator.java index cc59db74..1a51b297 100644 --- a/core/src/main/java/com/github/skjolber/packing/iterator/MutableLoadableItemPermutationRotationIterator.java +++ b/core/src/main/java/com/github/skjolber/packing/iterator/MutableIndexedStackableItemPermutationRotationIterator.java @@ -4,7 +4,6 @@ import java.util.List; import com.github.skjolber.packing.api.StackValue; -import com.github.skjolber.packing.api.StackableItem; import com.github.skjolber.packing.api.packager.StackableItems; /** @@ -16,15 +15,15 @@ */ -public class MutableLoadableItemPermutationRotationIterator implements StackableItems, LoadableItemPermutationRotationIterator { +public class MutableIndexedStackableItemPermutationRotationIterator implements StackableItems, StackableItemPermutationRotationIterator { public static Builder newMutableBuilder() { return new Builder(); } - public static class Builder extends AbstractLoadableIteratorBuilder { + public static class Builder extends AbstractStackableItemIteratorBuilder { - public MutableLoadableItemPermutationRotationIterator build() { + public MutableIndexedStackableItemPermutationRotationIterator build() { if(maxLoadWeight == -1) { throw new IllegalStateException(); } @@ -34,7 +33,7 @@ public MutableLoadableItemPermutationRotationIterator build() { IndexedStackableItem[] matrix = toMatrix(); - return new MutableLoadableItemPermutationRotationIterator(matrix); + return new MutableIndexedStackableItemPermutationRotationIterator(matrix); } } @@ -46,16 +45,16 @@ public MutableLoadableItemPermutationRotationIterator build() { // minimum volume from index i and above protected long[] mutableMinStackableVolume; - protected List mutableLoadableItems; + protected List mutableLoadableItems; - protected final DefaultLoadableItemPermutationRotationIterator iterator; + protected final DefaultStackableItemPermutationRotationIterator iterator; - protected final IndexedStackableItem[] loadableItems; // by index + protected final IndexedStackableItem[] stackableItems; // by index - public MutableLoadableItemPermutationRotationIterator(IndexedStackableItem[] loadableItems) { - iterator = new DefaultLoadableItemPermutationRotationIterator(loadableItems); + public MutableIndexedStackableItemPermutationRotationIterator(IndexedStackableItem[] stackableItems) { + iterator = new DefaultStackableItemPermutationRotationIterator(stackableItems); - this.loadableItems = loadableItems; + this.stackableItems = stackableItems; resetFromIterator(); } @@ -64,10 +63,10 @@ protected void resetFromIterator() { int[] permutations = iterator.getPermutations(); mutableLoadableItems = new ArrayList<>(); - for (int i = 0; i < loadableItems.length; i++) { - IndexedStackableItem loadableItem = loadableItems[i]; + for (int i = 0; i < stackableItems.length; i++) { + IndexedStackableItem loadableItem = stackableItems[i]; if(loadableItem != null && !loadableItem.isEmpty()) { - mutableLoadableItems.add(new MutableLoadableItem(loadableItem)); + mutableLoadableItems.add(new MutableIndexedStackableItem(loadableItem)); } } @@ -86,7 +85,7 @@ public IndexedStackableItem get(int index) { @Override public StackValue getStackValue(int index) { - return loadableItems[mutablePermutations[index]].getStackable().getStackValue(mutableRotations[index]); + return stackableItems[mutablePermutations[index]].getStackable().getStackValue(mutableRotations[index]); } @Override @@ -249,7 +248,7 @@ public long countRotations() { public long countMutableRotations() { long n = 1; for (int i = 0; i < mutablePermutations.length; i++) { - IndexedStackableItem value = loadableItems[mutablePermutations[i]]; + IndexedStackableItem value = stackableItems[mutablePermutations[i]]; if(Long.MAX_VALUE / value.getStackable().getStackValues().length <= n) { return -1L; } @@ -271,7 +270,7 @@ public long countMutablePermutations() { // fit within the container volume int maxCount = 0; - for (IndexedStackableItem value : loadableItems) { + for (IndexedStackableItem value : stackableItems) { if(value != null) { if(maxCount < value.getCount()) { maxCount = value.getCount(); @@ -282,7 +281,7 @@ public long countMutablePermutations() { long n = 1; if(maxCount > 1) { int[] factors = new int[maxCount]; - for (IndexedStackableItem value : loadableItems) { + for (IndexedStackableItem value : stackableItems) { if(value != null) { for (int k = 0; k < value.getCount(); k++) { factors[k]++; diff --git a/core/src/main/java/com/github/skjolber/packing/iterator/ParallelPermutationRotationIteratorList.java b/core/src/main/java/com/github/skjolber/packing/iterator/ParallelPermutationRotationIteratorList.java index cfce2100..f871a8d4 100644 --- a/core/src/main/java/com/github/skjolber/packing/iterator/ParallelPermutationRotationIteratorList.java +++ b/core/src/main/java/com/github/skjolber/packing/iterator/ParallelPermutationRotationIteratorList.java @@ -185,6 +185,7 @@ private static int firstDuplicate(int[] frequencies) { return -1; } + // https://stemhash.com/efficient-permutations-in-lexicographic-order/ static int[] kthPermutation(int[] frequencies, int elementCount, long permutationCount, long rank) { int[] result = new int[PADDING + elementCount]; diff --git a/core/src/main/java/com/github/skjolber/packing/iterator/ParallelStackableItemGroupPermutationRotationIterator.java b/core/src/main/java/com/github/skjolber/packing/iterator/ParallelStackableItemGroupPermutationRotationIterator.java new file mode 100644 index 00000000..f3fb793e --- /dev/null +++ b/core/src/main/java/com/github/skjolber/packing/iterator/ParallelStackableItemGroupPermutationRotationIterator.java @@ -0,0 +1,502 @@ +package com.github.skjolber.packing.iterator; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import com.github.skjolber.packing.api.StackValue; +import com.github.skjolber.packing.api.StackableItem; +import com.github.skjolber.packing.api.StackableItemGroup; + +public class ParallelStackableItemGroupPermutationRotationIterator extends AbstractStackableItemGroupPermutationRotationIterator { + + public static Builder newBuilder() { + return new Builder(); + } + + public static class Builder extends AbstractStackableItemGroupIteratorBuilder { + + public ParallelStackableItemGroupPermutationRotationIterator build() { + if(maxLoadWeight == -1) { + throw new IllegalStateException(); + } + if(size == null) { + throw new IllegalStateException(); + } + + List groups = toMatrix(); + + List matrix = new ArrayList<>(); + for (StackableItemGroup loadableItemGroup : groups) { + matrix.addAll(loadableItemGroup.getItems()); + } + + ParallelStackableItemGroupPermutationRotationIterator result = new ParallelStackableItemGroupPermutationRotationIterator(matrix.toArray(new IndexedStackableItem[matrix.size()]), groups); + + result.initiatePermutations(); + + return result; + } + } + + // try to avoid false sharing by using padding + public long t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15 = -1L; + + private int[] permutations; + private int[] rotations; + + private int[] lastPermutation; + private int lastPermutationMaxIndex = -1; + private boolean checkLastPermutation = false; + + // minimum volume from index i and above + protected long[] minStackableVolume; + + public ParallelStackableItemGroupPermutationRotationIterator(IndexedStackableItem[] matrix, List groups) { + super(matrix, groups); + } + + public long preventOptmisation() { + return t0 + t1 + t2 + t3 + t4 + t5 + t6 + t7 + t8 + t9 + t10 + t11 + t12 + t13 + t14 + t15; + } + + public void setReset(int[] reset) { + this.reset = reset; + } + + public int[] getPermutations() { + // TODO reuse object + int[] result = new int[permutations.length - ParallelPermutationRotationIteratorList.PADDING]; + System.arraycopy(permutations, ParallelPermutationRotationIteratorList.PADDING, result, 0, result.length); + return result; + } + + public void setGroups(List groups) { + this.groups = groups; + } + + public void setPermutations(int[] permutations) { + this.permutations = permutations; + } + + public void initMinStackableVolume() { + this.minStackableVolume = new long[permutations.length]; // i.e. with padding + + calculateMinStackableVolume(0); + } + + public void calculateMinStackableVolume(int offset) { + if(permutations.length > ParallelPermutationRotationIteratorList.PADDING) { + IndexedStackableItem value = stackableItems[permutations[permutations.length - 1]]; + + minStackableVolume[permutations.length - 1] = value.getStackable().getVolume(); + for (int i = permutations.length - 2; i >= offset + ParallelPermutationRotationIteratorList.PADDING; i--) { + long volume = stackableItems[permutations[i]].getStackable().getVolume(); + if(volume < minStackableVolume[i + 1]) { + minStackableVolume[i] = volume; + } else { + minStackableVolume[i] = minStackableVolume[i + 1]; + } + } + } + } + + public long getMinStackableVolume(int offset) { + return minStackableVolume[ParallelPermutationRotationIteratorList.PADDING + offset]; + } + + public int[] getRotations() { + int[] result = new int[rotations.length - ParallelPermutationRotationIteratorList.PADDING]; + System.arraycopy(rotations, ParallelPermutationRotationIteratorList.PADDING, result, 0, result.length); + return result; + } + + public void setRotations(int[] rotations) { + this.rotations = rotations; + } + + public int[] getLastPermutation() { + return lastPermutation; + } + + public void setLastPermutation(int[] lastPermutation) { // array without padding + this.lastPermutation = lastPermutation; + + this.checkLastPermutation = false; + + // find the first item that differs, so that we do not have to + // compare items for each iteration (to detect whether we have done enough work) + for (int k = ParallelPermutationRotationIteratorList.PADDING; k < ParallelPermutationRotationIteratorList.PADDING + lastPermutation.length; k++) { + if(permutations[k] != lastPermutation[k]) { + lastPermutationMaxIndex = k; + + break; + } + } + } + + public int getLastPermutationMaxIndex() { + return lastPermutationMaxIndex; + } + + public int nextRotation() { + return nextRotation(rotations.length - 1 - ParallelPermutationRotationIteratorList.PADDING); + } + + public int nextRotation(int maxIndex) { + // next rotation + for (int i = ParallelPermutationRotationIteratorList.PADDING + maxIndex; i >= ParallelPermutationRotationIteratorList.PADDING; i--) { + if(rotations[i] < stackableItems[permutations[i]].getStackable().getStackValues().length - 1) { + rotations[i]++; + + // reset all following counters + System.arraycopy(reset, 0, rotations, i + 1, rotations.length - (i + 1)); + + return i - ParallelPermutationRotationIteratorList.PADDING; + } + } + + return -1; + } + + protected int nextPermutationImpl() { + // https://www.baeldung.com/cs/array-generate-all-permutations#permutations-in-lexicographic-order + + int[] permutations = this.permutations; + + int endIndex = permutations.length - 1; + + for(int g = groups.size() - 1; g >= 0; g--) { + + StackableItemGroup loadableItemGroup = groups.get(g); + + // Find longest non-increasing suffix + + int i = endIndex; + int startIndex = endIndex - loadableItemGroup.stackableItemsCount() + 1; + + while (i > startIndex && permutations[i - 1] >= permutations[i]) + i--; + // Now i is the head index of the suffix + + // Are we at the last permutation already? + if(i <= startIndex) { + + // reset current group + // TODO system arraycopy? + i = startIndex; + + for (StackableItem loadableItem : loadableItemGroup.getItems()) { + IndexedStackableItem indexedStackableItem = (IndexedStackableItem)loadableItem; + for(int k = 0; k < indexedStackableItem.getCount(); k++) { + permutations[i] = indexedStackableItem.getIndex(); + + i++; + } + } + + // skip to next group + endIndex = startIndex - 1; + + continue; + + } + + // Let array[i - 1] be the pivot + // Find rightmost element that exceeds the pivot + int j = endIndex; + while (permutations[j] <= permutations[i - 1]) + j--; + // Now the value array[j] will become the new pivot + // Assertion: j >= i + + int head = i - 1 - ParallelPermutationRotationIteratorList.PADDING; + + // Swap the pivot with j + int temp = permutations[i - 1]; + permutations[i - 1] = permutations[j]; + permutations[j] = temp; + + // Reverse the suffix + j = endIndex; + while (i < j) { + temp = permutations[i]; + permutations[i] = permutations[j]; + permutations[j] = temp; + i++; + j--; + } + + // Successfully computed the next permutation + return head; + } + return -1; + + } + + public int nextPermutation() { + resetRotations(); + + int resultIndex = nextPermutationImpl(); + + int result = returnPermuationWithinRangeOrMinusOne(resultIndex); + if(result != -1) { + calculateMinStackableVolume(resultIndex); + } + return result; + } + + public int nextPermutation(int maxIndex) { + // reset rotations + resetRotations(); + + int resultIndex = nextPermutationImpl(maxIndex); + + int result = returnPermuationWithinRangeOrMinusOne(resultIndex); + if(result != -1) { + calculateMinStackableVolume(resultIndex); + } + return result; + } + + private int returnPermuationWithinRangeOrMinusOne(int resultIndex) { + if(lastPermutation != null) { + if(resultIndex <= lastPermutationMaxIndex) { + // TODO initial check for bounds here + checkLastPermutation = true; + } + + if(checkLastPermutation) { + // are we still within our designated range? + // the next permutation must be lexicographically less than the first permutation + // in the next block + + int i = ParallelPermutationRotationIteratorList.PADDING; + while (i < lastPermutation.length) { + int value = permutations[i]; + if(value < lastPermutation[i]) { + return resultIndex; + } else if(value > lastPermutation[i]) { + return -1; + } + i++; + } + // so all most be equal + // we are at the exact last permutations + return -1; + } + } + + return resultIndex; + } + + public int nextPermutationImpl(int maxIndex) { + int limit = permutations.length; + + for(int g = groups.size() - 1; g >= 0; g--) { + StackableItemGroup loadableItemGroup = groups.get(g); + + // Find longest non-increasing suffix + int startIndex = limit - loadableItemGroup.stackableItemsCount(); + + if(startIndex <= maxIndex && maxIndex < limit) { + + while (maxIndex >= startIndex) { + + int current = permutations[ParallelPermutationRotationIteratorList.PADDING + maxIndex]; + + int minIndex = -1; + for (int i = ParallelPermutationRotationIteratorList.PADDING + maxIndex + 1; i < permutations.length; i++) { + if(current < permutations[i] && (minIndex == -1 || permutations[i] < permutations[minIndex])) { + minIndex = i; + } + } + + if(minIndex == -1) { + maxIndex--; + + continue; + } + + // swap indexes + permutations[ParallelPermutationRotationIteratorList.PADDING + maxIndex] = permutations[minIndex]; + permutations[minIndex] = current; + + Arrays.sort(permutations, ParallelPermutationRotationIteratorList.PADDING + maxIndex + 1, permutations.length); + + return maxIndex; + } + } + // reset current group + // TODO system arraycopy? + int i = startIndex; + + for (StackableItem loadableItem : loadableItemGroup.getItems()) { + IndexedStackableItem indexedStackableItem = (IndexedStackableItem)loadableItem; + for(int k = 0; k < indexedStackableItem.getCount(); k++) { + permutations[i] = indexedStackableItem.getIndex(); + + i++; + } + } + + // skip to next group + limit = startIndex; + } + + return -1; + } + + public StackValue get(int permutationIndex) { + return stackableItems[permutations[ParallelPermutationRotationIteratorList.PADDING + permutationIndex]].getStackable().getStackValue(rotations[ParallelPermutationRotationIteratorList.PADDING + permutationIndex]); + } + + @Override + public PermutationRotationState getState() { + return new PermutationRotationState(getRotations(), getPermutations()); + } + + public void resetRotations() { + System.arraycopy(reset, 0, rotations, ParallelPermutationRotationIteratorList.PADDING, rotations.length - ParallelPermutationRotationIteratorList.PADDING); + } + + @Override + public int length() { + return permutations.length - ParallelPermutationRotationIteratorList.PADDING; + } + + public long countRotations() { + long n = 1; + for (int i = 0; i < permutations.length; i++) { + IndexedStackableItem value = stackableItems[permutations[i]]; + if(Long.MAX_VALUE / value.getStackable().getStackValues().length <= n) { + return -1L; + } + + n = n * value.getStackable().getStackValues().length; + } + return n; + } + + /** + * Remove permutations, if present. + */ + + @Override + public void removePermutations(List removed) { + + for (Integer i : removed) { + IndexedStackableItem loadableItem = stackableItems[i]; + + loadableItem.decrement(); + + if(loadableItem.isEmpty()) { + stackableItems[i] = null; + } + } + + // go through all groups and clean up + for(int i = 0; i < groups.size(); i++) { + StackableItemGroup group = groups.get(i); + + group.removeEmpty(); + if(group.isEmpty()) { + groups.remove(i); + i--; + } + } + } + + @Override + public void removePermutations(int count) { + + // discard a number of items from the front + for(int i = 0; i < count; i++) { + IndexedStackableItem item = stackableItems[permutations[i]]; + + item.decrement(); + + if(item.isEmpty()) { + stackableItems[i] = null; + } + } + + // go through all groups and clean up + for(int i = 0; i < groups.size(); i++) { + StackableItemGroup group = groups.get(i); + + group.removeEmpty(); + + if(group.isEmpty()) { + groups.remove(i); + i--; + } else { + break; + } + } + } + + @Override + public StackValue getStackValue(int index) { + return stackableItems[permutations[ParallelPermutationRotationIteratorList.PADDING + index]].getStackable().getStackValue(rotations[ParallelPermutationRotationIteratorList.PADDING + index]); + } + + protected void initiatePermutations() { + int count = 0; + for (int j = 0; j < stackableItems.length; j++) { + IndexedStackableItem value = stackableItems[j]; + if(value != null && !value.isEmpty()) { + count += value.getCount(); + } + } + + // need to be in ascending order for the algorithm to work + int[] permutations = new int[ParallelPermutationRotationIteratorList.PADDING + count]; + + int offset = 0; + for (int j = 0; j < stackableItems.length; j++) { + IndexedStackableItem value = stackableItems[j]; + if(value != null && !value.isEmpty()) { + for (int k = 0; k < value.getCount(); k++) { + permutations[ParallelPermutationRotationIteratorList.PADDING + offset] = j; + offset++; + } + } + } + + initiatePermutation(permutations); + + int[] lastPermutation = new int[ParallelPermutationRotationIteratorList.PADDING + count]; + + count = 0; + for(int g = 0; g < groups.size(); g++) { + StackableItemGroup group = groups.get(g); + + int stackableItemsCount = group.stackableItemsCount(); + + for(int i = 0; i < stackableItemsCount; i++) { + lastPermutation[ParallelPermutationRotationIteratorList.PADDING + count + i] = permutations[ParallelPermutationRotationIteratorList.PADDING + count + stackableItemsCount - 1 - i]; + } + count += stackableItemsCount; + } + + setLastPermutation(lastPermutation); + + // include the last permutation + lastPermutationMaxIndex = -1; + } + + protected void initiatePermutation(int[] permutations) { + this.permutations = permutations; + this.rotations = new int[permutations.length]; + this.reset = new int[rotations.length]; + + if(permutations.length > ParallelPermutationRotationIteratorList.PADDING) { + initMinStackableVolume(); + } + + checkLastPermutation = true; + } + + +} \ No newline at end of file diff --git a/core/src/main/java/com/github/skjolber/packing/iterator/ParallelStackableItemGroupPermutationRotationIteratorList.java b/core/src/main/java/com/github/skjolber/packing/iterator/ParallelStackableItemGroupPermutationRotationIteratorList.java new file mode 100644 index 00000000..49676b61 --- /dev/null +++ b/core/src/main/java/com/github/skjolber/packing/iterator/ParallelStackableItemGroupPermutationRotationIteratorList.java @@ -0,0 +1,465 @@ +package com.github.skjolber.packing.iterator; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import com.github.skjolber.packing.api.StackValue; +import com.github.skjolber.packing.api.StackableItem; +import com.github.skjolber.packing.api.StackableItemGroup; +import com.github.skjolber.packing.iterator.DefaultStackableItemGroupPermutationRotationIterator.Builder; + +/** + * + * This class is responsible for splitting the work load (as in the permutations) over multiple iterators. + * + */ + +public class ParallelStackableItemGroupPermutationRotationIteratorList implements StackableItemPermutationRotationIterator { + + protected final static int PADDING = 16; + + public static Builder newBuilder() { + return new Builder(); + } + + public static class Builder extends AbstractStackableItemGroupIteratorBuilder { + + private int parallelizationCount = -1; + + public Builder withParallelizationCount(int parallelizationCount) { + this.parallelizationCount = parallelizationCount; + + return this; + } + + public ParallelStackableItemGroupPermutationRotationIteratorList build() { + if(parallelizationCount == -1) { + throw new IllegalStateException(); + } + if(maxLoadWeight == -1) { + throw new IllegalStateException(); + } + if(size == null) { + throw new IllegalStateException(); + } + + List groups = toMatrix(); + + List matrix = new ArrayList<>(); + for (StackableItemGroup loadableItemGroup : groups) { + matrix.addAll(loadableItemGroup.getItems()); + } + + return new ParallelStackableItemGroupPermutationRotationIteratorList(matrix.toArray(new IndexedStackableItem[matrix.size()]), groups, parallelizationCount); + } + + } + + protected final int[] frequencies; + protected ParallelStackableItemGroupPermutationRotationIterator[] workUnits; + + protected List groups; + + protected int workUnitIndex = 0; + + public ParallelStackableItemGroupPermutationRotationIteratorList(IndexedStackableItem[] matrix, List groups, int parallelizationCount) { + this.frequencies = new int[matrix.length]; + + for (int i = 0; i < matrix.length; i++) { + if(matrix[i] != null) { + frequencies[i] = matrix[i].getCount(); + } + } + + this.groups = groups; + + workUnits = new ParallelStackableItemGroupPermutationRotationIterator[parallelizationCount]; + for (int i = 0; i < parallelizationCount; i++) { + + // clone working variables so threads are less of the same + // memory area as one another + IndexedStackableItem[] clone = clone(matrix); + + workUnits[i] = new ParallelStackableItemGroupPermutationRotationIterator(clone, clone(groups)); + if(workUnits[i].preventOptmisation() != -1L) { + throw new RuntimeException(); + } + } + + calculate(); + } + + private List clone(List groups) { + List result = new ArrayList<>(); + for (StackableItemGroup stackableItemGroup : groups) { + result.add(stackableItemGroup.clone()); + } + return result; + } + + private IndexedStackableItem[] clone(IndexedStackableItem[] matrix) { + IndexedStackableItem[] clone = new IndexedStackableItem[matrix.length]; + for(int i = 0; i < clone.length; i++) { + IndexedStackableItem item = matrix[i]; + if(item != null) { + clone[i] = item.clone(); + } + } + return clone; + } + + private void calculate() { + int count = getCount(); + + if(count == 0) { + return; + } + + int[] reset = new int[PADDING + count]; + + long permutationCount = countPermutations(); + + if(permutationCount == -1L) { + throw new IllegalArgumentException(); + } + + int[] copyOfFrequencies = new int[frequencies.length]; + for (int i = 0; i < workUnits.length; i++) { + long permutationNumber = (permutationCount * i) / workUnits.length; + + permutationNumber++; + + System.arraycopy(frequencies, 0, copyOfFrequencies, 0, frequencies.length); + int[] permutations = unrank(copyOfFrequencies, count, permutationCount, permutationNumber, groups); + + workUnits[i].setPermutations(permutations); + workUnits[i].setRotations(new int[reset.length]); + workUnits[i].setReset(reset); + workUnits[i].initMinStackableVolume(); + } + + for (int i = 0; i < workUnits.length - 1; i++) { + int[] nextWorkUnitPermutations = workUnits[i + 1].getPermutations(); + int[] lexiographicalLimit = new int[PADDING + nextWorkUnitPermutations.length]; + + System.arraycopy(nextWorkUnitPermutations, 0, lexiographicalLimit, PADDING, nextWorkUnitPermutations.length); + + workUnits[i].setLastPermutation(lexiographicalLimit); + } + } + + private int getCount() { + int count = 0; + for (int f : frequencies) { + count += f; + } + return count; + } + + public long countPermutations() { + int first = firstDuplicate(frequencies); + if(first == -1) { + return getPermutationCount(); + } else { + return getPermutationCountWithRepeatedItems(); + } + } + + private long getPermutationCount() { + long n = 1; + + for (StackableItemGroup loadableItemGroup : groups) { + int count = loadableItemGroup.stackableItemsCount(); + + for (long i = 0; i < count; i++) { + if(Long.MAX_VALUE / (i + 1) <= n) { + return -1L; + } + n = n * (i + 1); + } + } + return n; + } + + /** + * Return number of permutations for boxes which fit within this container. + * + * @return permutation count + */ + + public long getPermutationCountWithRepeatedItems() { + // reduce permutations for boxes which are duplicated + + // could be further bounded by looking at how many boxes (i.e. n x the smallest) which actually + // fit within the container volume + long n = 1; + + for (StackableItemGroup loadableItemGroup : groups) { + + List items = loadableItemGroup.getItems(); + + int count = loadableItemGroup.stackableItemsCount(); + + int maxCount = 0; + for (StackableItem value : items) { + if(value != null) { + if(maxCount < value.getCount()) { + maxCount = value.getCount(); + } + } + } + + if(maxCount > 1) { + int[] factors = new int[maxCount]; + for (StackableItem value : items) { + if(value != null) { + for (int k = 0; k < value.getCount(); k++) { + factors[k]++; + } + } + } + + for (long i = 0; i < count; i++) { + if(Long.MAX_VALUE / (i + 1) <= n) { + return -1L; + } + + n = n * (i + 1); + + // reduce n if possible + for (int k = 1; k < maxCount; k++) { + while (factors[k] > 0 && n % (k + 1) == 0) { + n = n / (k + 1); + + factors[k]--; + } + } + } + + for (int k = 1; k < maxCount; k++) { + while (factors[k] > 0) { + n = n / (k + 1); + + factors[k]--; + } + } + } else { + for (long i = 0; i < count; i++) { + if(Long.MAX_VALUE / (i + 1) <= n) { + return -1L; + } + n = n * (i + 1); + } + } + } + return n; + } + + private static int firstDuplicate(int[] frequencies) { + for (int i = 0; i < frequencies.length; i++) { + if(frequencies[i] > 1) { + return i; + } + } + return -1; + } + + protected static int[] unrank(int[] frequencies, int elementCount, long permutationCount, long rank, List groups) { + int[] result = new int[PADDING + elementCount]; + + int resultOffset = 0; + int frequenciesOffset = 0; + for (int j = 0; j < groups.size(); j++) { + StackableItemGroup group = groups.get(j); + + int stackableItemsCount = group.stackableItemsCount(); + + for(int i = 0; i < stackableItemsCount; i++) { + for(int k = 0; k < group.size(); k++) { + if(frequencies[frequenciesOffset + k] == 0) { + continue; + } + // suffixcount is the number of distinct perms that begin with x + long suffixcount = permutationCount * frequencies[frequenciesOffset + k] / (stackableItemsCount - i); + if (rank <= suffixcount) { + result[PADDING + resultOffset + i] = frequenciesOffset + k; + + permutationCount = suffixcount; + + frequencies[frequenciesOffset + k]--; + break; + } + rank -= suffixcount; + } + } + + frequenciesOffset += group.size(); + resultOffset += stackableItemsCount; + } + return result; + } + + + public ParallelStackableItemGroupPermutationRotationIterator[] getIterators() { + return workUnits; + } + + public ParallelStackableItemGroupPermutationRotationIterator getIterator(int i) { + return workUnits[i]; + } + + public int[] getFrequencies() { + return frequencies; + } + + public int length() { + return workUnits[workUnitIndex].length(); + } + + @Override + public StackValue getStackValue(int index) { + return workUnits[workUnitIndex].getStackValue(index); + } + + @Override + public PermutationRotationState getState() { + return workUnits[workUnitIndex].getState(); + } + + @Override + public List get(PermutationRotationState state, int length) { + return workUnits[workUnitIndex].get(state, length); + } + + @Override + public long getMinStackableVolume(int index) { + return workUnits[workUnitIndex].getMinStackableVolume(index); + } + + @Override + public int getMinStackableAreaIndex(int index) { + return workUnits[workUnitIndex].getMinStackableAreaIndex(index); + } + + @Override + public int[] getPermutations() { + return workUnits[workUnitIndex].getPermutations(); + } + + @Override + public long countRotations() { + return workUnits[workUnitIndex].countRotations(); + } + + @Override + public int nextRotation() { + return workUnits[workUnitIndex].nextRotation(); + } + + @Override + public int nextRotation(int maxIndex) { + return workUnits[workUnitIndex].nextRotation(maxIndex); + } + + @Override + public int nextPermutation() { + while(workUnitIndex < workUnits.length) { + int nextPermutation = workUnits[workUnitIndex].nextPermutation(); + + if(nextPermutation != -1) { + return nextPermutation; + } + + // compare previous permutation to the next + workUnitIndex++; + if(workUnitIndex < workUnits.length) { + int[] permutations = workUnits[workUnitIndex].getPermutations(); + + // TODO how to find the correct index here? + for(int i = permutations.length - 2; i >= 0; i--) { + if(permutations[i] > permutations[i + 1]) { + return i; + } + } + + // should never happen + return 0; + } + } + return -1; + } + + @Override + public int nextPermutation(int maxIndex) { + + iterators: + while(workUnitIndex < workUnits.length) { + int nextPermutation = workUnits[workUnitIndex].nextPermutation(maxIndex); + + if(nextPermutation != -1) { + return nextPermutation; + } + // compare previous permutation to the next + workUnitIndex++; + if(workUnitIndex < workUnits.length) { + int[] permutations = workUnits[workUnitIndex].getPermutations(); + + // TODO how to find the correct index here? + for(int i = permutations.length - 2; i >= 0; i--) { + if(permutations[i] > permutations[i + 1]) { + if(i <= maxIndex) { + return i; + } else { + continue iterators; + } + } + } + + // should never happen + return 0; + } + } + return -1; + } + + @Override + public void removePermutations(int count) { + int[] permutations = workUnits[workUnitIndex].getPermutations(); + + List removed = new ArrayList<>(permutations.length); + + for(int i = 0; i < count; i++) { + removed.add(permutations[i]); + } + + removePermutations(removed); + } + + public void removePermutations(List removed) { + for (Integer integer : removed) { + if(frequencies[integer] > 0) { + frequencies[integer]--; + } + } + + // go through all groups and clean up + for(int i = 0; i < groups.size(); i++) { + StackableItemGroup group = groups.get(i); + + group.removeEmpty(); + if(group.isEmpty()) { + groups.remove(i); + i--; + } + } + + for (ParallelStackableItemGroupPermutationRotationIterator unit : workUnits) { + unit.removePermutations(removed); + } + + calculate(); + } + + +} diff --git a/core/src/main/java/com/github/skjolber/packing/iterator/LoadableItemPermutationRotationIterator.java b/core/src/main/java/com/github/skjolber/packing/iterator/StackableItemPermutationRotationIterator.java similarity index 97% rename from core/src/main/java/com/github/skjolber/packing/iterator/LoadableItemPermutationRotationIterator.java rename to core/src/main/java/com/github/skjolber/packing/iterator/StackableItemPermutationRotationIterator.java index 590bb2e6..dcc43314 100644 --- a/core/src/main/java/com/github/skjolber/packing/iterator/LoadableItemPermutationRotationIterator.java +++ b/core/src/main/java/com/github/skjolber/packing/iterator/StackableItemPermutationRotationIterator.java @@ -37,7 +37,7 @@ * target="_top">next-lexicographical-permutation-algorithm */ -public interface LoadableItemPermutationRotationIterator { +public interface StackableItemPermutationRotationIterator { /** * diff --git a/core/src/test/java/com/github/skjolber/packing/iterator/AbstractLoadablePermutationRotationIteratorTest.java b/core/src/test/java/com/github/skjolber/packing/iterator/AbstractStackableItemPermutationRotationIteratorTest.java similarity index 91% rename from core/src/test/java/com/github/skjolber/packing/iterator/AbstractLoadablePermutationRotationIteratorTest.java rename to core/src/test/java/com/github/skjolber/packing/iterator/AbstractStackableItemPermutationRotationIteratorTest.java index ff076307..08d4cb28 100644 --- a/core/src/test/java/com/github/skjolber/packing/iterator/AbstractLoadablePermutationRotationIteratorTest.java +++ b/core/src/test/java/com/github/skjolber/packing/iterator/AbstractStackableItemPermutationRotationIteratorTest.java @@ -15,9 +15,9 @@ import com.github.skjolber.packing.api.StackValue; import com.github.skjolber.packing.api.StackableItem; -public abstract class AbstractLoadablePermutationRotationIteratorTest { +public abstract class AbstractStackableItemPermutationRotationIteratorTest { - protected static void assertMinStackableVolumeValid(LoadableItemPermutationRotationIterator iterator) { + protected static void assertMinStackableVolumeValid(StackableItemPermutationRotationIterator iterator) { for (int i = 0; i < iterator.length(); i++) { long calculatedMinStackableVolume = getMinStackableVolume(iterator, i); long cachedMinStackableVolume = iterator.getMinStackableVolume(i); @@ -26,7 +26,7 @@ protected static void assertMinStackableVolumeValid(LoadableItemPermutationRotat } } - protected static long getMinStackableVolume(LoadableItemPermutationRotationIterator iterator, int offset) { + protected static long getMinStackableVolume(StackableItemPermutationRotationIterator iterator, int offset) { long minVolume = Long.MAX_VALUE; for (int i = offset; i < iterator.length(); i++) { StackValue stackValue = iterator.getStackValue(i); @@ -55,7 +55,7 @@ void testRotationCount() { products1.add(item); } - LoadableItemPermutationRotationIterator rotator = + StackableItemPermutationRotationIterator rotator = newBuilder() .withLoadSize(container) .withStackableItems(products1) @@ -82,7 +82,7 @@ void testUnconstrainedRotationCount() { Box box = Box.newBuilder().withSize(1, 2, 3).withRotate3D().withDescription("0").withWeight(1).build(); products.add(new StackableItem(box)); - LoadableItemPermutationRotationIterator rotator = newBuilder() + StackableItemPermutationRotationIterator rotator = newBuilder() .withLoadSize(container) .withStackableItems(products) .withMaxLoadWeight(products.size()) @@ -101,7 +101,7 @@ void testNumberOfConstrainedRotations() { products.add(new StackableItem(box)); - LoadableItemPermutationRotationIterator rotator = newBuilder() + StackableItemPermutationRotationIterator rotator = newBuilder() .withLoadSize(container) .withStackableItems(products) .withMaxLoadWeight(products.size()) @@ -122,7 +122,7 @@ void testNumberOfConstrainedRotationsWithOutOfScopeBox() { products.add(new StackableItem(box1)); products.add(new StackableItem(box2)); - LoadableItemPermutationRotationIterator rotator = newBuilder() + StackableItemPermutationRotationIterator rotator = newBuilder() .withLoadSize(container) .withStackableItems(products) .withMaxLoadWeight(products.size()) @@ -141,7 +141,7 @@ void testNumberOfRotationsForSquare2D() { Box box = Box.newBuilder().withSize(3, 1, 1).withRotate2D().withDescription("0").withWeight(1).build(); products.add(new StackableItem(box)); - LoadableItemPermutationRotationIterator rotator = newBuilder() + StackableItemPermutationRotationIterator rotator = newBuilder() .withLoadSize(container) .withStackableItems(products) .withMaxLoadWeight(products.size()) @@ -159,7 +159,7 @@ void testNumberOfConstrainedRotationsForSquare2D() { Box box = Box.newBuilder().withSize(3, 1, 1).withRotate2D().withDescription("0").withWeight(1).build(); products.add(new StackableItem(box)); - LoadableItemPermutationRotationIterator rotator = newBuilder() + StackableItemPermutationRotationIterator rotator = newBuilder() .withLoadSize(container) .withStackableItems(products) .withMaxLoadWeight(products.size()) @@ -177,7 +177,7 @@ void testNumberOfRotationsForSquare3D() { Box box = Box.newBuilder().withRotate3D().withSize(1, 1, 1).withDescription("0").withWeight(1).build(); products.add(new StackableItem(box)); - LoadableItemPermutationRotationIterator rotator = newBuilder() + StackableItemPermutationRotationIterator rotator = newBuilder() .withLoadSize(container) .withStackableItems(products) .withMaxLoadWeight(products.size()) @@ -196,7 +196,7 @@ void testRotation() { products.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("1").withWeight(1).build())); products.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("2").withWeight(1).build())); - LoadableItemPermutationRotationIterator rotator = newBuilder() + StackableItemPermutationRotationIterator rotator = newBuilder() .withLoadSize(container) .withStackableItems(products) .withMaxLoadWeight(products.size()) @@ -232,7 +232,7 @@ void testPermutations() { products.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("3").withWeight(1).build())); products.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("4").withWeight(1).build())); - LoadableItemPermutationRotationIterator rotator = newBuilder() + StackableItemPermutationRotationIterator rotator = newBuilder() .withLoadSize(container) .withStackableItems(products) .withMaxLoadWeight(products.size()) @@ -260,7 +260,7 @@ void testPermutationsForMaxIndex() { products.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("3").withWeight(1).build())); products.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("4").withWeight(1).build())); - LoadableItemPermutationRotationIterator rotator = newBuilder() + StackableItemPermutationRotationIterator rotator = newBuilder() .withLoadSize(container) .withStackableItems(products) .withMaxLoadWeight(products.size()) @@ -286,13 +286,13 @@ void testPermutationsForMaxIndexInRightOrder() { products.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 6).withDescription("3").withWeight(1).build())); products.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 7).withDescription("4").withWeight(1).build())); - LoadableItemPermutationRotationIterator rotator1 = newBuilder() + StackableItemPermutationRotationIterator rotator1 = newBuilder() .withLoadSize(container) .withStackableItems(products) .withMaxLoadWeight(products.size()) .build(); - LoadableItemPermutationRotationIterator rotator2 = newBuilder() + StackableItemPermutationRotationIterator rotator2 = newBuilder() .withLoadSize(container) .withStackableItems(products) .withMaxLoadWeight(products.size()) @@ -324,7 +324,7 @@ void testPermutationCorrectIndexReturned() { products.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 5).withDescription("2").withWeight(1).build())); products.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 6).withDescription("3").withWeight(1).build())); - LoadableItemPermutationRotationIterator rotator = newBuilder() + StackableItemPermutationRotationIterator rotator = newBuilder() .withLoadSize(container) .withStackableItems(products) .withMaxLoadWeight(products.size()) @@ -372,7 +372,7 @@ void testPermutationsWithMultipleBoxes() { products.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("0").withWeight(1).build(), 2)); products.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("1").withWeight(1).build(), 4)); - LoadableItemPermutationRotationIterator rotator = newBuilder() + StackableItemPermutationRotationIterator rotator = newBuilder() .withLoadSize(container) .withStackableItems(products) .withMaxLoadWeight(products.size()) @@ -397,7 +397,7 @@ void testCounts() { Dimension container = new Dimension(null, 5 * n, 10, 10); - LoadableItemPermutationRotationIterator rotator = newBuilder() + StackableItemPermutationRotationIterator rotator = newBuilder() .withLoadSize(container) .withStackableItems(products) .withMaxLoadWeight(products.size()) @@ -420,7 +420,7 @@ void testCountPermutations1() { products.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(5, 10, 10).withWeight(1).build(), 1)); } - LoadableItemPermutationRotationIterator iterator = newBuilder() + StackableItemPermutationRotationIterator iterator = newBuilder() .withLoadSize(container) .withStackableItems(products) .withMaxLoadWeight(products.size()) @@ -439,7 +439,7 @@ void testCountPermutations2() { for (int k = 0; k < n; k++) { products.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(5, 10, 10).withWeight(1).build(), 1)); } - LoadableItemPermutationRotationIterator iterator = newBuilder() + StackableItemPermutationRotationIterator iterator = newBuilder() .withLoadSize(container) .withStackableItems(products) .withMaxLoadWeight(products.size()) @@ -460,7 +460,7 @@ void testRemovePermutations1() { products.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("3").withWeight(1).build())); products.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("4").withWeight(1).build())); - LoadableItemPermutationRotationIterator iterator = newBuilder() + StackableItemPermutationRotationIterator iterator = newBuilder() .withLoadSize(container) .withStackableItems(products) .withMaxLoadWeight(products.size()) @@ -493,7 +493,7 @@ void testRemovePermutations2() { products.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("3").withWeight(1).build())); products.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("4").withWeight(1).build())); - LoadableItemPermutationRotationIterator iterator = newBuilder() + StackableItemPermutationRotationIterator iterator = newBuilder() .withLoadSize(container) .withStackableItems(products) .withMaxLoadWeight(products.size()) diff --git a/core/src/test/java/com/github/skjolber/packing/iterator/DefaultLoadableItemGroupPermutationRotationIteratorTest.java b/core/src/test/java/com/github/skjolber/packing/iterator/DefaultStackableItemGroupPermutationRotationIteratorTest.java similarity index 93% rename from core/src/test/java/com/github/skjolber/packing/iterator/DefaultLoadableItemGroupPermutationRotationIteratorTest.java rename to core/src/test/java/com/github/skjolber/packing/iterator/DefaultStackableItemGroupPermutationRotationIteratorTest.java index 2be91a6c..d714392e 100644 --- a/core/src/test/java/com/github/skjolber/packing/iterator/DefaultLoadableItemGroupPermutationRotationIteratorTest.java +++ b/core/src/test/java/com/github/skjolber/packing/iterator/DefaultStackableItemGroupPermutationRotationIteratorTest.java @@ -15,7 +15,7 @@ import com.github.skjolber.packing.api.StackableItem; import com.github.skjolber.packing.api.StackableItemGroup; -public class DefaultLoadableItemGroupPermutationRotationIteratorTest { +public class DefaultStackableItemGroupPermutationRotationIteratorTest { @Test void testPermutations() { @@ -35,7 +35,7 @@ void testPermutations() { products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("5").withWeight(1).build())); groups.add(new StackableItemGroup("2", products2)); - DefaultLoadableItemGroupPermutationRotationIterator rotator = DefaultLoadableItemGroupPermutationRotationIterator.newBuilder() + DefaultStackableItemGroupPermutationRotationIterator rotator = DefaultStackableItemGroupPermutationRotationIterator.newBuilder() .withLoadSize(container) .withStackableItemGroups(groups) .withMaxLoadWeight(products2.size()) @@ -72,7 +72,7 @@ void testPermutationsRepeatedItems() { products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("6").withWeight(1).build(), 2)); groups.add(new StackableItemGroup("2", products2)); - DefaultLoadableItemGroupPermutationRotationIterator rotator = DefaultLoadableItemGroupPermutationRotationIterator.newBuilder() + DefaultStackableItemGroupPermutationRotationIterator rotator = DefaultStackableItemGroupPermutationRotationIterator.newBuilder() .withLoadSize(container) .withStackableItemGroups(groups) .withMaxLoadWeight(products2.size()) @@ -108,7 +108,7 @@ void testRemovePermutations() { products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("5").withWeight(1).build())); groups.add(new StackableItemGroup("2", products2)); - DefaultLoadableItemGroupPermutationRotationIterator rotator = DefaultLoadableItemGroupPermutationRotationIterator.newBuilder() + DefaultStackableItemGroupPermutationRotationIterator rotator = DefaultStackableItemGroupPermutationRotationIterator.newBuilder() .withLoadSize(container) .withStackableItemGroups(groups) .withMaxLoadWeight(products2.size()) @@ -154,7 +154,7 @@ void testNexPermutationMaxIndexGroup1() { products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("8").withWeight(1).build())); groups.add(new StackableItemGroup("2", products2)); - DefaultLoadableItemGroupPermutationRotationIterator iterator = DefaultLoadableItemGroupPermutationRotationIterator.newBuilder() + DefaultStackableItemGroupPermutationRotationIterator iterator = DefaultStackableItemGroupPermutationRotationIterator.newBuilder() .withLoadSize(container) .withStackableItemGroups(groups) .withMaxLoadWeight(products2.size()) @@ -184,10 +184,7 @@ void testNexPermutationMaxIndexGroup1() { for(int i = products1.size(); i < permutations.length; i++) { assertEquals(clone[i], permutations[i]); } - - } - @Test void testNexPermutationMaxIndexGroup2() { @@ -210,7 +207,7 @@ void testNexPermutationMaxIndexGroup2() { products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("8").withWeight(1).build())); groups.add(new StackableItemGroup("2", products2)); - DefaultLoadableItemGroupPermutationRotationIterator iterator = DefaultLoadableItemGroupPermutationRotationIterator.newBuilder() + DefaultStackableItemGroupPermutationRotationIterator iterator = DefaultStackableItemGroupPermutationRotationIterator.newBuilder() .withLoadSize(container) .withStackableItemGroups(groups) .withMaxLoadWeight(products2.size()) @@ -239,11 +236,7 @@ void testNexPermutationMaxIndexGroup2() { for(int i = 0; i < products1.size(); i++) { assertEquals(clone[i], permutations[i]); } - - } - - @Test void testNexPermutationMaxIndexTransitionGroup() { @@ -267,7 +260,7 @@ void testNexPermutationMaxIndexTransitionGroup() { products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("8").withWeight(1).build())); groups.add(new StackableItemGroup("2", products2)); - DefaultLoadableItemGroupPermutationRotationIterator iterator = DefaultLoadableItemGroupPermutationRotationIterator.newBuilder() + DefaultStackableItemGroupPermutationRotationIterator iterator = DefaultStackableItemGroupPermutationRotationIterator.newBuilder() .withLoadSize(container) .withStackableItemGroups(groups) .withMaxLoadWeight(products2.size()) diff --git a/core/src/test/java/com/github/skjolber/packing/iterator/LoadableItemPermutationRotationIteratorTest.java b/core/src/test/java/com/github/skjolber/packing/iterator/LoadableItemPermutationRotationIteratorTest.java deleted file mode 100644 index 3b6fb8a9..00000000 --- a/core/src/test/java/com/github/skjolber/packing/iterator/LoadableItemPermutationRotationIteratorTest.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.github.skjolber.packing.iterator; - -import com.github.skjolber.packing.iterator.DefaultLoadableItemPermutationRotationIterator.Builder; - -class LoadableItemPermutationRotationIteratorTest extends AbstractLoadablePermutationRotationIteratorTest { - - @Override - public Builder newBuilder() { - return DefaultLoadableItemPermutationRotationIterator.newBuilder(); - } - -} diff --git a/core/src/test/java/com/github/skjolber/packing/iterator/MutableLoadablePermutationRotationIteratorTest.java b/core/src/test/java/com/github/skjolber/packing/iterator/MutableLoadablePermutationRotationIteratorTest.java index 721615e8..ddc8ddac 100644 --- a/core/src/test/java/com/github/skjolber/packing/iterator/MutableLoadablePermutationRotationIteratorTest.java +++ b/core/src/test/java/com/github/skjolber/packing/iterator/MutableLoadablePermutationRotationIteratorTest.java @@ -14,11 +14,11 @@ import com.github.skjolber.packing.api.StackableItem; import com.github.skjolber.packing.api.packager.StackableItems; -class MutableLoadablePermutationRotationIteratorTest extends AbstractLoadablePermutationRotationIteratorTest { +class MutableLoadablePermutationRotationIteratorTest extends AbstractStackableItemPermutationRotationIteratorTest { @Override - public MutableLoadableItemPermutationRotationIterator.Builder newBuilder() { - return MutableLoadableItemPermutationRotationIterator.newMutableBuilder(); + public MutableIndexedStackableItemPermutationRotationIterator.Builder newBuilder() { + return MutableIndexedStackableItemPermutationRotationIterator.newMutableBuilder(); } @Test @@ -36,7 +36,7 @@ void testMutableRotationCount() { products1.add(item); } - MutableLoadableItemPermutationRotationIterator rotator = + MutableIndexedStackableItemPermutationRotationIterator rotator = newBuilder() .withLoadSize(container) .withStackableItems(products1) @@ -80,7 +80,7 @@ void testMutablePermutationsWithMultipleBoxes() { products.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withId("0").withWeight(1).build(), 2)); products.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withId("1").withWeight(1).build(), 4)); - MutableLoadableItemPermutationRotationIterator rotator = newBuilder() + MutableIndexedStackableItemPermutationRotationIterator rotator = newBuilder() .withLoadSize(container) .withStackableItems(products) .withMaxLoadWeight(products.size()) @@ -113,7 +113,7 @@ void testLoadableItems() { products.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withId("0").withWeight(1).build(), 2)); products.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withId("1").withWeight(1).build(), 4)); - MutableLoadableItemPermutationRotationIterator rotator = newBuilder() + MutableIndexedStackableItemPermutationRotationIterator rotator = newBuilder() .withLoadSize(container) .withStackableItems(products) .withMaxLoadWeight(products.size()) @@ -151,7 +151,7 @@ void testLoadableItems() { assertEquals(rotator.size(), 0); } - public int[] toFrequency(MutableLoadableItemPermutationRotationIterator rotator, int size) { + public int[] toFrequency(MutableIndexedStackableItemPermutationRotationIterator rotator, int size) { int[] counts = new int[size]; for (int i : rotator.getPermutations()) { counts[i]++; diff --git a/core/src/test/java/com/github/skjolber/packing/iterator/ParallelStackableItemGroupPermutationRotationIteratorListTest.java b/core/src/test/java/com/github/skjolber/packing/iterator/ParallelStackableItemGroupPermutationRotationIteratorListTest.java new file mode 100644 index 00000000..7c9636ea --- /dev/null +++ b/core/src/test/java/com/github/skjolber/packing/iterator/ParallelStackableItemGroupPermutationRotationIteratorListTest.java @@ -0,0 +1,307 @@ +package com.github.skjolber.packing.iterator; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import com.github.skjolber.packing.api.Box; +import com.github.skjolber.packing.api.Dimension; +import com.github.skjolber.packing.api.StackableItem; +import com.github.skjolber.packing.api.StackableItemGroup; + +public class ParallelStackableItemGroupPermutationRotationIteratorListTest { + + @Test + void testPermutations() { + Dimension container = new Dimension(null, 9, 1, 1); + + List groups = new ArrayList<>(); + + List products1 = new ArrayList<>(); + products1.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("0").withWeight(1).build())); + products1.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("1").withWeight(1).build())); + products1.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("2").withWeight(1).build())); + groups.add(new StackableItemGroup("1", products1)); + + List products2 = new ArrayList<>(); + products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("3").withWeight(1).build())); + products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("4").withWeight(1).build())); + products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("5").withWeight(1).build())); + groups.add(new StackableItemGroup("2", products2)); + + ParallelStackableItemGroupPermutationRotationIteratorList rotator = ParallelStackableItemGroupPermutationRotationIteratorList.newBuilder() + .withLoadSize(container) + .withStackableItemGroups(groups) + .withMaxLoadWeight(products2.size()) + .withParallelizationCount(5) + .build(); + + ParallelStackableItemGroupPermutationRotationIterator rotator2 = ParallelStackableItemGroupPermutationRotationIterator.newBuilder() + .withLoadSize(container) + .withStackableItemGroups(groups) + .withMaxLoadWeight(products2.size()) + .build(); + + int count = 0; + do { + count++; + System.out.println(Arrays.toString(rotator.getPermutations()) + " " + Arrays.toString(rotator2.getPermutations())); + } while (rotator.nextPermutation() != -1 && rotator2.nextPermutation() != -1); + + assertEquals((3 * 2 * 1) * (3 * 2 * 1), count); + + assertEquals(count, rotator.countPermutations()); + } + + @Test + void testPermutationsRepeatedItems() { + Dimension container = new Dimension(null, 9, 1, 1); + + List groups = new ArrayList<>(); + + List products1 = new ArrayList<>(); + products1.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("0").withWeight(1).build())); + products1.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("1").withWeight(1).build())); + products1.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("2").withWeight(1).build())); + groups.add(new StackableItemGroup("1", products1)); + + + List products2 = new ArrayList<>(); + products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("3").withWeight(1).build())); + products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("4").withWeight(1).build())); + products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("5").withWeight(1).build())); + products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("6").withWeight(1).build(), 2)); + groups.add(new StackableItemGroup("2", products2)); + + ParallelStackableItemGroupPermutationRotationIteratorList rotator = ParallelStackableItemGroupPermutationRotationIteratorList.newBuilder() + .withLoadSize(container) + .withStackableItemGroups(groups) + .withMaxLoadWeight(products2.size()) + .withParallelizationCount(2) + .build(); + + int count = 0; + do { + count++; + System.out.println(Arrays.toString(rotator.getPermutations())); + } while (rotator.nextPermutation() != -1); + + assertEquals((3 * 2 * 1) * (5 * 4 * 3 * 2 * 1) / 2, count); + + assertEquals(count, rotator.countPermutations()); + } + + @Test + void testRemovePermutations() { + Dimension container = new Dimension(null, 9, 1, 1); + + List groups = new ArrayList<>(); + + List products1 = new ArrayList<>(); + products1.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("0").withWeight(1).build())); + products1.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("1").withWeight(1).build())); + products1.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("2").withWeight(1).build())); + groups.add(new StackableItemGroup("1", products1)); + + + List products2 = new ArrayList<>(); + products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("3").withWeight(1).build())); + products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("4").withWeight(1).build())); + products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("5").withWeight(1).build())); + groups.add(new StackableItemGroup("2", products2)); + + ParallelStackableItemGroupPermutationRotationIteratorList rotator = ParallelStackableItemGroupPermutationRotationIteratorList.newBuilder() + .withLoadSize(container) + .withStackableItemGroups(groups) + .withMaxLoadWeight(products2.size()) + .withParallelizationCount(2) + .build(); + + rotator.removePermutations(1); + + int count = 0; + do { + count++; + System.out.println(Arrays.toString(rotator.getPermutations())); + } while (rotator.nextPermutation() != -1); + + assertEquals((2 * 1) * (3 * 2 * 1), count); + + assertEquals(count, rotator.countPermutations()); + + // remove the rest of the group + rotator.removePermutations(2); + + assertEquals(3 * 2 * 1, rotator.countPermutations()); + } + + @Test + void testNexPermutationMaxIndexGroup1() { + Dimension container = new Dimension(null, 9, 1, 1); + + List groups = new ArrayList<>(); + + List products1 = new ArrayList<>(); + products1.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("0").withWeight(1).build())); + products1.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("1").withWeight(1).build())); + products1.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("2").withWeight(1).build())); + products1.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("3").withWeight(1).build())); + products1.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("4").withWeight(1).build())); + groups.add(new StackableItemGroup("1", products1)); + + + List products2 = new ArrayList<>(); + products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("5").withWeight(1).build())); + products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("6").withWeight(1).build())); + products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("7").withWeight(1).build())); + products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("8").withWeight(1).build())); + groups.add(new StackableItemGroup("2", products2)); + + ParallelStackableItemGroupPermutationRotationIteratorList iterator = ParallelStackableItemGroupPermutationRotationIteratorList.newBuilder() + .withLoadSize(container) + .withStackableItemGroups(groups) + .withMaxLoadWeight(products2.size()) + .withParallelizationCount(2) + .build(); + + int[] permutations = iterator.getPermutations(); + + int[] clone = new int[permutations.length]; + System.arraycopy(permutations, 0, clone, 0, clone.length); + + // forward to some random permutation within the group2 + for(int i = 0; i < 10; i++) { + iterator.nextPermutation(); + } + + int maxIndex = 3; + + iterator.nextPermutation(maxIndex); + + for(int i = 0; i < maxIndex; i++) { + assertEquals(clone[i], permutations[i]); + } + + assertNotEquals(clone[maxIndex], permutations[maxIndex]); + + // group 2 should be reset + for(int i = products1.size(); i < permutations.length; i++) { + assertEquals(clone[i], permutations[i]); + } + } + + @Test + void testNexPermutationMaxIndexGroup2() { + Dimension container = new Dimension(null, 9, 1, 1); + + List groups = new ArrayList<>(); + + List products1 = new ArrayList<>(); + products1.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("0").withWeight(1).build())); + products1.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("1").withWeight(1).build())); + products1.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("2").withWeight(1).build())); + products1.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("3").withWeight(1).build())); + products1.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("4").withWeight(1).build())); + groups.add(new StackableItemGroup("1", products1)); + + List products2 = new ArrayList<>(); + products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("5").withWeight(1).build())); + products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("6").withWeight(1).build())); + products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("7").withWeight(1).build())); + products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("8").withWeight(1).build())); + groups.add(new StackableItemGroup("2", products2)); + + ParallelStackableItemGroupPermutationRotationIteratorList iterator = ParallelStackableItemGroupPermutationRotationIteratorList.newBuilder() + .withLoadSize(container) + .withStackableItemGroups(groups) + .withMaxLoadWeight(products2.size()) + .withParallelizationCount(2) + .build(); + + int[] permutations = iterator.getPermutations(); + + int[] clone = new int[permutations.length]; + System.arraycopy(permutations, 0, clone, 0, clone.length); + + System.out.println(Arrays.toString(iterator.getPermutations())); + + int maxIndex = 6; + + iterator.nextPermutation(maxIndex); + + System.out.println(Arrays.toString(iterator.getPermutations())); + + for(int i = 0; i < maxIndex; i++) { + assertEquals(clone[i], permutations[i]); + } + + assertNotEquals(clone[maxIndex], permutations[maxIndex]); + + // group 1 should not be touched + for(int i = 0; i < products1.size(); i++) { + assertEquals(clone[i], permutations[i]); + } + } + + @Test + void testNexPermutationMaxIndexTransitionGroup() { + Dimension container = new Dimension(null, 9, 1, 1); + + List groups = new ArrayList<>(); + + List products1 = new ArrayList<>(); + products1.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("0").withWeight(1).build())); + products1.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("1").withWeight(1).build())); + products1.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("2").withWeight(1).build())); + products1.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("3").withWeight(1).build())); + products1.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("4").withWeight(1).build())); + groups.add(new StackableItemGroup("1", products1)); + + + List products2 = new ArrayList<>(); + products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("5").withWeight(1).build())); + products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("6").withWeight(1).build())); + products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("7").withWeight(1).build())); + products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("8").withWeight(1).build())); + groups.add(new StackableItemGroup("2", products2)); + + ParallelStackableItemGroupPermutationRotationIteratorList iterator = ParallelStackableItemGroupPermutationRotationIteratorList.newBuilder() + .withLoadSize(container) + .withStackableItemGroups(groups) + .withMaxLoadWeight(products2.size()) + .withParallelizationCount(2) + .build(); + + // go to the last permuation of the second group + for(int i = 0; i < 4 * 3 * 2 * 1 - 1; i++) { + iterator.nextPermutation(); + } + + int[] permutations = iterator.getPermutations(); + + int[] clone = new int[permutations.length]; + System.arraycopy(permutations, 0, clone, 0, clone.length); + + System.out.println(Arrays.toString(iterator.getPermutations())); + + int maxIndex = 6; + + int index = iterator.nextPermutation(maxIndex); + + assertTrue(index < products1.size()); + + System.out.println(Arrays.toString(iterator.getPermutations())); + + for(int i = 0; i < index; i++) { + assertEquals(clone[i], permutations[i]); + } + + assertNotEquals(clone[index], permutations[index]); + } +} diff --git a/core/src/test/java/com/github/skjolber/packing/iterator/ParallelStackableItemGroupPermutationRotationIteratorTest.java b/core/src/test/java/com/github/skjolber/packing/iterator/ParallelStackableItemGroupPermutationRotationIteratorTest.java new file mode 100644 index 00000000..1c7055b2 --- /dev/null +++ b/core/src/test/java/com/github/skjolber/packing/iterator/ParallelStackableItemGroupPermutationRotationIteratorTest.java @@ -0,0 +1,54 @@ +package com.github.skjolber.packing.iterator; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import com.github.skjolber.packing.api.Box; +import com.github.skjolber.packing.api.Dimension; +import com.github.skjolber.packing.api.StackableItem; +import com.github.skjolber.packing.api.StackableItemGroup; + +public class ParallelStackableItemGroupPermutationRotationIteratorTest { + + @Test + void testPermutations() { + Dimension container = new Dimension(null, 9, 1, 1); + + List groups = new ArrayList<>(); + + List products1 = new ArrayList<>(); + products1.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("0").withWeight(1).build())); + products1.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("1").withWeight(1).build())); + products1.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("2").withWeight(1).build())); + groups.add(new StackableItemGroup("1", products1)); + + List products2 = new ArrayList<>(); + products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("3").withWeight(1).build())); + products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("4").withWeight(1).build())); + products2.add(new StackableItem(Box.newBuilder().withRotate3D().withSize(1, 1, 3).withDescription("5").withWeight(1).build())); + groups.add(new StackableItemGroup("2", products2)); + + ParallelStackableItemGroupPermutationRotationIterator rotator = ParallelStackableItemGroupPermutationRotationIterator.newBuilder() + .withLoadSize(container) + .withStackableItemGroups(groups) + .withMaxLoadWeight(products2.size()) + .build(); + + int count = 0; + do { + count++; + System.out.println(Arrays.toString(rotator.getPermutations())); + } while (rotator.nextPermutation() != -1); + + assertEquals((3 * 2 * 1) * (3 * 2 * 1), count); + + assertEquals(count, rotator.countPermutations()); + } + + +} diff --git a/core/src/test/java/com/github/skjolber/packing/iterator/StackableItemPermutationRotationIteratorTest.java b/core/src/test/java/com/github/skjolber/packing/iterator/StackableItemPermutationRotationIteratorTest.java new file mode 100644 index 00000000..b634d299 --- /dev/null +++ b/core/src/test/java/com/github/skjolber/packing/iterator/StackableItemPermutationRotationIteratorTest.java @@ -0,0 +1,12 @@ +package com.github.skjolber.packing.iterator; + +import com.github.skjolber.packing.iterator.DefaultStackableItemPermutationRotationIterator.Builder; + +class StackableItemPermutationRotationIteratorTest extends AbstractStackableItemPermutationRotationIteratorTest { + + @Override + public Builder newBuilder() { + return DefaultStackableItemPermutationRotationIterator.newBuilder(); + } + +}