diff --git a/src/main/java/io/github/arrayv/dialogs/ShuffleDialog.java b/src/main/java/io/github/arrayv/dialogs/ShuffleDialog.java index e0da3ac8..397fbaed 100644 --- a/src/main/java/io/github/arrayv/dialogs/ShuffleDialog.java +++ b/src/main/java/io/github/arrayv/dialogs/ShuffleDialog.java @@ -72,6 +72,7 @@ public ShuffleDialog(ArrayManager arrayManager, JFrame frame) { bypassEvents = true; this.shuffleEditor.setShuffle(arrayManager.getShuffle()); + jList4.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); jList4.setListData(arrayManager.getDistributionIDs()); for (int i = 0; i < arrayManager.getDistributions().length; i++) { if (arrayManager.getDistribution().equals(arrayManager.getDistributions()[i])) { @@ -81,13 +82,16 @@ public ShuffleDialog(ArrayManager arrayManager, JFrame frame) { } distributions = Arrays.stream(arrayManager.getDistributions()) - .filter(dist -> !dist.getName().equals("Custom")) + .filter(dist -> dist != Distributions.CUSTOM) .collect(Collectors.toList()); Object[] distributionNames = distributions.stream() .map(Distributions::getName).toArray(); + jList1.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); jList1.setListData(distributionNames); + jList3.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); jList3.setListData(distributionNames); + jList2.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); jList2.setListData(arrayManager.getShuffleIDs()); jTextField1.setText(Double.toString( @@ -376,17 +380,29 @@ private void addToGraph(ShuffleInfo shuffle) { shuffleEditor.getShuffle().addDisconnected(shuffle, safePos.x, safePos.y); } + private int jList4PrevSelection; + + // Base Distribution private void jList4ValueChanged() {//GEN-FIRST:event_jList1ValueChanged - if (bypassEvents) + if (bypassEvents || jList4.getValueIsAdjusting()) return; int selection = jList4.getSelectedIndex(); Distributions[] distributions = arrayManager.getDistributions(); - if (selection >= 0 && selection < distributions.length) - arrayManager.setDistribution(distributions[selection]); + if (selection >= 0 && selection < distributions.length) { + if (arrayManager.setDistribution(distributions[selection])) { + jList4PrevSelection = selection; + } else { + // Selection failed for whatever reason. Need to revert to the previous selection. + bypassEvents = true; + jList4.setSelectedIndex(jList4PrevSelection); + bypassEvents = false; + } + } }//GEN-LAST:event_jList1ValueChanged + // Distribution Stretch private void jList1ValueChanged() {//GEN-FIRST:event_jList1ValueChanged - if (bypassEvents) + if (bypassEvents || jList1.getValueIsAdjusting()) return; String selection = (String)jList1.getSelectedValue(); distributions.stream() @@ -398,8 +414,9 @@ private void jList1ValueChanged() {//GEN-FIRST:event_jList1ValueChanged bypassEvents = false; }//GEN-LAST:event_jList1ValueChanged + // Distribution Warp private void jList3ValueChanged() {//GEN-FIRST:event_jList1ValueChanged - if (bypassEvents) + if (bypassEvents || jList3.getValueIsAdjusting()) return; String selection = (String)jList3.getSelectedValue(); distributions.stream() @@ -411,8 +428,9 @@ private void jList3ValueChanged() {//GEN-FIRST:event_jList1ValueChanged bypassEvents = false; }//GEN-LAST:event_jList1ValueChanged + // Shuffle private void jList2ValueChanged() {//GEN-FIRST:event_jList1ValueChanged - if (bypassEvents) + if (bypassEvents || jList2.getValueIsAdjusting()) return; int selection = jList2.getSelectedIndex(); Shuffles[] shuffles = arrayManager.getShuffles(); diff --git a/src/main/java/io/github/arrayv/main/ArrayManager.java b/src/main/java/io/github/arrayv/main/ArrayManager.java index 83415c1c..3cdb2d45 100644 --- a/src/main/java/io/github/arrayv/main/ArrayManager.java +++ b/src/main/java/io/github/arrayv/main/ArrayManager.java @@ -153,11 +153,14 @@ public Distributions[] getDistributions() { public Distributions getDistribution() { return this.distribution; } - public void setDistribution(Distributions choice) { - this.distribution = choice; - this.distribution.selectDistribution(arrayVisualizer.getArray(), arrayVisualizer); - if (!arrayVisualizer.isActive()) - this.initializeArray(arrayVisualizer.getArray()); + public boolean setDistribution(Distributions choice) { + if (choice.selectDistribution(arrayVisualizer.getArray(), arrayVisualizer)) { + this.distribution = choice; + if (!arrayVisualizer.isActive()) + this.initializeArray(arrayVisualizer.getArray()); + return true; + } + return false; } public boolean containsShuffle(Shuffles shuffle) { diff --git a/src/main/java/io/github/arrayv/panes/JEnhancedOptionPane.java b/src/main/java/io/github/arrayv/panes/JEnhancedOptionPane.java index e1f58bc6..f514ee24 100644 --- a/src/main/java/io/github/arrayv/panes/JEnhancedOptionPane.java +++ b/src/main/java/io/github/arrayv/panes/JEnhancedOptionPane.java @@ -8,6 +8,16 @@ public final class JEnhancedOptionPane extends JOptionPane { private static final long serialVersionUID = 1L; + /** + * Prompts the user with a textbox and returns their answer, or null if they didn't confirm. + * The buttons are customizable, but option 0 is always defined as the Confirm action. + * + * @param title Title bar + * @param message Prompt message + * @param options Buttons to be clicked. You usually just want to pass a String[] of labels here, e.g. ["OK", "Cancel"]. + * + * @return The user input, or null if they closed out or picked a secondary option. + */ public static String showInputDialog(final String title, final Object message, final Object[] options) throws HeadlessException { final JOptionPane pane = new JOptionPane(message, QUESTION_MESSAGE, @@ -17,10 +27,14 @@ public static String showInputDialog(final String title, final Object message, f pane.setComponentOrientation((getRootFrame()).getComponentOrientation()); pane.setMessageType(QUESTION_MESSAGE); pane.selectInitialValue(); + final JDialog dialog = pane.createDialog(null, title); dialog.setVisible(true); dialog.dispose(); - final Object value = pane.getInputValue(); - return (value == UNINITIALIZED_VALUE) ? null : (String) value; + + final Object input = pane.getInputValue(); + final Object button = pane.getValue(); + + return button == options[0] ? (String) input : null; } } diff --git a/src/main/java/io/github/arrayv/prompts/ShufflePrompt.java b/src/main/java/io/github/arrayv/prompts/ShufflePrompt.java index bd4cbb52..42c56485 100644 --- a/src/main/java/io/github/arrayv/prompts/ShufflePrompt.java +++ b/src/main/java/io/github/arrayv/prompts/ShufflePrompt.java @@ -40,6 +40,7 @@ of this software and associated documentation files (the "Software"), to deal public final class ShufflePrompt extends javax.swing.JFrame implements AppFrame { private static final long serialVersionUID = 1L; + private static final String ADVANCED = "(Advanced)"; private final ArrayManager arrayManager; private final JFrame frame; @@ -62,6 +63,7 @@ public ShufflePrompt(ArrayManager arrayManager, JFrame frame, UtilFrame utilFram initComponents(); initializing = true; + jList1.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); jList1.setListData(arrayManager.getDistributionIDs()); for (int i = 0; i < arrayManager.getDistributions().length; i++) { if (arrayManager.getDistribution().equals(arrayManager.getDistributions()[i])) { @@ -70,10 +72,12 @@ public ShufflePrompt(ArrayManager arrayManager, JFrame frame, UtilFrame utilFram } } shuffleModel = new DefaultListModel<>(); + jList2.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); jList2.setModel(shuffleModel); Arrays.stream(arrayManager.getShuffleIDs()).forEach(shuffleModel::addElement); + // Add ghost option if advanced shuffle graph is active if (arrayManager.getShuffle().size() > 1) { - shuffleModel.add(0, "Advanced"); + shuffleModel.add(0, ADVANCED); jList2.setSelectedIndex(0); } else { for (int i = 0; i < arrayManager.getShuffles().length; i++) { @@ -83,7 +87,7 @@ public ShufflePrompt(ArrayManager arrayManager, JFrame frame, UtilFrame utilFram } } if (jList2.getSelectedIndex() == -1) { - shuffleModel.add(0, "Advanced"); + shuffleModel.add(0, ADVANCED); jList2.setSelectedIndex(0); } } @@ -205,20 +209,31 @@ private void jButton1ActionPerformed() {//GEN-FIRST:event_jList1ValueChanged new ShuffleDialog(arrayManager, this); }//GEN-LAST:event_jList1ValueChanged + private int jList1PrevSelection; + private void jList1ValueChanged() {//GEN-FIRST:event_jList1ValueChanged - if (initializing) + if (initializing || jList1.getValueIsAdjusting()) return; int selection = jList1.getSelectedIndex(); Distributions[] distributions = arrayManager.getDistributions(); - if (selection >= 0 && selection < distributions.length) - arrayManager.setDistribution(distributions[selection]); + if (selection >= 0 && selection < distributions.length) { + if (arrayManager.setDistribution(distributions[selection])) { + jList1PrevSelection = selection; + } else { + // Selection failed for whatever reason. Need to revert to the previous selection. + initializing = true; + jList1.setSelectedIndex(jList1PrevSelection); + initializing = false; + } + } }//GEN-LAST:event_jList1ValueChanged private void jList2ValueChanged() {//GEN-FIRST:event_jList1ValueChanged - if (initializing) + if (initializing || jList2.getValueIsAdjusting()) return; int selection = jList2.getSelectedIndex(); - if (shuffleModel.getElementAt(0).equals("Advanced")) { + // Remove ghost option if something else has been selected + if (shuffleModel.getElementAt(0).equals(ADVANCED)) { if (selection == 0) return; shuffleModel.remove(0); selection--; diff --git a/src/main/java/io/github/arrayv/sortdata/SortMeta.java b/src/main/java/io/github/arrayv/sortdata/SortMeta.java index 1ff6632e..75895214 100644 --- a/src/main/java/io/github/arrayv/sortdata/SortMeta.java +++ b/src/main/java/io/github/arrayv/sortdata/SortMeta.java @@ -79,15 +79,16 @@ boolean bucketSort() default false; /** - * A question to ask the user when they choose this sort. You can perform response validation by creating a method - * that is {@code public static int validateAnswer(int answer)}. + * If specified, a prompt for the runSort {@code param} will pop up when this sort is selected. You can perform + * response validation by creating a method with the following signature in your sort class: + * {@code public static int validateAnswer(int answer)}. It will get called automagically. * @return The question to ask the user, or {@code ""} if there isn't one. */ String question() default ""; /** - * The default response to use for {@link #question()}. This is used when the user pressed "Use default". This - * value is ignored if there is no question. This value is not passed through {@code validatorAnswer}. + * The default response to use for {@link #question()}. This is used when the user presses "Use default". This + * value is ignored if there is no question. This value is not passed through {@code validateAnswer}. * @return The default answer response. */ int defaultAnswer() default 0; diff --git a/src/main/java/io/github/arrayv/sorts/cabin/Java14Sort.java b/src/main/java/io/github/arrayv/sorts/cabin/Java14Sort.java new file mode 100644 index 00000000..deab103b --- /dev/null +++ b/src/main/java/io/github/arrayv/sorts/cabin/Java14Sort.java @@ -0,0 +1,893 @@ +package io.github.arrayv.sorts.cabin; + +import io.github.arrayv.main.ArrayVisualizer; +import io.github.arrayv.sortdata.SortMeta; +import io.github.arrayv.sorts.templates.Sort; + +import java.util.Arrays; +import java.util.Optional; + +/** + * JDK 14's dual-pivot Quicksort, which I've painstakingly adapted line by line from DualPivotQuicksort.java to call + * into the ArrayV hooks. Although I've gutted all that parallel merge stuff, because it essentially calls into the main + * algorithm on smaller segments in parallel, and merges them back in parallel. I'd rather just watch the main algorithm. + * While this parallel merge piece is ostensibly what sets JDK 14's sort apart from its predecessors, there are still a + * few little optimizations compared to JDK 11, including pivot selection and a new heapsort fallback (which I haven't + * been able to trigger in ArrayV without changing the tuning constants). + *
+ * Unfortunately, this janked out copypasta is the only way to observe the standard sort. I thought of adding hooks into + * the ArrayV code in a List implementor, but the collections framework actually dumps the List into an array and calls + * Arrays::sort when you call List::sort. Plus, it uses a whole different algorithm for Comparables as opposed + * to primitives. + *
+ * Overview: + *
+ * The algorithm is an Introsort variant at heart, but with so many safeguards, it's basically invincible. + *
+ * The core algorithm is a dual-pivot Quicksort. It selects the pivots using a weird median of 5 thing which is based + * on the golden ratio, because of course it is. Safeguard #1: If any two of them are equal, it switches to a + * single-pivot Quicksort for that range. + *
+ * Safeguard #2: Before trying Quicksort, it tries to find and merge runs. A run is defined as an ascending, + * descending, or constant sequence of values (a constant sequence could technically be considered ascending and + * descending, but it is handled slightly differently here). A descending sequence is reversed on the spot. Then, if + * all the sequences are long enough, it will attempt to do an N-way merge on them. Otherwise, it will leave them alone + * and defer to the core Quicksort loop. + *
+ * Safeguard #3: If the recursion delves too greedily and too deep, it will call Heapsort on that range. This is, + * of course, a classic Introsort optimization. Interestingly, they set a huge depth on it, likely impossible to + * reach without millions of elements. + *
+ * Safeguard #4: If called on a small enough range, it will call insertion sort. Another classic Introsort + * optimization. But there are two versions of it... one is a regular insertion sort like you're used to. The other is a + * so-called "mixed" insertion sort, which uses the pivot to do some sort of double-ended thing that helps cut down on + * swaps. I find this fascinating. + *
+ * Suggested settings: + *
+ * Markers #1 and #2 are used by the Reads and Writes methods, so you might want to start at #3 if you're invoking + * this method yourself. + * + * @param marker Marker ID + * @param markPosition Index into the array that should be marked + */ public synchronized void markArray(int marker, int markPosition) { - try { - if (markPosition < 0) { - if (markPosition == -1) throw new Exception("Highlights.markArray(): Invalid position! -1 is reserved for the clearMark method."); - else if (markPosition == -5) throw new Exception("Highlights.markArray(): Invalid position! -5 was the constant originally used to unmark numbers in the array. Instead, use the clearMark method."); - else throw new Exception("Highlights.markArray(): Invalid position!"); - } else { - if (highlights[marker] == markPosition) { - return; - } - Delays.disableStepping(); - if (highlights[marker] != -1) { - decrementIndexMarkCount(highlights[marker]); - } - highlights[marker] = markPosition; - incrementIndexMarkCount(markPosition); + if (markPosition < 0 || markPosition >= arrayVisualizer.getCurrentLength()) { + if (markPosition == -1) throw new IndexOutOfBoundsException("Highlights.markArray(): Invalid position! -1 is reserved for the clearMark method."); + else if (markPosition == -5) throw new IndexOutOfBoundsException("Highlights.markArray(): Invalid position! -5 was the constant originally used to unmark numbers in the array. Instead, use the clearMark method."); + else throw new IndexOutOfBoundsException("Highlights.markArray(): Invalid position " + markPosition + "!"); + } else { + if (highlights[marker] == markPosition) { + return; + } + Delays.disableStepping(); + if (highlights[marker] != -1) { + decrementIndexMarkCount(highlights[marker]); + } + highlights[marker] = markPosition; + incrementIndexMarkCount(markPosition); - if (marker >= this.maxHighlightMarked) { - this.maxHighlightMarked = marker + 1; - } + if (marker >= this.maxHighlightMarked) { + this.maxHighlightMarked = marker + 1; } - } catch (Exception e) { - e.printStackTrace(); } arrayVisualizer.updateNow(); Delays.enableStepping(); diff --git a/src/main/java/io/github/arrayv/utils/Reads.java b/src/main/java/io/github/arrayv/utils/Reads.java index dc2e1f57..e5262de4 100644 --- a/src/main/java/io/github/arrayv/utils/Reads.java +++ b/src/main/java/io/github/arrayv/utils/Reads.java @@ -83,6 +83,13 @@ public void setComparisons(long value) { this.comparisons.set(value); } + /** + * Doesn't result in any visualizations, but counts for the number of comparisons stat. + * + * @param left + * @param right + * @return Integer.compare(left, right) + */ public int compareValues(int left, int right) { if (arrayVisualizer.sortCanceled()) throw new StopSort(); this.comparisons.incrementAndGet(); @@ -125,6 +132,17 @@ public int compareOriginalValues(int left, int right) { return cmpVal; } + /** + * Compare index to index. + * + * @param array + * @param left The first index to read from the array + * @param right The second index to read from the array + * @param sleep How many milliseconds to sleep (subject to Delays.sleepRatio) + * @param mark Whether to mark and delay for this comparison. + * + * @return Integer.compare(array[left], array[right]) + */ public int compareIndices(int[] array, int left, int right, double sleep, boolean mark) { if (mark) { Highlights.markArray(1, left); @@ -147,6 +165,17 @@ public int compareOriginalIndices(int[] array, int left, int right, double sleep return this.compareOriginalValues(array[left], array[right]); } + /** + * Compare index to value. Useful for comparing, say, the current pointer to the current minimum. + * + * @param array + * @param index The index to read from the array + * @param value A constant value + * @param sleep How many milliseconds to sleep (subject to Delays.sleepRatio) + * @param mark Whether to mark and delay for this comparison. + * + * @return Integer.compare(array[index], value) + */ public int compareIndexValue(int[] array, int index, int value, double sleep, boolean mark) { if (mark) { Highlights.markArray(1, index); @@ -163,6 +192,17 @@ public int compareOriginalIndexValue(int[] array, int index, int value, double s return this.compareOriginalValues(array[index], value); } + /** + * Compare value to index. Useful for comparing, say, the current minimum to the current pointer. + * + * @param array + * @param value A constant value + * @param index The index to read from the array + * @param sleep How many milliseconds to sleep (subject to Delays.sleepRatio) + * @param mark Whether to mark and delay for this comparison. + * + * @return Integer.compare(value, array[index]) + */ public int compareValueIndex(int[] array, int value, int index, double sleep, boolean mark) { if (mark) { Highlights.markArray(1, index); diff --git a/src/main/java/io/github/arrayv/utils/ShuffleInfo.java b/src/main/java/io/github/arrayv/utils/ShuffleInfo.java index 7451f7a0..42bcc2bd 100644 --- a/src/main/java/io/github/arrayv/utils/ShuffleInfo.java +++ b/src/main/java/io/github/arrayv/utils/ShuffleInfo.java @@ -115,10 +115,8 @@ public void shuffle(int[] array, ArrayVisualizer arrayVisualizer) { } } else { assert shuffle != null; - Delays Delays = arrayVisualizer.getDelays(); - Highlights Highlights = arrayVisualizer.getHighlights(); - Writes Writes = arrayVisualizer.getWrites(); - this.shuffle.shuffleArray(array, arrayVisualizer, Delays, Highlights, Writes); + shuffle.init(arrayVisualizer); + shuffle.shuffleArray(array, arrayVisualizer.getCurrentLength()); } } } diff --git a/src/main/java/io/github/arrayv/utils/Shuffles.java b/src/main/java/io/github/arrayv/utils/Shuffles.java index 0b87beb8..82534e81 100644 --- a/src/main/java/io/github/arrayv/utils/Shuffles.java +++ b/src/main/java/io/github/arrayv/utils/Shuffles.java @@ -48,10 +48,9 @@ public String getName() { return "Randomly"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); - shuffle(array, 0, currentLen, delay ? 1 : 0, Writes); + shuffle(array, 0, currentLen, delay ? 1 : 0); } }, REVERSE { @@ -59,8 +58,7 @@ public String getName() { return "Backwards"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); Writes.reversal(array, 0, currentLen-1, delay ? 1 : 0, true, false); } @@ -70,10 +68,7 @@ public String getName() { return "Slight Shuffle"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); - Random random = new Random(); - + public void shuffleArray(int[] array, int currentLen) { for (int i = 0; i < Math.max(currentLen / 20, 1); i++){ Writes.swap(array, random.nextInt(currentLen), random.nextInt(currentLen), 0, true, false); @@ -86,8 +81,7 @@ public String getName() { return "No Shuffle"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { for (int i = 0; i < currentLen; i++) { Highlights.markArray(1, i); if (arrayVisualizer.shuffleEnabled()) Delays.sleep(1); @@ -99,10 +93,9 @@ public String getName() { return "Sorted"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); - this.sort(array, 0, currentLen, delay ? 1 : 0, Writes); + this.sort(array, 0, currentLen, delay ? 1 : 0); } }, NAIVE { @@ -110,10 +103,8 @@ public String getName() { return "Naive Randomly"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); - Random random = new Random(); for (int i = 0; i < currentLen; i++) Writes.swap(array, i, random.nextInt(currentLen), delay ? 1 : 0, true, false); @@ -124,11 +115,9 @@ public String getName() { return "Scrambled Tail"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); - Random random = new Random(); int[] aux = new int[currentLen]; int i = 0, j = 0, k = 0; while (i < currentLen) { @@ -139,7 +128,7 @@ public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays De Writes.write(array, j++, array[i++], delay ? 1 : 0, true, false); } Writes.arraycopy(aux, 0, array, j, k, delay ? 1 : 0, true, false); - shuffle(array, j, currentLen, delay ? 2 : 0, Writes); + shuffle(array, j, currentLen, delay ? 2 : 0); } }, SHUFFLED_HEAD { @@ -147,11 +136,9 @@ public String getName() { return "Scrambled Head"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); - Random random = new Random(); int[] aux = new int[currentLen]; int i = currentLen - 1, j = currentLen - 1, k = 0; while (i >= 0) { @@ -162,7 +149,7 @@ public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays De Writes.write(array, j--, array[i--], delay ? 1 : 0, true, false); } Writes.arraycopy(aux, 0, array, 0, k, delay ? 1 : 0, true, false); - shuffle(array, 0, j, delay ? 2 : 0, Writes); + shuffle(array, 0, j, delay ? 2 : 0); } }, MOVED_ELEMENT { @@ -170,10 +157,8 @@ public String getName() { return "Shifted Element"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); - Random random = new Random(); int start = random.nextInt(currentLen); int dest = random.nextInt(currentLen); @@ -189,15 +174,13 @@ public String getName() { return "Noisy"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); - Random random = new Random(); int i, size = Math.max(4, (int)(Math.sqrt(currentLen)/2)); for (i = 0; i+size <= currentLen; i += random.nextInt(size-1)+1) - shuffle(array, i, i+size, delay ? 0.5 : 0, Writes); - shuffle(array, i, currentLen, delay ? 0.5 : 0, Writes); + shuffle(array, i, i+size, delay ? 0.5 : 0); + shuffle(array, i, currentLen, delay ? 0.5 : 0); } }, SHUFFLED_ODDS { @@ -205,10 +188,7 @@ public String getName() { return "Scrambled Odds"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); - Random random = new Random(); - + public void shuffleArray(int[] array, int currentLen) { for (int i = 1; i < currentLen; i += 2){ int randomIndex = (((random.nextInt(currentLen - i) / 2)) * 2) + i; Writes.swap(array, i, randomIndex, 0, true, false); @@ -222,8 +202,7 @@ public String getName() { return "Final Merge Pass"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); int count = 2; @@ -243,14 +222,13 @@ public String getName() { return "Shuffled Final Merge"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); - this.shuffle(array, 0, currentLen, delay ? 0.5 : 0, Writes); + this.shuffle(array, 0, currentLen, delay ? 0.5 : 0); Highlights.clearMark(2); - this.sort(array, 0, currentLen / 2, delay ? 0.5 : 0, Writes); - this.sort(array, currentLen / 2, currentLen, delay ? 0.5 : 0, Writes); + this.sort(array, 0, currentLen / 2, delay ? 0.5 : 0); + this.sort(array, currentLen / 2, currentLen, delay ? 0.5 : 0); } }, SHUFFLED_HALF { @@ -258,13 +236,12 @@ public String getName() { return "Shuffled Half"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); - this.shuffle(array, 0, currentLen, delay ? 2/3d : 0, Writes); + this.shuffle(array, 0, currentLen, delay ? 2/3d : 0); Highlights.clearMark(2); - this.sort(array, 0, currentLen / 2, delay ? 2/3d : 0, Writes); + this.sort(array, 0, currentLen / 2, delay ? 2/3d : 0); } }, PARTITIONED { @@ -272,14 +249,13 @@ public String getName() { return "Partitioned"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); - this.sort(array, 0, currentLen, delay ? 0.5 : 0, Writes); + this.sort(array, 0, currentLen, delay ? 0.5 : 0); Highlights.clearMark(2); - this.shuffle(array, 0, currentLen/2, delay ? 0.5 : 0, Writes); - this.shuffle(array, currentLen/2, currentLen, delay ? 0.5 : 0, Writes); + this.shuffle(array, 0, currentLen/2, delay ? 0.5 : 0); + this.shuffle(array, currentLen/2, currentLen, delay ? 0.5 : 0); } }, SAWTOOTH { @@ -287,8 +263,7 @@ public String getName() { return "Sawtooth"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); int count = 4; @@ -308,8 +283,7 @@ public String getName() { return "Pipe Organ"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); int[] temp = new int[currentLen]; @@ -329,8 +303,7 @@ public String getName() { return "Final Bitonic Pass"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); int[] temp = new int[currentLen]; @@ -352,8 +325,7 @@ public String getName() { return "Interlaced"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); int[] referenceArray = new int[currentLen]; @@ -376,9 +348,7 @@ public String getName() { return "Double Layered"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); - + public void shuffleArray(int[] array, int currentLen) { for (int i = 0; i < currentLen / 2; i += 2) { Writes.swap(array, i, currentLen - i - 1, 0, true, false); if (arrayVisualizer.shuffleEnabled()) Delays.sleep(1); @@ -390,8 +360,7 @@ public String getName() { return "Final Radix"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); currentLen -= currentLen % 2; @@ -412,9 +381,7 @@ public String getName() { return "Real Final Radix"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); - + public void shuffleArray(int[] array, int currentLen) { int mask = 0; for (int i = 0; i < currentLen; i++) while (mask < array[i]) mask = (mask << 1) + 1; @@ -440,14 +407,13 @@ public String getName() { return "Recursive Final Radix"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); - weaveRec(array, 0, currentLen, 1, delay ? 0.5 : 0, Writes); + weaveRec(array, 0, currentLen, 1, delay ? 0.5 : 0); } - public void weaveRec(int[] array, int pos, int length, int gap, double delay, Writes Writes) { + public void weaveRec(int[] array, int pos, int length, int gap, double delay) { if (length < 2) return; int mod2 = length % 2; @@ -463,8 +429,8 @@ public void weaveRec(int[] array, int pos, int length, int gap, double delay, Wr Writes.write(array, j+gap, temp[k], delay, true, false); } - weaveRec(array, pos, mid+mod2, 2*gap, delay/2, Writes); - weaveRec(array, pos+gap, mid, 2*gap, delay/2, Writes); + weaveRec(array, pos, mid+mod2, 2*gap, delay/2); + weaveRec(array, pos+gap, mid, 2*gap, delay/2); } }, HALF_ROTATION { @@ -472,8 +438,7 @@ public String getName() { return "Half Rotation"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); int a = 0, m = (currentLen + 1) / 2; @@ -496,8 +461,7 @@ public String getName() { return "Half Reversed"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); Writes.reversal(array, 0, currentLen-1, delay ? 1 : 0, true, false); @@ -509,8 +473,7 @@ public String getName() { return "BST Traversal"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { int[] temp = Arrays.copyOf(array, currentLen); // credit to sam walko/anon @@ -546,8 +509,7 @@ public String getName() { return "Inverted BST"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); int[] temp = new int[currentLen]; @@ -588,8 +550,7 @@ public String getName() { return "Logarithmic Slopes"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); int[] temp = new int[currentLen]; @@ -610,8 +571,7 @@ public String getName() { return "Heapified"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); MaxHeapSort heapSort = new MaxHeapSort(arrayVisualizer); @@ -623,9 +583,7 @@ public String getName() { return "Smoothified"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); - + public void shuffleArray(int[] array, int currentLen) { SmoothSort smoothSort = new SmoothSort(arrayVisualizer); smoothSort.smoothHeapify(array, currentLen); } @@ -635,9 +593,7 @@ public String getName() { return "Poplarified"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); - + public void shuffleArray(int[] array, int currentLen) { PoplarHeapSort poplarHeapSort = new PoplarHeapSort(arrayVisualizer); poplarHeapSort.poplarHeapify(array, 0, currentLen); } @@ -647,8 +603,7 @@ public String getName() { return "Triangular Heapified"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); if (delay) Delays.setSleepRatio(Delays.getSleepRatio()*10); @@ -666,21 +621,19 @@ public String getName() { return "First Circle Pass"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); - Reads Reads = arrayVisualizer.getReads(); - shuffle(array, 0, currentLen, delay ? 0.5 : 0, Writes); + shuffle(array, 0, currentLen, delay ? 0.5 : 0); int n = 1; //noinspection StatementWithEmptyBody for (; n < currentLen; n*=2); - circleSortRoutine(array, 0, n-1, currentLen, delay ? 0.5 : 0, Reads, Writes); + circleSortRoutine(array, 0, n-1, currentLen, delay ? 0.5 : 0); } - public void circleSortRoutine(int[] array, int lo, int hi, int end, double sleep, Reads Reads, Writes Writes) { + public void circleSortRoutine(int[] array, int lo, int hi, int end, double sleep) { if (lo == hi) return; int high = hi; @@ -695,8 +648,8 @@ public void circleSortRoutine(int[] array, int lo, int hi, int end, double sleep hi--; } - circleSortRoutine(array, low, low + mid, end, sleep/2, Reads, Writes); - if (low + mid + 1 < end) circleSortRoutine(array, low + mid + 1, high, end, sleep/2, Reads, Writes); + circleSortRoutine(array, low, low + mid, end, sleep/2); + if (low + mid + 1 < end) circleSortRoutine(array, low + mid + 1, high, end, sleep/2); } }, PAIRWISE { @@ -704,12 +657,11 @@ public String getName() { return "Final Pairwise Pass"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); Reads Reads = arrayVisualizer.getReads(); - shuffle(array, 0, currentLen, delay ? 0.5 : 0, Writes); + shuffle(array, 0, currentLen, delay ? 0.5 : 0); //create pairs for (int i = 1; i < currentLen; i+=2) @@ -743,21 +695,20 @@ public String getName() { return "Recursive Reversal"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); - reversalRec(array, 0, currentLen, delay ? 1 : 0, Writes); + reversalRec(array, 0, currentLen, delay ? 1 : 0); } - public void reversalRec(int[] array, int a, int b, double sleep, Writes Writes) { + public void reversalRec(int[] array, int a, int b, double sleep) { if (b-a < 2) return; Writes.reversal(array, a, b-1, sleep, true, false); int m = (a+b)/2; - this.reversalRec(array, a, m, sleep/2, Writes); - this.reversalRec(array, m, b, sleep/2, Writes); + this.reversalRec(array, a, m, sleep/2); + this.reversalRec(array, m, b, sleep/2); } }, GRAY_CODE { @@ -765,14 +716,13 @@ public String getName() { return "Gray Code Fractal"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); - reversalRec(array, 0, currentLen, false, delay ? 1 : 0, Writes); + reversalRec(array, 0, currentLen, false, delay ? 1 : 0); } - public void reversalRec(int[] array, int a, int b, boolean bw, double sleep, Writes Writes) { + public void reversalRec(int[] array, int a, int b, boolean bw, double sleep) { if (b-a < 3) return; int m = (a+b)/2; @@ -780,8 +730,8 @@ public void reversalRec(int[] array, int a, int b, boolean bw, double sleep, Wri if (bw) Writes.reversal(array, a, m-1, sleep, true, false); else Writes.reversal(array, m, b-1, sleep, true, false); - this.reversalRec(array, a, m, false, sleep/2, Writes); - this.reversalRec(array, m, b, true, sleep/2, Writes); + this.reversalRec(array, a, m, false, sleep/2); + this.reversalRec(array, m, b, true, sleep/2); } }, SIERPINSKI { @@ -789,8 +739,7 @@ public String getName() { return "Sierpinski Triangle"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { int[] triangle = new int[currentLen]; triangleRec(triangle, 0, currentLen); @@ -820,8 +769,7 @@ public String getName() { return "Triangular"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); int[] triangle = new int[currentLen]; @@ -857,8 +805,7 @@ public String getName() { return "Quicksort Adversary"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); for (int j = currentLen-currentLen%2-2, i = j-1; i >= 0; i-=2, j--) @@ -866,9 +813,6 @@ public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays De } }, PDQ_BAD { - Reads Reads; - Writes Writes; - Highlights Highlights; boolean delay; double sleep; @@ -898,13 +842,9 @@ public String getName() { return "PDQ Adversary"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { delay = arrayVisualizer.shuffleEnabled(); sleep = delay ? 1 : 0; - this.Reads = arrayVisualizer.getReads(); - this.Writes = Writes; - this.Highlights = Highlights; int[] copy = new int[currentLen]; @@ -918,7 +858,7 @@ public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays De Writes.write(temp, i, gas, sleep, true, true); } - pdqLoop(array, 0, currentLen, false, PDQSorting.pdqLog(currentLen)); + pdqLoop(array, 0, currentLen, PDQSorting.pdqLog(currentLen)); for (int i = 0; i < currentLen; i++) { Writes.write(array, i, copy[temp[i] - 1], sleep, true, false); @@ -955,7 +895,7 @@ protected int compare(int ap, int bp) { return Integer.compare(temp[a], temp[b]); } - protected void pdqLoop(int[] array, int begin, int end, boolean branchless, int badAllowed) { + protected void pdqLoop(int[] array, int begin, int end, int badAllowed) { boolean leftmost = true; while (true) { @@ -1030,7 +970,7 @@ && pdqPartialInsertSort(array, pivotPos + 1, end)) return; } - this.pdqLoop(array, begin, pivotPos, branchless, badAllowed); + this.pdqLoop(array, begin, pivotPos, badAllowed); begin = pivotPos + 1; leftmost = false; } @@ -1050,7 +990,7 @@ private void siftDown(int[] array, int root, int dist, int start, double sleep, Highlights.markArray(1, start + root - 1); Highlights.markArray(2, start + leaf - 1); if (compare(array[start + root - 1], array[start + leaf - 1]) == compareVal) { - Writes.swap(array, start + root - 1, start + leaf - 1, 0, true, false); + Writes.swap(array, start + root - 1, start + leaf - 1, sleep, true, false); root = leaf; } else break; } @@ -1215,8 +1155,7 @@ public String getName() { return "Grailsort Adversary"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { boolean delay = arrayVisualizer.shuffleEnabled(); if (currentLen <= 16) Writes.reversal(array, 0, currentLen-1, delay ? 1 : 0, true, false); @@ -1227,23 +1166,23 @@ public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays De int numKeys = (currentLen - 1) / blockLen + 1; int keys = blockLen + numKeys; - shuffle(array, 0, currentLen, delay ? 0.25 : 0, Writes); - sort(array, 0, keys, delay ? 0.25 : 0, Writes); + shuffle(array, 0, currentLen, delay ? 0.25 : 0); + sort(array, 0, keys, delay ? 0.25 : 0); Writes.reversal(array, 0, keys-1, delay ? 0.25 : 0, true, false); Highlights.clearMark(2); - sort(array, keys, currentLen, delay ? 0.25 : 0, Writes); + sort(array, keys, currentLen, delay ? 0.25 : 0); - push(array, keys, currentLen, blockLen, delay ? 0.25 : 0, Writes); + push(array, keys, currentLen, blockLen, delay ? 0.25 : 0); } } - public void rotate(int[] array, int a, int m, int b, double sleep, Writes Writes) { + public void rotate(int[] array, int a, int m, int b, double sleep) { Writes.reversal(array, a, m-1, sleep, true, false); Writes.reversal(array, m, b-1, sleep, true, false); Writes.reversal(array, a, b-1, sleep, true, false); } - public void push(int[] array, int a, int b, int bLen, double sleep, Writes Writes) { + public void push(int[] array, int a, int b, int bLen, double sleep) { int len = b-a, b1 = b - len%bLen, len1 = b1-a; if (len1 <= 2*bLen) return; @@ -1252,16 +1191,16 @@ public void push(int[] array, int a, int b, int bLen, double sleep, Writes Write while (2*m < len) m *= 2; m += a; - if (b1-m < bLen) push(array, a, m, bLen, sleep, Writes); + if (b1-m < bLen) push(array, a, m, bLen, sleep); else { m = a+b1-m; - rotate(array, m-(bLen-2), b1-(bLen-1), b1, sleep, Writes); + rotate(array, m-(bLen-2), b1-(bLen-1), b1, sleep); Writes.multiSwap(array, a, m, sleep/2, true, false); - rotate(array, a, m, b1, sleep, Writes); + rotate(array, a, m, b1, sleep); m = a+b1-m; - push(array, a, m, bLen, sleep/2, Writes); - push(array, m, b, bLen, sleep/2, Writes); + push(array, a, m, bLen, sleep/2); + push(array, m, b, bLen, sleep/2); } } }, @@ -1270,8 +1209,7 @@ public String getName() { return "Shuffle Merge Adversary"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int n = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int n) { boolean delay = arrayVisualizer.shuffleEnabled(); int[] tmp = new int[n]; @@ -1294,23 +1232,23 @@ public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays De dec -= d; k++; } - shuffleMergeBad(array, tmp, i, j, k, delay ? sleep : 0, Writes); + shuffleMergeBad(array, tmp, i, j, k, delay ? sleep : 0); i = k; } d *= 2; } } - public void shuffleMergeBad(int[] array, int[] tmp, int a, int m, int b, double sleep, Writes Writes) { + public void shuffleMergeBad(int[] array, int[] tmp, int a, int m, int b, double sleep) { if ((b-a)%2 == 1) { if (m-a > b-m) a++; else b--; } - shuffleBad(array, tmp, a, b, sleep, Writes); + shuffleBad(array, tmp, a, b, sleep); } //length is always even - public void shuffleBad(int[] array, int[] tmp, int a, int b, double sleep, Writes Writes) { + public void shuffleBad(int[] array, int[] tmp, int a, int b, double sleep) { if (b-a < 2) return; int m = (a+b)/2; @@ -1334,9 +1272,8 @@ public String getName() { return "Bit Reversal"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); - int len = 1 << (int)(Math.log(arrayVisualizer.getCurrentLength())/Math.log(2)); + public void shuffleArray(int[] array, int currentLen) { + int len = 1 << (int)(Math.log(currentLen)/Math.log(2)); boolean delay = arrayVisualizer.shuffleEnabled(); boolean pow2 = len == currentLen; @@ -1387,21 +1324,19 @@ public String getName() { return "Randomly w/ Blocks"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { int blockSize = pow2lte((int)Math.sqrt(currentLen)); currentLen -= currentLen%blockSize; boolean delay = arrayVisualizer.shuffleEnabled(); double sleep = delay ? 1 : 0; - Random random = new Random(); for (int i = 0; i < currentLen; i += blockSize) { int randomIndex = random.nextInt((currentLen - i) / blockSize) * blockSize + i; - blockSwap(array, i, randomIndex, blockSize, Writes, sleep); + blockSwap(array, i, randomIndex, blockSize, sleep); } } - private void blockSwap(int[] array, int a, int b, int len, Writes Writes, double sleep) { + private void blockSwap(int[] array, int a, int b, int len, double sleep) { for (int i = 0; i < len; i++) { Writes.swap(array, a + i, b + i, sleep, true, false); } @@ -1420,8 +1355,7 @@ public String getName() { return "Block Reverse"; } @Override - public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) { - int currentLen = arrayVisualizer.getCurrentLength(); + public void shuffleArray(int[] array, int currentLen) { int blockSize = pow2lte((int)Math.sqrt(currentLen)); currentLen -= currentLen % blockSize; boolean delay = arrayVisualizer.shuffleEnabled(); @@ -1429,13 +1363,13 @@ public void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays De int i = 0, j = currentLen - blockSize; while (i < j) { - blockSwap(array, i, j, blockSize, Writes, sleep); + blockSwap(array, i, j, blockSize, sleep); i += blockSize; j -= blockSize; } } - private void blockSwap(int[] array, int a, int b, int len, Writes Writes, double sleep) { + private void blockSwap(int[] array, int a, int b, int len, double sleep) { for (int i = 0; i < len; i++) { Writes.swap(array, a + i, b + i, sleep, true, false); } @@ -1447,9 +1381,50 @@ private int pow2lte(int value) { for (val = 1; val <= value; val <<= 1); return val >> 1; } + }, + SMB3 { + @Override + public String getName() { + return "SMB3 Matching Game"; + } + @Override + public void shuffleArray(int[] array, int currentLen) { + // Multiply length by 5/6 to simulate that bottom right corner easter egg + smb3Shuffle(array, (int) (currentLen * 5.0 / 6.0), false, + arrayVisualizer.shuffleEnabled() ? 0.5 : 0); + } + }, + SMB3_FIXED { + @Override + public String getName() { + return "SMB3 Matching Game (Fixed)"; + } + @Override + public void shuffleArray(int[] array, int currentLen) { + // Multiply length by 5/6 to simulate that bottom right corner easter egg + smb3Shuffle(array, (int) (currentLen * 5.0 / 6.0), true, + arrayVisualizer.shuffleEnabled() ? 0.5 : 0); + } }; - public void sort(int[] array, int start, int end, double sleep, Writes Writes) { + protected ArrayVisualizer arrayVisualizer; + protected Delays Delays; + protected Highlights Highlights; + protected Writes Writes; + protected Reads Reads; + + protected Random random = new Random(); + + public final void init(ArrayVisualizer arrayVisualizer) { + this.arrayVisualizer = arrayVisualizer; + Delays = arrayVisualizer.getDelays(); + Highlights = arrayVisualizer.getHighlights(); + Writes = arrayVisualizer.getWrites(); + Reads = arrayVisualizer.getReads(); + } + + // counting sort + public void sort(int[] array, int start, int end, double sleep) { int min = array[start], max = min; for (int i = start+1; i < end; i++) { if (array[i] < min) min = array[i]; @@ -1471,14 +1446,104 @@ public void sort(int[] array, int start, int end, double sleep, Writes Writes) { } } - public void shuffle(int[] array, int start, int end, double sleep, Writes Writes) { - Random random = new Random(); + public void shuffle(int[] array, int start, int end, double sleep) { for (int i = start; i < end; i++){ int randomIndex = random.nextInt(end - i) + i; Writes.swap(array, i, randomIndex, sleep, true, false); } } + /** + * Did you know the card matching minigame in Super Mario Bros. 3 only has 8 possible layouts? It's true! + * But did you know that's not an intentional feature, but the result of an + * incredibly sloppy shuffle routine? + *
+ * This method emulates that shuffle, with the option to fix or preserve the bugs that resulted in only 8 outcomes. + * It was only ever designed to operate on a list of exactly 15 elements, but I've adapted it to work for any length. + * + * @param array + * @param length + * @param fixed True to fix the bugs and allow for more possibilities. False for a more accurate experience with + * only 8 possible outcomes. + * @param sleep Sleep amount for visualizations + */ + protected void smb3Shuffle(int[] array, int length, boolean fixed, double sleep) { + + // There were always 3 loops of the main shuffle algorithm, + // which did a rotation and a sequence of "triple swaps." + for (int numShuffles = 0; numShuffles < 3; numShuffles++) { + + int numRotates = fixed + ? random.nextInt(length) // It was supposed to be any number from 0 to 14, + : random.nextInt(2) * 2 + 1; // But with bugs, it was always 1 or 3 + // It's supposed to be a rightRotate, but all I have is this leftRotate function, so numRotates needs to be + // adapted. It doesn't really matter in the fixed version, but it does for the broken version. + leftRotate(array, length, length - numRotates, sleep); + + if (length <= 10) return; + + int x = fixed + ? random.nextInt(length - 10) // It had this weird loop that did "triple swaps" down from a random starting point, + : 0; // But with bugs, it always started at 0. + for (; x >= 0; x -= 2) { + // Triple swap + Writes.swap(array, x, x + 5, sleep, true, false); + Writes.swap(array, x + 5, x + 10, sleep, true, false); + } + } + + } + + // TODO: HALF_ROTATE may be interested in this function. + /** + * Does a left rotation with a minimal number of swaps and O(1) extra memory, based on the observation that a + * rotation can be broken up into one or more independent cycles of swaps. The number of cycles is gcd(length, rotation). + * If you don't do it this way, you end up stumbling over yourself and overwriting values that aren't meant to be + * written yet. + *
+ * Adapted from Geeksforgeeks. I had to fix it up a + * little cause those comments were stanky. + *
+ * Examples: + *
+ * Length = 9, rotation = 2:
+ * 0 -> 2 -> 4 -> 6 -> 8 -> 1 -> 3 -> 5 -> 7 -> 0
+ *
+ * Length = 10, rotation = 2:
+ * 0 -> 2 -> 4 -> 6 -> 8 -> 0
+ * 1 -> 3 -> 5 -> 7 -> 9 -> 1
+ *
+ * @param array
+ * @param length
+ * @param rotation Rotation amount
+ * @param sleep Sleep amount for visualizations
+ */
+ protected void leftRotate(int[] array, int length, int rotation, double sleep) {
+ rotation %= length; // To handle if rotation >= length
+ int cycle, j, k, temp;
+ int gcd = gcd(rotation, length);
+ for (cycle = 0; cycle < gcd; cycle++) {
+ // Rotate cycle
+ temp = array[cycle];
+ j = cycle;
+ while (true) {
+ k = (j + rotation) % length;
+ if (k == cycle)
+ break;
+ Writes.write(array, j, array[k], sleep, true, false);
+ j = k;
+ }
+ Writes.write(array, j, temp, sleep, true, false);
+ }
+ }
+
+ protected int gcd(int a, int b) {
+ return b == 0
+ ? a
+ : gcd(b, a % b);
+ }
+
+ // TODO: display while shuffling
public abstract String getName();
- public abstract void shuffleArray(int[] array, ArrayVisualizer arrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes);
+ public abstract void shuffleArray(int[] array, int currentLen);
}
diff --git a/src/main/java/io/github/arrayv/utils/Writes.java b/src/main/java/io/github/arrayv/utils/Writes.java
index 7b0aa6c4..1e9e8671 100644
--- a/src/main/java/io/github/arrayv/utils/Writes.java
+++ b/src/main/java/io/github/arrayv/utils/Writes.java
@@ -156,6 +156,16 @@ private void markSwap(int a, int b) {
Highlights.markArray(2, b);
}
+ /**
+ * Swaps two values in the array.
+ *
+ * @param array
+ * @param a The first index
+ * @param b The second index
+ * @param pause How many milliseconds to sleep (subject to Delays.sleepRatio)
+ * @param mark Whether or not to mark this swap in the visualization
+ * @param auxwrite Whether the given array reference is an auxiliary array or the main subject being sorted.
+ */
public void swap(int[] array, int a, int b, double pause, boolean mark, boolean auxwrite) {
if (arrayVisualizer.sortCanceled()) throw new StopSort();
if (!auxwrite && a >= arrayVisualizer.getCurrentLength()) {
@@ -180,6 +190,18 @@ public void swap(int[] array, int a, int b, double pause, boolean mark, boolean
Delays.sleep(pause);
}
+ /**
+ * Does a rotation on the given range.
+ * If pos < to, it's a left rotate ([1,2,3,4,5] -> [2,3,4,5,1]).
+ * If pos > to, it's a right rotate ([1,2,3,4,5] -> [5,1,2,3,4,5]).
+ *
+ * @param array
+ * @param pos Start pos for the rotation
+ * @param to End pos for the rotation. Can be less than pos.
+ * @param sleep How many milliseconds to sleep (subject to Delays.sleepRatio)
+ * @param mark Whether or not to mark this swap in the visualization
+ * @param auxwrite Whether the given array reference is an auxiliary array or the main subject being sorted.
+ */
public void multiSwap(int[] array, int pos, int to, double sleep, boolean mark, boolean auxwrite) {
if (to - pos > 0) {
for (int i = pos; i < to; i++) {
@@ -385,6 +407,7 @@ public void arraycopy(int[] src, int srcPos, int[] dest, int destPos, int length
public int[] copyOfArray(int[] original, int newLength) {
this.allocAmount.addAndGet(newLength);
+ changeAuxWrites(newLength);
int[] result = Arrays.copyOf(original, newLength);
arrayVisualizer.getArrays().add(result);
arrayVisualizer.updateNow();
@@ -393,6 +416,7 @@ public int[] copyOfArray(int[] original, int newLength) {
public int[] copyOfRangeArray(int[] original, int from, int to) {
this.allocAmount.addAndGet(to - from);
+ changeAuxWrites(to - from);
int[] result = Arrays.copyOfRange(original, from, to);
arrayVisualizer.getArrays().add(result);
arrayVisualizer.updateNow();
@@ -417,6 +441,10 @@ public ArrayVList createArrayList(int defaultCapacity) {
return new ArrayVList(defaultCapacity);
}
+ /**
+ * Reserves an external array for auxiliary computations (e.g. scratch space for merge sort) and registers it to the
+ * visualizer. Make sure to call {@link Writes#deleteExternalArray(int[])} when you're done with it.
+ */
public int[] createExternalArray(int length) {
this.allocAmount.addAndGet(length);
int[] result = new int[length];
@@ -425,16 +453,23 @@ public int[] createExternalArray(int length) {
return result;
}
+ /**
+ * Deletes a registered auxiliary array from the visualization.
+ */
public void deleteExternalArray(int[] array) {
- this.allocAmount.addAndGet(-array.length);
- arrayVisualizer.getArrays().remove(array);
- arrayVisualizer.updateNow();
+ if (arrayVisualizer.getArrays().remove(array)) {
+ this.allocAmount.addAndGet(-array.length);
+ arrayVisualizer.updateNow();
+ }
}
public void deleteExternalArrays(int[]... arrays) {
- this.allocAmount.addAndGet(-Arrays.stream(arrays).reduce(0, (a, b) -> (a + b.length), Integer::sum));
List