sortFiles;
+ sortFiles = scanResult.getAllClasses();
+
+ for(int i = 0; i < sortFiles.size(); i++) {
+ try {
+ Class> sortClass = Class.forName(sortFiles.get(i).getName());
+ Constructor> newSort = sortClass.getConstructor(new Class[] {Delays.class, Highlights.class, Reads.class, Writes.class});
+ Sort sort = (Sort) newSort.newInstance(this.Delays, this.Highlights, this.Reads, this.Writes);
+
+ try {
+ if(verifySort(sort)) {
+ if(sort.comparisonBased()) {
+ comparisonSorts.add(sort);
+ }
+ else {
+ distributionSorts.add(sort);
+ }
+ }
+ else {
+ throw new Exception(sort.getClass().getName() + " is not a valid algorithm!");
+ }
+ }
+ catch(Exception e) {
+ invalidSorts.add(sort.getClass().getName());
+ e.printStackTrace();
+ }
+ }
+ catch(Exception e) {
+ invalidSorts.add(sortFiles.get(i).getName());
+ e.printStackTrace();
+ }
+ }
+ } catch (SecurityException | IllegalArgumentException e) {
+ JErrorPane.invokeErrorMessage(e);
+ }
+ }
+
+ private static boolean verifySort(Sort sort) {
+ boolean validSort = true;
+
+ if(sort.getSortPromptID().equals("")) {
+ System.out.println(sort.getClass().getName() + " does not have a prompt ID! ");
+ validSort = false;
+ }
+ if(sort.getRunAllID().equals("")) {
+ System.out.println(sort.getClass().getName() + " does not have a 'Run All Sorts' ID! ");
+ validSort = false;
+ }
+ if(sort.getReportSortID().equals("")) {
+ System.out.println(sort.getClass().getName() + " does not have a 'Run Sort' ID! ");
+ validSort = false;
+ }
+ if(sort.getCategory().equals("")) {
+ System.out.println(sort.getClass().getName() + " does not have a category! ");
+ validSort = false;
+ }
+
+ if(sort.getUnreasonablySlow() && sort.getUnreasonableLimit() == 0) {
+ System.out.println(sort.getClass().getName() + " is marked as an unreasonably slow sort, yet does not have a threshold for a warning message! ");
+ validSort = false;
+ }
+ if(!sort.getUnreasonablySlow() && sort.getUnreasonableLimit() != 0) {
+ System.out.println(sort.getClass().getName() + " is not marked as an unreasonably slow sort, yet has an unreasonably slow limit other than zero. ");
+ sort.setUnreasonableLimit(0);
+ }
+
+ if(!sort.getUnreasonablySlow() && sort.bogoSort()) {
+ System.out.println(sort.getClass().getName() + " is a type of Bogosort, yet is not labeled as unreasonably slow! ");
+ validSort = false;
+ }
+
+ if(sort.radixSort() && !sort.usesBuckets()) {
+ System.out.println(sort.getClass().getName() + " is a radix sort and should also be classified as a bucket sort. ");
+ sort.isBucketSort(true);
+ }
+
+ if(sort.radixSort() && sort.comparisonBased()) {
+ System.out.println(sort.getClass().getName() + " is a radix sort that is incorrectly labeled as a comparison sort! ");
+ validSort = false;
+ }
+
+ return validSort;
+ }
+
+ public String[][] getComparisonSorts() {
+ String[][] ComparisonSorts = new String[2][comparisonSorts.size()];
+
+ for(int i = 0; i < ComparisonSorts[0].length; i++) {
+ ComparisonSorts[0][i] = comparisonSorts.get(i).getClass().getName();
+ ComparisonSorts[1][i] = comparisonSorts.get(i).getSortPromptID();
+ }
+
+ return ComparisonSorts;
+ }
+ public String[][] getDistributionSorts() {
+ String[][] DistributionSorts = new String[2][distributionSorts.size()];
+
+ for(int i = 0; i < DistributionSorts[0].length; i++) {
+ DistributionSorts[0][i] = distributionSorts.get(i).getClass().getName();
+ DistributionSorts[1][i] = distributionSorts.get(i).getSortPromptID();
+ }
+ return DistributionSorts;
+ }
+ public String[] getInvalidSorts() {
+ if(invalidSorts.size() < 1) {
+ return null;
+ }
+
+ String[] InvalidSorts = new String[invalidSorts.size()];
+ InvalidSorts = invalidSorts.toArray(InvalidSorts);
+
+ return InvalidSorts;
+ }
+}
\ No newline at end of file
diff --git a/src/prompts/ShufflePrompt.java b/src/prompts/ShufflePrompt.java
new file mode 100644
index 00000000..ea471a72
--- /dev/null
+++ b/src/prompts/ShufflePrompt.java
@@ -0,0 +1,176 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package prompts;
+
+import javax.swing.JFrame;
+import javax.swing.JOptionPane;
+
+import frames.UtilFrame;
+import main.ArrayManager;
+import templates.Frame;
+import templates.JErrorPane;
+import utils.Shuffles;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+/**
+ *
+ * @author S630690
+ */
+final public class ShufflePrompt extends javax.swing.JFrame implements Frame {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ private ArrayManager ArrayManager;
+ private JFrame Frame;
+ private UtilFrame UtilFrame;
+
+ /**
+ * Creates new form SortPrompt
+ */
+ @SuppressWarnings("unchecked")
+ public ShufflePrompt(ArrayManager ArrayManager, JFrame frame, UtilFrame utilFrame) {
+ this.ArrayManager = ArrayManager;
+ this.Frame = frame;
+ this.UtilFrame = utilFrame;
+
+ setAlwaysOnTop(true);
+ setUndecorated(true);
+ initComponents();
+ jList1.setListData(ArrayManager.getShuffleIDs());
+ for(int i = 0; i < ArrayManager.getShuffles().length; i++) {
+ if(ArrayManager.getShuffle().equals(ArrayManager.getShuffles()[i])) {
+ jList1.setSelectedIndex(i);
+ break;
+ }
+ }
+ reposition();
+ setVisible(true);
+ }
+
+ @Override
+ public void reposition() {
+ setLocation(Frame.getX()+(Frame.getWidth()-getWidth())/2,Frame.getY()+(Frame.getHeight()-getHeight())/2);
+ }
+
+
+ /**
+ * This method is called from within the constructor to initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is always
+ * regenerated by the Form Editor.
+ */
+ @SuppressWarnings({ "rawtypes" })
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ this.jLabel1 = new javax.swing.JLabel();
+ this.jScrollPane1 = new javax.swing.JScrollPane();
+ this.jList1 = new javax.swing.JList();
+
+ setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
+
+ jLabel1.setText("How do you want the array to be shuffled?");
+
+ jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
+
+ jScrollPane1.setViewportView(this.jList1);
+
+ jList1.addListSelectionListener(new javax.swing.event.ListSelectionListener() {
+ @Override
+ public void valueChanged(javax.swing.event.ListSelectionEvent evt) {
+ try {
+ jList1ValueChanged(evt);
+ } catch (Exception e) {
+ JErrorPane.invokeErrorMessage(e);
+ }
+ }
+ });
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
+ getContentPane().setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
+ .addGroup(layout.createSequentialGroup()
+ .addGap(5, 5, 5)
+ .addComponent(this.jLabel1)
+ .addGap(5, 5, 5))
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(this.jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE))
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(this.jLabel1))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, true)
+ .addComponent(this.jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 95, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGap(5, 5, 5))
+ );
+
+ pack();
+ }// //GEN-END:initComponents
+
+ private void jList1ValueChanged(javax.swing.event.ListSelectionEvent evt) throws Exception {//GEN-FIRST:event_jList1ValueChanged
+ // TODO add your handling code here:
+ int selection = jList1.getSelectedIndex();
+ switch (selection) {
+ case 0:
+ ArrayManager.setShuffle(ArrayManager.getShuffles()[0]);
+ break;
+ case 1:
+ ArrayManager.setShuffle(ArrayManager.getShuffles()[1]);
+ break;
+ case 2:
+ ArrayManager.setShuffle(ArrayManager.getShuffles()[2]);
+ break;
+ case 3:
+ ArrayManager.setShuffle(ArrayManager.getShuffles()[3]);
+ break;
+ case 4:
+ ArrayManager.setShuffle(ArrayManager.getShuffles()[4]);
+ break;
+ default:
+ break;
+ }
+ UtilFrame.jButton6ResetText();
+ dispose();
+ }//GEN-LAST:event_jList1ValueChanged
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JLabel jLabel1;
+ @SuppressWarnings("rawtypes")
+ private javax.swing.JList jList1;
+ private javax.swing.JScrollPane jScrollPane1;
+ // End of variables declaration//GEN-END:variables
+}
\ No newline at end of file
diff --git a/src/prompts/SortPrompt.java b/src/prompts/SortPrompt.java
new file mode 100644
index 00000000..37f835f6
--- /dev/null
+++ b/src/prompts/SortPrompt.java
@@ -0,0 +1,244 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package prompts;
+
+import javax.swing.JFrame;
+
+import frames.UtilFrame;
+import main.ArrayVisualizer;
+import templates.Frame;
+import templates.JErrorPane;
+import threads.RunAllSorts;
+import threads.RunComparisonSort;
+import threads.RunDistributionSort;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+/**
+ *
+ * @author S630690
+ */
+
+final public class SortPrompt extends javax.swing.JFrame implements Frame {
+
+ private static final long serialVersionUID = 1L;
+
+ private int[] array;
+
+ private ArrayVisualizer ArrayVisualizer;
+ private JFrame Frame;
+ private UtilFrame UtilFrame;
+
+ @SuppressWarnings("unchecked")
+ public SortPrompt(int[] array, ArrayVisualizer arrayVisualizer, JFrame frame, UtilFrame utilFrame) {
+ this.array = array;
+ this.ArrayVisualizer = arrayVisualizer;
+ this.Frame = frame;
+ this.UtilFrame = utilFrame;
+
+ setAlwaysOnTop(true);
+ setUndecorated(true);
+ initComponents();
+ jList2.setListData(ArrayVisualizer.getComparisonSorts()[1]);
+ jList1.setListData(ArrayVisualizer.getDistributionSorts()[1]);
+ reposition();
+ setVisible(true);
+ }
+
+ @Override
+ public void reposition() {
+ setLocation(Frame.getX()+(Frame.getWidth()-getWidth())/2,Frame.getY()+(Frame.getHeight()-getHeight())/2);
+ }
+
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ this.jLabel1 = new javax.swing.JLabel();
+ this.jLabel2 = new javax.swing.JLabel();
+ this.jScrollPane1 = new javax.swing.JScrollPane();
+ this.jList2 = new javax.swing.JList();
+ this.jScrollPane2 = new javax.swing.JScrollPane();
+ this.jList1 = new javax.swing.JList();
+ this.jButton1 = new javax.swing.JButton();
+
+ setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
+
+ jLabel1.setText("Comparative");
+
+ jLabel2.setText("Distributive");
+
+ jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
+
+ jList2.setModel(new javax.swing.AbstractListModel() {
+
+ private static final long serialVersionUID = 1L;
+
+ String[] strings = { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5" };
+ @Override
+ public int getSize() { return strings.length; }
+ @Override
+ public Object getElementAt(int i) { return strings[i]; }
+ });
+
+ jList2.addListSelectionListener(new javax.swing.event.ListSelectionListener() {
+ @Override
+ public void valueChanged(javax.swing.event.ListSelectionEvent evt) {
+ jList2ValueChanged(evt);
+ }
+ });
+
+ jScrollPane1.setViewportView(this.jList2);
+
+ jScrollPane2.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
+
+ jList1.setModel(new javax.swing.AbstractListModel() {
+
+ private static final long serialVersionUID = 1L;
+
+ String[] strings = { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5" };
+ @Override
+ public int getSize() { return strings.length; }
+ @Override
+ public Object getElementAt(int i) { return strings[i]; }
+ });
+
+ jList1.addListSelectionListener(new javax.swing.event.ListSelectionListener() {
+ @Override
+ public void valueChanged(javax.swing.event.ListSelectionEvent evt) {
+ jList1ValueChanged(evt);
+ }
+ });
+
+ jScrollPane2.setViewportView(this.jList1);
+
+ jButton1.setText("Run All (approx. 30-60 minutes)");
+ jButton1.addActionListener(new java.awt.event.ActionListener() {
+ @Override
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ jButton1ActionPerformed();
+ }
+ });
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
+ getContentPane().setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addGap(40, 40, 40)
+ .addComponent(this.jLabel1)
+ .addGap(76, 76, 76)
+ .addComponent(this.jLabel2)
+ .addGap(35, 35, 35))
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(this.jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 150, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(this.jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 150, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGroup(javax.swing.GroupLayout.Alignment.CENTER, layout.createSequentialGroup()
+ .addComponent(this.jButton1))
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(this.jLabel1)
+ .addComponent(this.jLabel2))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addComponent(this.jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 200, Short.MAX_VALUE)
+ .addComponent(this.jScrollPane1))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(this.jButton1)
+ .addGap(0, 0, 0))
+ );
+
+ pack();
+ }// //GEN-END:initComponents
+
+ private void jButton1ActionPerformed() {//GEN-FIRST:event_jButton1ActionPerformed
+ new Thread(){
+ @Override
+ public void run(){
+ try {
+ RunAllSorts RunAllSorts = new RunAllSorts(ArrayVisualizer);
+ RunAllSorts.reportAllSorts(array);
+ } catch (Exception e) {
+ JErrorPane.invokeErrorMessage(e);
+ }
+ }
+ }.start();
+ UtilFrame.jButton1ResetText();
+ dispose();
+ }//GEN-LAST:event_jButton1ActionPerformed
+
+ private void jList1ValueChanged(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_jList1ValueChanged
+ // TODO add your handling code here:
+ final int selection = evt.getFirstIndex();
+ new Thread(){
+ @Override
+ public void run(){
+ try {
+ RunDistributionSort sortThread = new RunDistributionSort(ArrayVisualizer);
+ sortThread.ReportDistributiveSort(array, selection);
+ } catch (Exception e) {
+ JErrorPane.invokeErrorMessage(e);
+ }
+ }
+ }.start();
+ UtilFrame.jButton1ResetText();
+ dispose();
+ }//GEN-LAST:event_jList1ValueChanged
+
+ private void jList2ValueChanged(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_jList2ValueChanged
+ // TODO add your handling code here:
+ final int selection = evt.getFirstIndex();
+ new Thread(){
+ @Override
+ public void run() {
+ RunComparisonSort sortThread = new RunComparisonSort(ArrayVisualizer);
+ sortThread.ReportComparativeSort(array, selection);
+ }
+ }.start();
+ UtilFrame.jButton1ResetText();
+ dispose();
+ }//GEN-LAST:event_jList2ValueChanged
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JButton jButton1;
+ private javax.swing.JLabel jLabel1;
+ private javax.swing.JLabel jLabel2;
+ @SuppressWarnings("rawtypes")
+ private javax.swing.JList jList1;
+ @SuppressWarnings("rawtypes")
+ private javax.swing.JList jList2;
+ private javax.swing.JScrollPane jScrollPane1;
+ private javax.swing.JScrollPane jScrollPane2;
+ // End of variables declaration//GEN-END:variables
+}
\ No newline at end of file
diff --git a/src/prompts/ViewPrompt.java b/src/prompts/ViewPrompt.java
new file mode 100644
index 00000000..92c6043d
--- /dev/null
+++ b/src/prompts/ViewPrompt.java
@@ -0,0 +1,365 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package prompts;
+
+import javax.swing.JFrame;
+
+import frames.UtilFrame;
+import main.ArrayVisualizer;
+import templates.Frame;
+import visuals.VisualStyles;
+import javax.swing.JButton;
+import javax.swing.GroupLayout.Alignment;
+import javax.swing.GroupLayout;
+import javax.swing.LayoutStyle.ComponentPlacement;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+/**
+ *
+ * @author S630690
+ */
+
+final public class ViewPrompt extends javax.swing.JFrame implements Frame {
+
+ private static final long serialVersionUID = 1L;
+
+ private ArrayVisualizer ArrayVisualizer;
+ private JFrame Frame;
+ private UtilFrame UtilFrame;
+
+ public ViewPrompt(ArrayVisualizer arrayVisualizer, JFrame frame, UtilFrame utilFrame) {
+ this.ArrayVisualizer = arrayVisualizer;
+ this.Frame = frame;
+ this.UtilFrame = utilFrame;
+
+ setAlwaysOnTop(true);
+ setUndecorated(true);
+ initComponents();
+ reposition();
+ setVisible(true);
+ }
+
+ @Override
+ public void reposition(){
+ setLocation(Frame.getX() + ((Frame.getWidth() - getWidth()) / 2), Frame.getY() + ((Frame.getHeight() - getHeight()) / 2));
+ }
+
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ this.jLabel1 = new javax.swing.JLabel();
+ this.barGraph = new javax.swing.JButton();
+ this.dotGraph = new javax.swing.JButton();
+ this.colorCircle = new javax.swing.JButton();
+ this.triangleMesh = new javax.swing.JButton();
+ this.spiral = new javax.swing.JButton();
+ this.disparity = new javax.swing.JButton();
+ this.disparityDots = new javax.swing.JButton();
+ this.spiralDots= new javax.swing.JButton();
+ this.rainbow = new javax.swing.JButton();
+ this.hoops = new javax.swing.JButton();
+ this.sineWave = new javax.swing.JButton();
+ this.waveDots = new javax.swing.JButton();
+
+ setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
+ setResizable(false);
+
+ jLabel1.setText("Select Visual Style");
+
+ barGraph.setText("Bar Graph");
+ barGraph.addActionListener(new java.awt.event.ActionListener() {
+ @Override
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ barGraphActionPerformed(evt);
+ }
+ });
+
+ dotGraph.setText("Dot Graph");
+ dotGraph.addActionListener(new java.awt.event.ActionListener() {
+ @Override
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ dotGraphActionPerformed(evt);
+ }
+ });
+
+ colorCircle.setText("Color Circle");
+ colorCircle.addActionListener(new java.awt.event.ActionListener() {
+ @Override
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ colorCircleActionPerformed(evt);
+ }
+ });
+
+ triangleMesh.setText("Triangle Mesh");
+ triangleMesh.addActionListener(new java.awt.event.ActionListener() {
+ @Override
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ triangleMeshActionPerformed(evt);
+ }
+ });
+
+ spiral.setText("Spiral");
+ spiral.addActionListener(new java.awt.event.ActionListener() {
+ @Override
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ spiralActionPerformed(evt);
+ }
+ });
+
+ rainbow.setText("Rainbow");
+ rainbow.addActionListener(new java.awt.event.ActionListener() {
+ @Override
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ rainbowActionPerformed(evt);
+ }
+ });
+
+ disparity.setText("Disparity Circle");
+ disparity.addActionListener(new java.awt.event.ActionListener() {
+ @Override
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ disparityActionPerformed(evt);
+ }
+ });
+
+ hoops.setText("Hoops");
+ hoops.addActionListener(new java.awt.event.ActionListener() {
+ @Override
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ hoopsActionPerformed(evt);
+ }
+ });
+
+ disparityDots.setText("Disparity Dots");
+ disparityDots.addActionListener(new java.awt.event.ActionListener() {
+ @Override
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ disparityDotsActionPerformed(evt);
+ }
+ });
+
+ spiralDots.setText("Spiral Dots");
+ spiralDots.addActionListener(new java.awt.event.ActionListener() {
+ @Override
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ spiralDotsActionPerformed(evt);
+ }
+ });
+
+ sineWave.setText("Sine Wave");
+ sineWave.addActionListener(new java.awt.event.ActionListener() {
+ @Override
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ sineWaveActionPerformed(evt);
+ }
+ });
+ waveDots.setText("Wave Dots");
+ waveDots.addActionListener(new java.awt.event.ActionListener() {
+ @Override
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ waveDotsActionPerformed(evt);
+ }
+ });
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(Alignment.CENTER)
+ .addComponent(jLabel1)
+ .addGroup(Alignment.LEADING, layout.createSequentialGroup()
+ .addGap(18)
+ .addGroup(layout.createParallelGroup(Alignment.LEADING)
+ .addComponent(barGraph, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 105, Short.MAX_VALUE)
+ .addComponent(rainbow, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 105, Short.MAX_VALUE)
+ .addComponent(colorCircle, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 105, Short.MAX_VALUE)
+ .addComponent(disparity, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(disparityDots, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 105, Short.MAX_VALUE)
+ .addComponent(sineWave, GroupLayout.DEFAULT_SIZE, 105, Short.MAX_VALUE))
+ .addPreferredGap(ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(Alignment.LEADING)
+ .addComponent(waveDots, GroupLayout.DEFAULT_SIZE, 101, Short.MAX_VALUE)
+ .addComponent(dotGraph, GroupLayout.DEFAULT_SIZE, 101, Short.MAX_VALUE)
+ .addComponent(triangleMesh, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(spiral, GroupLayout.DEFAULT_SIZE, 101, Short.MAX_VALUE)
+ .addComponent(hoops, GroupLayout.DEFAULT_SIZE, 101, Short.MAX_VALUE)
+ .addComponent(spiralDots, GroupLayout.DEFAULT_SIZE, 101, Short.MAX_VALUE))
+ .addGap(18))
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(Alignment.CENTER)
+ .addGroup(layout.createSequentialGroup()
+ .addGap(7)
+ .addComponent(jLabel1)
+ .addPreferredGap(ComponentPlacement.UNRELATED)
+ .addGroup(layout.createParallelGroup(Alignment.BASELINE)
+ .addComponent(barGraph)
+ .addComponent(dotGraph))
+ .addPreferredGap(ComponentPlacement.UNRELATED)
+ .addGroup(layout.createParallelGroup(Alignment.BASELINE)
+ .addComponent(rainbow)
+ .addComponent(triangleMesh))
+ .addPreferredGap(ComponentPlacement.UNRELATED)
+ .addGroup(layout.createParallelGroup(Alignment.BASELINE)
+ .addComponent(colorCircle)
+ .addComponent(hoops))
+ .addPreferredGap(ComponentPlacement.UNRELATED)
+ .addGroup(layout.createParallelGroup(Alignment.BASELINE)
+ .addComponent(disparity)
+ .addComponent(spiral))
+ .addPreferredGap(ComponentPlacement.UNRELATED)
+ .addGroup(layout.createParallelGroup(Alignment.BASELINE)
+ .addComponent(disparityDots)
+ .addComponent(spiralDots))
+ .addPreferredGap(ComponentPlacement.UNRELATED)
+ .addGroup(layout.createParallelGroup(Alignment.LEADING)
+ .addComponent(sineWave)
+ .addComponent(waveDots))
+ .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+ getContentPane().setLayout(layout);
+
+ pack();
+ }// //GEN-END:initComponents
+
+ private void setAllFieldsFalse(){
+ ArrayVisualizer.togglePointer(false);
+ ArrayVisualizer.toggleDistance(false);
+ ArrayVisualizer.togglePixels(false);
+ ArrayVisualizer.toggleRainbow(false);
+ ArrayVisualizer.toggleSpiral(false);
+ ArrayVisualizer.toggleWave(false);
+ }
+
+ private void barGraphActionPerformed(java.awt.event.ActionEvent evt) {
+ setAllFieldsFalse();
+ ArrayVisualizer.setVisual(VisualStyles.BARS);
+ UtilFrame.jButton2ResetText();
+ dispose();
+ }
+ private void dotGraphActionPerformed(java.awt.event.ActionEvent evt) {
+ setAllFieldsFalse();
+ ArrayVisualizer.setVisual(VisualStyles.PIXELS);
+ UtilFrame.jButton2ResetText();
+ dispose();
+ }
+ private void rainbowActionPerformed(java.awt.event.ActionEvent evt) {
+ setAllFieldsFalse();
+ ArrayVisualizer.setVisual(VisualStyles.BARS);
+ ArrayVisualizer.toggleRainbow(true);
+ UtilFrame.jButton2ResetText();
+ dispose();
+ }
+ private void triangleMeshActionPerformed(java.awt.event.ActionEvent evt) {
+ setAllFieldsFalse();
+ ArrayVisualizer.setVisual(VisualStyles.MESH);
+ UtilFrame.jButton2ResetText();
+ dispose();
+ }
+ private void colorCircleActionPerformed(java.awt.event.ActionEvent evt) {
+ //TODO: Pointer as separate option
+ setAllFieldsFalse();
+ ArrayVisualizer.setVisual(VisualStyles.CIRCULAR);
+ //ArrayVisualizer.togglePointer(true);
+ ArrayVisualizer.toggleRainbow(true);
+ if(ArrayVisualizer.getCurrentLength() == 2) ArrayVisualizer.setCurrentLength(4);
+ UtilFrame.jButton2ResetText();
+ dispose();
+ }
+ private void spiralActionPerformed(java.awt.event.ActionEvent evt) {
+ setAllFieldsFalse();
+ ArrayVisualizer.setVisual(VisualStyles.CIRCULAR);
+ ArrayVisualizer.toggleSpiral(true);
+ if(ArrayVisualizer.getCurrentLength() == 2) ArrayVisualizer.setCurrentLength(4);
+ UtilFrame.jButton2ResetText();
+ dispose();
+ }
+ private void disparityActionPerformed(java.awt.event.ActionEvent evt) {
+ setAllFieldsFalse();
+ ArrayVisualizer.setVisual(VisualStyles.CIRCULAR);
+ ArrayVisualizer.toggleDistance(true);
+ //ArrayVisualizer.togglePointer(true);
+ if(ArrayVisualizer.getCurrentLength() == 2) ArrayVisualizer.setCurrentLength(4);
+ UtilFrame.jButton2ResetText();
+ dispose();
+ }
+ private void hoopsActionPerformed(java.awt.event.ActionEvent evt) {
+ setAllFieldsFalse();
+ ArrayVisualizer.setVisual(VisualStyles.HOOPS);
+ UtilFrame.jButton2ResetText();
+ dispose();
+ }
+ private void disparityDotsActionPerformed(java.awt.event.ActionEvent evt) {
+ setAllFieldsFalse();
+ ArrayVisualizer.setVisual(VisualStyles.CIRCULAR);
+ ArrayVisualizer.toggleDistance(true);
+ ArrayVisualizer.togglePixels(true);
+ if(ArrayVisualizer.getCurrentLength() == 2) ArrayVisualizer.setCurrentLength(4);
+ UtilFrame.jButton2ResetText();
+ dispose();
+ }
+ private void spiralDotsActionPerformed(java.awt.event.ActionEvent evt) {
+ setAllFieldsFalse();
+ ArrayVisualizer.setVisual(VisualStyles.CIRCULAR);
+ ArrayVisualizer.togglePixels(true);
+ ArrayVisualizer.toggleSpiral(true);
+ if(ArrayVisualizer.getCurrentLength() == 2) ArrayVisualizer.setCurrentLength(4);
+ UtilFrame.jButton2ResetText();
+ dispose();
+ }
+ private void sineWaveActionPerformed(java.awt.event.ActionEvent evt) {
+ setAllFieldsFalse();
+ ArrayVisualizer.setVisual(VisualStyles.BARS);
+ ArrayVisualizer.toggleWave(true);
+ UtilFrame.jButton2ResetText();
+ dispose();
+ }
+ private void waveDotsActionPerformed(java.awt.event.ActionEvent evt) {
+ setAllFieldsFalse();
+ ArrayVisualizer.setVisual(VisualStyles.PIXELS);
+ ArrayVisualizer.togglePixels(true);
+ ArrayVisualizer.toggleWave(true);
+ UtilFrame.jButton2ResetText();
+ dispose();
+ }
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JButton barGraph;
+ private javax.swing.JButton dotGraph;
+ private javax.swing.JButton colorCircle;
+ private javax.swing.JButton triangleMesh;
+ private javax.swing.JButton spiral;
+ private javax.swing.JButton spiralDots;
+ private javax.swing.JButton disparity;
+ private javax.swing.JButton disparityDots;
+ private javax.swing.JButton rainbow;
+ private javax.swing.JButton hoops;
+ private javax.swing.JButton sineWave;
+ private javax.swing.JButton waveDots;
+ private javax.swing.JLabel jLabel1;
+}
\ No newline at end of file
diff --git a/src/sorts/AmericanFlagSort.java b/src/sorts/AmericanFlagSort.java
new file mode 100644
index 00000000..ed2c16cc
--- /dev/null
+++ b/src/sorts/AmericanFlagSort.java
@@ -0,0 +1,156 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+Copyright 2017 Justin Wetherell
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+ *
+ */
+
+/*
+ * An American flag sort is an efficient, in-place variant of radix sort that
+ * distributes items into hundreds of buckets. Non-comparative sorting
+ * algorithms such as radix sort and American flag sort are typically used to
+ * sort large objects such as strings, for which comparison is not a unit-time
+ * operation.
+ *
+ * Family: Bucket.
+ * Space: In-place.
+ * Stable: False.
+ *
+ * Average case = O(n*k/d)
+ * Worst case = O(n*k/d)
+ * Best case = O(n*k/d)
+ *
+ * NOTE: n is the number of digits and k is the average bucket size
+ *
+ * @see American Flag Sort (Wikipedia)
+ *
+ * @author Justin Wetherell
+ */
+
+final public class AmericanFlagSort extends Sort {
+ private int NUMBER_OF_BUCKETS = 128; // ex. 10 for base 10 numbers
+
+ public AmericanFlagSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("American Flag");
+ this.setRunAllID("American Flag Sort, " + this.NUMBER_OF_BUCKETS + " Buckets");
+ this.setReportSortID("American Flag Sort");
+ this.setCategory("Distributive Sorts");
+ this.isComparisonBased(false);
+ this.isBucketSort(true);
+ this.isRadixSort(true);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ // Slightly different than Reads.analyzeMaxLog.
+ private int getMaxNumberOfDigits(int[] array, int length) {
+ int max = Integer.MIN_VALUE;
+ int temp = 0;
+
+ for (int i = 0; i < length; i++) {
+ temp = (int) (Math.log(array[i]) / Math.log(this.NUMBER_OF_BUCKETS)) + 1;
+
+ if (temp > max)
+ max = temp;
+ }
+ return max;
+ }
+
+ private int getDigit(int integer, int divisor) {
+ return (integer / divisor) % this.NUMBER_OF_BUCKETS;
+ }
+
+ private void sort(int[] array, int start, int length, int divisor) {
+ // First pass - find counts
+ int[] count = new int[this.NUMBER_OF_BUCKETS];
+ int[] offset = new int[this.NUMBER_OF_BUCKETS];
+ int digit = 0;
+
+ for (int i = start; i < length; i++) {
+ Highlights.markArray(1, i);
+ Delays.sleep(0.75);
+
+ int d = array[i];
+ digit = this.getDigit(d, divisor);
+
+ Writes.write(count, digit, count[digit] + 1, 0, false, true);
+ }
+
+ Writes.write(offset, 0, start + 0, 0, false, true);
+
+ for (int i = 1; i < this.NUMBER_OF_BUCKETS; i++) {
+ Writes.write(offset, i, count[i - 1] + offset[i - 1], 0, false, true);
+ }
+
+ // Second pass - move into position
+ for (int b = 0; b < this.NUMBER_OF_BUCKETS; b++) {
+ while (count[b] > 0) {
+ int origin = offset[b];
+ int from = origin;
+ int num = array[from];
+
+ Writes.write(array, from, -1, 0.5, true, false);
+
+ do {
+ digit = this.getDigit(num, divisor);
+ int to = offset[digit];
+
+ Writes.write(offset, digit, offset[digit] + 1, 0, false, true);
+ Writes.write(count, digit, count[digit] - 1, 0, false, true);
+
+ int temp = array[to];
+ Writes.write(array, to, num, 0.75, true, false);
+
+ num = temp;
+ from = to;
+ } while (from != origin);
+ }
+ }
+ if (divisor > 1) {
+ // Sort the buckets
+ for (int i = 0; i < this.NUMBER_OF_BUCKETS; i++) {
+ int begin = (i > 0) ? offset[i - 1] : start;
+ int end = offset[i];
+
+ if (end - begin > 1)
+ this.sort(array, begin, end, divisor / this.NUMBER_OF_BUCKETS);
+ }
+ }
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLen, int bucketCount) {
+ this.NUMBER_OF_BUCKETS = bucketCount;
+ this.setRunAllID("American Flag Sort, " + this.NUMBER_OF_BUCKETS + " Buckets");
+
+ int numberOfDigits = this.getMaxNumberOfDigits(array, currentLen); // Max number of digits
+ int max = 1;
+
+ for (int i = 0; i < numberOfDigits - 1; i++)
+ max *= this.NUMBER_OF_BUCKETS;
+
+ this.sort(array, 0, currentLen, max);
+ }
+ }
\ No newline at end of file
diff --git a/src/sorts/BadSort.java b/src/sorts/BadSort.java
new file mode 100644
index 00000000..9ef47de9
--- /dev/null
+++ b/src/sorts/BadSort.java
@@ -0,0 +1,58 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ * This example of an O(n^3) sorting algorithm may be found here, written by James Jensen (StriplingWarrayior on StackOverflow):
+ * https://stackoverflow.com/questions/27389344/is-there-a-sorting-algorithm-with-a-worst-case-time-complexity-of-n3
+ */
+
+final public class BadSort extends Sort {
+ public BadSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Bad");
+ this.setRunAllID("Bad Sort");
+ this.setReportSortID("Badsort");
+ this.setCategory("Selection Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(true);
+ this.setUnreasonableLimit(4096);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLen, int bucketCount) {
+ for (int i = 0; i < currentLen; i++) {
+ int shortest = i;
+ Delays.sleep(0.05);
+
+ for (int j = i; j < currentLen; j++) {
+ Highlights.markArray(1, j);
+ Delays.sleep(0.05);
+
+ boolean isShortest = true;
+ for (int k = j + 1; k < currentLen; k++) {
+ Highlights.markArray(2, k);
+ Delays.sleep(0.05);
+
+ if (Reads.compare(array[j], array[k]) == 1) {
+ isShortest = false;
+ break;
+ }
+ }
+ if(isShortest) {
+ shortest = j;
+ break;
+ }
+ }
+ Writes.swap(array, i, shortest, 0.05, true, false);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/BinaryGnomeSort.java b/src/sorts/BinaryGnomeSort.java
new file mode 100644
index 00000000..eca2b99e
--- /dev/null
+++ b/src/sorts/BinaryGnomeSort.java
@@ -0,0 +1,60 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+final public class BinaryGnomeSort extends Sort {
+ public BinaryGnomeSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Binary Gnome");
+ this.setRunAllID("Optimized Gnome Sort + Binary Search");
+ this.setReportSortID("Optimized Gnomesort + Binary Search");
+ this.setCategory("Exchange Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ for (int i = 1; i < length; i++) {
+ int num = array[i];
+
+ int lo = 0, hi = i;
+ while (lo < hi) {
+ int mid = lo + ((hi - lo) / 2);
+
+ Highlights.markArray(1, lo);
+ Highlights.markArray(3, mid);
+ Highlights.markArray(2, hi);
+
+ Delays.sleep(1);
+
+ if (Reads.compare(num, array[mid]) < 0) { // do NOT shift equal elements past each other; this maintains stability!
+ hi = mid;
+ }
+ else {
+ lo = mid + 1;
+ }
+ }
+
+ // item has to go into position lo
+
+ Highlights.clearMark(1);
+ Highlights.clearMark(2);
+
+ int j = i;
+ while (j > lo) {
+ Writes.swap(array, j, j - 1, 0.05, true, false);
+ j--;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/BinaryInsertionSort.java b/src/sorts/BinaryInsertionSort.java
new file mode 100644
index 00000000..5c0d0bec
--- /dev/null
+++ b/src/sorts/BinaryInsertionSort.java
@@ -0,0 +1,59 @@
+package sorts;
+
+import templates.BinaryInsertionSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class BinaryInsertionSort extends BinaryInsertionSorting {
+ public BinaryInsertionSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Binary Insert");
+ this.setRunAllID("Binary Insertion Sort");
+ this.setReportSortID("Binary Insertsort");
+ this.setCategory("Insertion Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ public void customBinaryInsert(int[] array, int start, int end, double sleep) {
+ this.binaryInsertSort(array, start, end, sleep, sleep);
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ this.binaryInsertSort(array, 0, currentLength, 1, 0.05);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/BinaryMergeSort.java b/src/sorts/BinaryMergeSort.java
new file mode 100644
index 00000000..a808a096
--- /dev/null
+++ b/src/sorts/BinaryMergeSort.java
@@ -0,0 +1,55 @@
+package sorts;
+
+import templates.MergeSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class BinaryMergeSort extends MergeSorting {
+ public BinaryMergeSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Binary Merge");
+ this.setRunAllID("Binary Merge Sort");
+ this.setReportSortID("Binary Mergesort");
+ this.setCategory("Hybrid Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ this.mergeSort(array, length, true);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/BinaryQuickSort.java b/src/sorts/BinaryQuickSort.java
new file mode 100644
index 00000000..d7e82583
--- /dev/null
+++ b/src/sorts/BinaryQuickSort.java
@@ -0,0 +1,47 @@
+package sorts;
+
+import templates.BinaryQuickSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/**
+ * Binary MSD Radix Sort / Binary Quicksort.
+ *
+ * Implemented as recursive decent, and via task queue, see:
+ * * binaryQuickSortRecursive, and
+ * * binaryQuickSort respectively.
+ *
+ * Both of which are in-place sorting algorithms, with the recursive utilizing
+ * the stack for divide-and-conquer, while the non-recursive utilizes a queue.
+ *
+ * Can be extended to support unsigned integers, by sorting the first bit rin
+ * reverse. Can be made stable at the cost of O(n) memory. Can be parallalized
+ * to O(log2(n)) subtasks / threads.
+ *
+ * @author Skeen
+ */
+
+final public class BinaryQuickSort extends BinaryQuickSorting {
+ public BinaryQuickSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Binary Quick");
+ this.setRunAllID("Binary Quick Sort");
+ this.setReportSortID("Binary Quicksort");
+ this.setCategory("Distribution Sorts");
+ this.isComparisonBased(false);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ int mostSignificantBit = Reads.analyzeBit(array, length);
+ this.binaryQuickSort(array, 0, length - 1, mostSignificantBit);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/BitonicSort.java b/src/sorts/BitonicSort.java
new file mode 100644
index 00000000..aeed3bad
--- /dev/null
+++ b/src/sorts/BitonicSort.java
@@ -0,0 +1,88 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ * This version of Bitonic Sort was taken from here, written by H.W. Lang:
+ * http://www.inf.fh-flensburg.de/lang/algorithmen/sortieren/bitonic/oddn.htm
+ */
+
+final public class BitonicSort extends Sort {
+ private boolean direction = true;
+
+ public BitonicSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Bitonic");
+ this.setRunAllID("Batcher's Bitonic Sort");
+ this.setReportSortID("Bitonic Sort");
+ this.setCategory("Concurrent Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ private static int greatestPowerOfTwoLessThan(int n){
+ int k = 1;
+ while (k < n) {
+ k = k << 1;
+ }
+ return k >> 1;
+ }
+
+ private void compare(int[] A, int i, int j, boolean dir)
+ {
+ Highlights.markArray(1, i);
+ Highlights.markArray(2, j);
+
+ Delays.sleep(0.5);
+
+ int cmp = Reads.compare(A[i], A[j]);
+
+ if (dir == (cmp == 1)) Writes.swap(A, i, j, 1, true, false);
+ }
+
+ private void bitonicMerge(int[] A, int lo, int n, boolean dir)
+ {
+ if (n > 1)
+ {
+ int m = BitonicSort.greatestPowerOfTwoLessThan(n);
+
+ for (int i = lo; i < lo + n - m; i++) {
+ this.compare(A, i, i+m, dir);
+ }
+
+ this.bitonicMerge(A, lo, m, dir);
+ this.bitonicMerge(A, lo + m, n - m, dir);
+ }
+ }
+
+ private void bitonicSort(int[] A, int lo, int n, boolean dir)
+ {
+ if (n > 1)
+ {
+ int m = n / 2;
+ this.bitonicSort(A, lo, m, !dir);
+ this.bitonicSort(A, lo + m, n - m, dir);
+ this.bitonicMerge(A, lo, n, dir);
+ }
+ }
+
+ public void changeDirection(String choice) throws Exception {
+ if(choice.equals("forward")) this.direction = true;
+ else if(choice.equals("backward")) this.direction = false;
+ else throw new Exception("Invalid direction for Bitonic Sort!");
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ this.bitonicSort(array, 0, currentLength, this.direction);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/BogoBogoSort.java b/src/sorts/BogoBogoSort.java
new file mode 100644
index 00000000..4445add5
--- /dev/null
+++ b/src/sorts/BogoBogoSort.java
@@ -0,0 +1,41 @@
+package sorts;
+
+import templates.BogoSorting;
+
+public final class BogoBogoSort extends BogoSorting {
+ public BogoBogoSort(utils.Delays delayOps, utils.Highlights markOps, utils.Reads readOps, utils.Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Bogobogo");
+ this.setRunAllID("Bogobogo Sort");
+ this.setReportSortID("Bogobogosort");
+ this.setCategory("Distributive Sorts");
+ this.isComparisonBased(false); //Comparisons are not used to swap elements
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(true);
+ this.setUnreasonableLimit(8);
+ this.isBogoSort(true);
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ int bogoLength = 2;
+ boolean arrayNotSorted = true;
+
+ while(arrayNotSorted) {
+ if(bogoIsSorted(array, bogoLength)) {
+ if(bogoLength == currentLength) {
+ arrayNotSorted = false;
+ }
+ else {
+ bogoLength++;
+ }
+ }
+ else {
+ bogoLength = 2;
+ }
+ if(arrayNotSorted) bogoSwap(array, bogoLength, 0);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/BogoSort.java b/src/sorts/BogoSort.java
new file mode 100644
index 00000000..81403482
--- /dev/null
+++ b/src/sorts/BogoSort.java
@@ -0,0 +1,57 @@
+package sorts;
+
+import templates.BogoSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class BogoSort extends BogoSorting {
+ public BogoSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Bogo");
+ this.setRunAllID("Bogo Sort");
+ this.setReportSortID("Bogosort");
+ this.setCategory("Distributive Sorts");
+ this.isComparisonBased(false); //Comparisons are not used to swap elements
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(true);
+ this.setUnreasonableLimit(16);
+ this.isBogoSort(true);
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLen, int bucketCount) {
+ while(!this.bogoIsSorted(array, currentLen)) {
+ this.bogoSwap(array, currentLen, 0);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/BottomUpMergeSort.java b/src/sorts/BottomUpMergeSort.java
new file mode 100644
index 00000000..4486049e
--- /dev/null
+++ b/src/sorts/BottomUpMergeSort.java
@@ -0,0 +1,145 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+final public class BottomUpMergeSort extends Sort {
+ public BottomUpMergeSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Bottom-Up Merge");
+ //this.setRunAllID("Bottom-Up Merge Sort w/ Binary Insert (std::stable_sort)");
+ this.setRunAllID("Bottom-Up Merge Sort [std::stable_sort]");
+ this.setReportSortID("Bottom-Up Mergesort w/ Binary Insert");
+ this.setCategory("Hybrid Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ /**
+ * Iterative merge sort algorithm --- as a static method
+
+ * @author: Sartaj Sahni, ported to Java by Timothy Rolfe
+
+ * @see Data Structures, Algorithms, and Applications in C++,
+ * pp. 680-81 (the original in C++ has a memory leak, but
+ * that is not a problem in Java due to garbage collection)
+
+ * Minor revision by Timothy Rolfe (tjr) --- saves a comparison
+ */
+
+ //retrieved from http://penguin.ewu.edu/cscd300/Topic/AdvSorting/MergeSorts/MergeIter.html
+ private void merge (int[] c, int[] d, int lt, int md, int rt, boolean activeSound) {
+ // Merge c[lt:md] and c[md+1:rt] to d[lt:rt]
+ int i = lt, // cursor for first segment
+ j = md+1, // cursor for second
+ k = lt; // cursor for result
+
+ // merge until i or j exits its segment
+ while ( (i <= md) && (j <= rt) ) {
+ if (Reads.compare(c[i], c[j]) <= 0) {
+ if(activeSound) {
+ Writes.write(d, k++, c[i++], 0.5, false, true);
+ Highlights.markArray(1, i);
+ Highlights.markArray(2, j);
+ }
+ else {
+ Writes.write(d, k++, c[i++], 0.5, false, false);
+ Highlights.markArray(1, k - 1);
+ Highlights.clearMark(2);
+ }
+ }
+ else {
+ if(activeSound) {
+ Writes.write(d, k++, c[j++], 0.5, false, true);
+ Highlights.markArray(1, j);
+ Highlights.markArray(2, i);
+ }
+ else {
+ Writes.write(d, k++, c[j++], 0.5, false, false);
+ Highlights.markArray(1, k - 1);
+ Highlights.clearMark(2);
+ }
+ }
+ Delays.sleep(0.5);
+ }
+ // take care of left overs --- tjr code: only one while loop actually runs
+ while ( i <= md ) {
+ Writes.write(d, k++, c[i++], 1, true, activeSound);
+ }
+ while ( j <= rt ) {
+ Writes.write(d, k++, c[j++], 1, true, activeSound);
+ }
+
+ } // end merge()
+
+ /**
+ * Perform one pass through the two arrays, invoking Merge() above
+ */
+ private void mergePass (int[] x, int[] y, int s, int n, boolean activeSound)
+ {// Merge adjacent segments of size s.
+ int i = 0;
+
+ while (i <= n - 2 * s)
+ {//Merge two adjacent segments of size s
+ this.merge (x, y, i, i+s-1, i+2*s-1, activeSound);
+ i = i + 2*s;
+ }
+ // fewer than 2s elements remain
+ if (i + s < n) {
+ this.merge (x, y, i, i+s-1, n-1, activeSound);
+ }
+ else
+ for (int j = i; j <= n-1; j++) {
+ Writes.write(y, j, x[j], 1, false, activeSound); // copy last segment to y
+ Highlights.markArray(1, j);
+ Highlights.clearMark(2);
+ }
+
+
+ }// end mergePass()
+
+ /**
+ * Entry point for merge sort
+ */
+ private void stableSort (int[] a, int n) {
+ BinaryInsertionSort binaryInserter = new BinaryInsertionSort(this.Delays, this.Highlights, this.Reads, this.Writes);
+
+ if(n < 16) {
+ binaryInserter.customBinaryInsert(a, 0, 16, 0.35);
+ return;
+ }
+
+ // Sort a[0:n-1] using merge sort.
+ int s = 16; // segment size
+ int[] b = new int [n];
+ int i;
+
+ for(i = 0; i <= n - 16; i += 16) {
+ binaryInserter.customBinaryInsert(a, i, i + 16, 0.35);
+ }
+ binaryInserter.customBinaryInsert(a, i, n, 0.35);
+
+ while (s < n)
+ {
+ this.mergePass (a, b, s, n, true); // merge from a to b
+ s += s; // double the segment size
+ this.mergePass (b, a, s, n, false); // merge from b to a
+ s += s; // again, double the segment size
+ } // end while
+ // in C/C++, return the scratch array b by free/delete --- tjr
+ // end mergeSort
+ }// end MergeArray class
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ this.stableSort(array, length);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/BranchedPDQSort.java b/src/sorts/BranchedPDQSort.java
new file mode 100644
index 00000000..5ea432e9
--- /dev/null
+++ b/src/sorts/BranchedPDQSort.java
@@ -0,0 +1,52 @@
+package sorts;
+
+import templates.PDQSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+pdqsort.h - Pattern-defeating quicksort.
+Copyright (c) 2015 Orson Peters
+This software is provided 'as-is', without any express or implied warranty. In no event will the
+authors be held liable for any damages arising from the use of this software.
+Permission is granted to anyone to use this software for any purpose, including commercial
+applications, and to alter it and redistribute it freely, subject to the following restrictions:
+1. The origin of this software must not be misrepresented; you must not claim that you wrote the
+ original software. If you use this software in a product, an acknowledgment in the product
+ documentation would be appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be misrepresented as
+ being the original software.
+3. This notice may not be removed or altered from any source distribution.
+ *
+ */
+
+final public class BranchedPDQSort extends PDQSorting {
+ public BranchedPDQSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Branched PDQ");
+ this.setRunAllID("Pattern-Defeating Quick Sort");
+ this.setReportSortID("Pattern-Defeating Quicksort");
+ this.setCategory("Hybrid Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ public void customSort(int[] array, int low, int high) {
+ this.newHeapSorter(new MaxHeapSort(this.Delays, this.Highlights, this.Reads, this.Writes));
+ pdqLoop(array, low, high, false, pdqLog(high - low));
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ this.newHeapSorter(new MaxHeapSort(this.Delays, this.Highlights, this.Reads, this.Writes));
+ pdqLoop(array, 0, currentLength, false, pdqLog(currentLength));
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/BranchlessPDQSort.java b/src/sorts/BranchlessPDQSort.java
new file mode 100644
index 00000000..55008b3d
--- /dev/null
+++ b/src/sorts/BranchlessPDQSort.java
@@ -0,0 +1,47 @@
+package sorts;
+
+import templates.PDQSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+pdqsort.h - Pattern-defeating quicksort.
+Copyright (c) 2015 Orson Peters
+This software is provided 'as-is', without any express or implied warranty. In no event will the
+authors be held liable for any damages arising from the use of this software.
+Permission is granted to anyone to use this software for any purpose, including commercial
+applications, and to alter it and redistribute it freely, subject to the following restrictions:
+1. The origin of this software must not be misrepresented; you must not claim that you wrote the
+ original software. If you use this software in a product, an acknowledgment in the product
+ documentation would be appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be misrepresented as
+ being the original software.
+3. This notice may not be removed or altered from any source distribution.
+ *
+ */
+
+final public class BranchlessPDQSort extends PDQSorting {
+ public BranchlessPDQSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Branchless PDQ");
+ this.setRunAllID("Branchless Pattern-Defeating Quick Sort");
+ this.setReportSortID("Branchless Pattern-Defeating Quicksort");
+ this.setCategory("Hybrid Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ this.newHeapSorter(new MaxHeapSort(this.Delays, this.Highlights, this.Reads, this.Writes));
+ pdqLoop(array, 0, currentLength, true, pdqLog(currentLength));
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/BubbleBogoSort.java b/src/sorts/BubbleBogoSort.java
new file mode 100644
index 00000000..844011f6
--- /dev/null
+++ b/src/sorts/BubbleBogoSort.java
@@ -0,0 +1,63 @@
+package sorts;
+
+import templates.BogoSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class BubbleBogoSort extends BogoSorting {
+ public BubbleBogoSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Bubble Bogo");
+ this.setRunAllID("Bubble Bogo Sort");
+ this.setReportSortID("Bubble Bogosort");
+ this.setCategory("Exchange Sorts");
+ this.isComparisonBased(true); //Comparisons ARE used to swap elements
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(true);
+ this.setUnreasonableLimit(2048);
+ this.isBogoSort(true);
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLen, int bucketCount) {
+ while(!this.bogoIsSorted(array, currentLen)){
+ int index1 = (int) (Math.random() * (currentLen - 1));
+
+ Highlights.markArray(1, index1);
+
+ if(Reads.compare(array[index1], array[index1 + 1]) == 1){
+ Writes.swap(array, index1, index1 + 1, 1, true, false);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/BubbleSort.java b/src/sorts/BubbleSort.java
new file mode 100644
index 00000000..3cffb038
--- /dev/null
+++ b/src/sorts/BubbleSort.java
@@ -0,0 +1,69 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class BubbleSort extends Sort {
+ public BubbleSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Bubble");
+ this.setRunAllID("Bubble Sort");
+ this.setReportSortID("Bubblesort");
+ this.setCategory("Exchange Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ boolean sorted = false;
+
+ while(!sorted) {
+ sorted = true;
+ for(int i = 0; i < length - 1; i++) {
+ if(Reads.compare(array[i], array[i + 1]) == 1){
+ Writes.swap(array, i, i + 1, 0.075, true, false);
+ sorted = false;
+ }
+
+ Highlights.markArray(1, i);
+ Highlights.markArray(2, i + 1);
+ Delays.sleep(0.05);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/CircleSort.java b/src/sorts/CircleSort.java
new file mode 100644
index 00000000..0624bf52
--- /dev/null
+++ b/src/sorts/CircleSort.java
@@ -0,0 +1,43 @@
+package sorts;
+
+import templates.CircleSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+Copyright (c) rosettacode.org.
+Permission is granted to copy, distribute and/or modify this document
+under the terms of the GNU Free Documentation License, Version 1.2
+or any later version published by the Free Software Foundation;
+with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
+Texts. A copy of the license is included in the section entitled "GNU
+Free Documentation License".
+ *
+ */
+
+final public class CircleSort extends CircleSorting {
+ public CircleSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Circle");
+ this.setRunAllID("Circle Sort");
+ this.setReportSortID("Circlesort");
+ this.setCategory("Exchange Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ do {
+ Delays.sleep(1);
+ } while (this.circleSortRoutine(array, 0, length - 1, 0) != 0);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/CocktailBogoSort.java b/src/sorts/CocktailBogoSort.java
new file mode 100644
index 00000000..00a1de2e
--- /dev/null
+++ b/src/sorts/CocktailBogoSort.java
@@ -0,0 +1,53 @@
+package sorts;
+
+import templates.BogoSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+final public class CocktailBogoSort extends BogoSorting {
+ public CocktailBogoSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Cocktail Bogo");
+ this.setRunAllID("Cocktail Bogo Sort");
+ this.setReportSortID("Cocktail Bogosort");
+ this.setCategory("Distributive Sorts");
+ this.isComparisonBased(false); //Comparisons are not used to swap elements
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(true);
+ this.setUnreasonableLimit(512);
+ this.isBogoSort(true);
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLen, int bucketCount) {
+ int minIterator = 0;
+ int maxIterator = currentLen - 1;
+
+ while(minIterator < maxIterator) {
+ boolean maxSorted = this.isMaxSorted(array, minIterator, maxIterator);
+ boolean minSorted = this.isMinSorted(array, maxIterator + 1, minIterator);
+
+ while(!maxSorted && !minSorted) {
+ this.bogoSwap(array, maxIterator + 1, minIterator);
+
+ maxSorted = this.isMaxSorted(array, minIterator, maxIterator);
+ minSorted = this.isMinSorted(array, maxIterator + 1, minIterator);
+ }
+
+ if(minSorted) {
+ //Highlights.markArray(1, minIterator);
+ minIterator++;
+ minSorted = false;
+ }
+ if(maxSorted) {
+ //Highlights.markArray(2, maxIterator);
+ maxIterator--;
+ maxSorted = false;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/CocktailMergeSort.java b/src/sorts/CocktailMergeSort.java
new file mode 100644
index 00000000..ca099844
--- /dev/null
+++ b/src/sorts/CocktailMergeSort.java
@@ -0,0 +1,64 @@
+package sorts;
+
+import templates.Sort;
+import templates.TimSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+// Inspired by Sorting Stuff's "Obscure Sorting Algorithms": https://www.youtube.com/watch?v=fWubJgIWyxQ
+
+// Basically, "Cocktail Merge Sort" is a hybrid between Cocktail Shaker Sort and TimSort. It starts by building
+// runs of TimSort's minimum length using Cocktail Shaker, then merges all these runs using TimSort. This
+// effectively replaces Binary Insertion Sort, used for building runs in TimSort. Big-O analysis would still say
+// this is constant time, as the minrun value is not dependent on the number of elements we are sorting, but
+// Cocktail Shaker has worse constant factors than Insertion Sort. So basically, this is just for fun.
+// But hey, why not? ;)
+
+public class CocktailMergeSort extends Sort {
+ private TimSorting timSortInstance; // TimSort cannot be simply written off as an abstract class, as it creates an instance of itself
+ // in order to track its state. Plus, it contains both instance and static methods, requiring even
+ // more refactoring, which would be just doing unnecessary busy work. Instead of what we've done for
+ // the rest of the algorithms, we'll favor composition over inheritance here and pass "util" objects
+ // to it.
+
+ public CocktailMergeSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Cocktail Merge");
+ this.setRunAllID("Cocktail Merge Sort");
+ this.setReportSortID("Cocktail Mergesort");
+ this.setCategory("Hybrid Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ CocktailShakerSort cocktailShaker = new CocktailShakerSort(Delays, Highlights, Reads, Writes);
+ int minRunLen = TimSorting.minRunLength(currentLength);
+
+ if (currentLength == minRunLen) {
+ cocktailShaker.runSort(array, currentLength, bucketCount);
+ }
+ else {
+ int i = 0;
+ for (; i <= (currentLength - minRunLen); i += minRunLen) {
+ cocktailShaker.customSort(array, i, i + minRunLen);
+ }
+ if (i + minRunLen > currentLength) {
+ cocktailShaker.customSort(array, i, currentLength);
+ }
+
+ Highlights.clearAllMarks();
+
+ this.timSortInstance = new TimSorting(array, currentLength, this.Delays, this.Highlights, this.Reads, this.Writes);
+ TimSorting.sort(this.timSortInstance, array, currentLength);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/CocktailShakerSort.java b/src/sorts/CocktailShakerSort.java
new file mode 100644
index 00000000..34e20f8d
--- /dev/null
+++ b/src/sorts/CocktailShakerSort.java
@@ -0,0 +1,87 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class CocktailShakerSort extends Sort {
+ public CocktailShakerSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Cocktail Shaker");
+ this.setRunAllID("Cocktail Shaker Sort");
+ this.setReportSortID("Cocktail Shaker Sort");
+ this.setCategory("Exchange Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ private void cocktailShaker(int[] array, int start, int end, double sleep) {
+ int i = start;
+ while(i < ((end / 2) + start)) {
+ for(int j = i; j < end + start - i - 1; j++) {
+ if(Reads.compare(array[j], array[j + 1]) == 1) {
+ Writes.swap(array, j, j + 1, sleep, true, false);
+ }
+
+ Highlights.markArray(1, j);
+ Highlights.markArray(2, j + 1);
+
+ Delays.sleep(sleep / 2);
+ }
+ for(int j = end + start - i - 1; j > i; j--){
+ if(Reads.compare(array[j], array[j - 1]) == -1) {
+ Writes.swap(array, j, j - 1, sleep, true, false);
+ }
+
+ Highlights.markArray(1, j);
+ Highlights.markArray(2, j - 1);
+
+ Delays.sleep(sleep / 2);
+ }
+
+ i++;
+ }
+ }
+
+ public void customSort(int[] array, int start, int end) {
+ this.cocktailShaker(array, start, end, 1);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ this.cocktailShaker(array, 0, length, 0.1);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/CombSort.java b/src/sorts/CombSort.java
new file mode 100644
index 00000000..f52aadbe
--- /dev/null
+++ b/src/sorts/CombSort.java
@@ -0,0 +1,57 @@
+package sorts;
+
+import templates.CombSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+The MIT License (MIT)
+
+Copyright (c) 2012 Daniel Imms, http://www.growingwiththeweb.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+final public class CombSort extends CombSorting {
+ public CombSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Comb");
+ this.setRunAllID("Comb Sort");
+ this.setReportSortID("Combsort");
+ this.setCategory("Exchange Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+
+ //Default options
+ this.setShrinkFactor(4); //Index 4 of Shrink Factors array
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ this.combSort(this.ArrayVisualizer, array, currentLength, false);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/CountingSort.java b/src/sorts/CountingSort.java
new file mode 100644
index 00000000..1283336f
--- /dev/null
+++ b/src/sorts/CountingSort.java
@@ -0,0 +1,81 @@
+package sorts;
+
+import java.util.Arrays;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class CountingSort extends Sort {
+ public CountingSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Counting");
+ this.setRunAllID("Counting Sort");
+ this.setReportSortID("Counting Sort");
+ this.setCategory("Distributive Sorts");
+ this.isComparisonBased(false);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ int max = Reads.analyzeMax(array, length, 0, false);
+
+ int[] output = Arrays.copyOf(array, length);
+ int[] counts = new int[max + 1];
+
+ for (int i = 0; i < length; i++) {
+ Writes.write(counts, array[i], counts[array[i]] + 1, 1, false, true);
+ Highlights.markArray(1, i);
+ }
+
+ for (int i = 1; i < counts.length; i++) {
+ Writes.write(counts, i, counts[i] + counts[i - 1], 1, true, true);
+ }
+
+ for (int i = length - 1; i >= 0; i--) {
+ output[counts[array[i]] - 1] = array[i];
+ counts[array[i]]--;
+ }
+
+ // Extra loop to simulate the results from the "output" array being written
+ // to the visual array.
+ for (int i = length - 1; i >= 0; i--) {
+ Writes.write(array, i, output[i], 1, true, false);
+ Writes.changeTempWrites(1);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/CycleSort.java b/src/sorts/CycleSort.java
new file mode 100644
index 00000000..310e3b26
--- /dev/null
+++ b/src/sorts/CycleSort.java
@@ -0,0 +1,109 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+Copyright (c) rosettacode.org
+Permission is granted to copy, distribute and/or modify this document
+under the terms of the GNU Free Documentation License, Version 1.3
+or any later version published by the Free Software Foundation;
+with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
+Texts. A copy of the license is included in the section entitled "GNU
+Free Documentation License".
+ *
+ */
+
+final public class CycleSort extends Sort {
+ public CycleSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Cycle");
+ this.setRunAllID("Cycle Sort");
+ this.setReportSortID("Cyclesort");
+ this.setCategory("Selection Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ for (int cycleStart = 0; cycleStart < length - 1; cycleStart++) {
+ int val = array[cycleStart];
+
+ /*
+ Count the number of values that are smaller
+ than val since cycleStart
+ */
+
+ int pos = cycleStart;
+ Highlights.markArray(3, pos);
+
+ for (int i = cycleStart + 1; i < length; i++) {
+ Highlights.markArray(2, i);
+ Delays.sleep(0.01);
+
+ if (Reads.compare(array[i], val) == -1) {
+ pos++;
+ Highlights.markArray(1, pos);
+ Delays.sleep(0.01);
+ }
+
+ }
+
+ // there aren't any
+ if (pos == cycleStart) {
+ Highlights.markArray(1, pos);
+ continue;
+ }
+
+ // Skip duplicates
+ while (val == array[pos]) {
+ pos++;
+ Highlights.markArray(1, pos);
+ }
+
+ // Put val into final position
+ int tmp = array[pos];
+ Writes.write(array, pos, val, 0.02, true, false);
+ val = tmp;
+
+ /*
+ Repeat as long as we can find values to swap
+ otherwise start new cycle
+ */
+ while (pos != cycleStart) {
+ pos = cycleStart;
+ Highlights.markArray(3, pos);
+
+ for (int i = cycleStart + 1; i < length; i++) {
+ Highlights.markArray(2, i);
+ Delays.sleep(0.01);
+
+ if (Reads.compare(array[i], val) == -1) {
+ pos++;
+ Highlights.markArray(1, pos);
+ Delays.sleep(0.01);
+ }
+ }
+
+ while (val == array[pos]) {
+ pos++;
+ Highlights.markArray(1, pos);
+ }
+
+ tmp = array[pos];
+ Writes.write(array, pos, val, 0.02, true, false);
+ val = tmp;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/DoubleSelectionSort.java b/src/sorts/DoubleSelectionSort.java
new file mode 100644
index 00000000..40cc772d
--- /dev/null
+++ b/src/sorts/DoubleSelectionSort.java
@@ -0,0 +1,88 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class DoubleSelectionSort extends Sort {
+ public DoubleSelectionSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Double Selection");
+ this.setRunAllID("Double Selection Sort");
+ this.setReportSortID("Double Selection Sort");
+ this.setCategory("Selection Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ int left = 0;
+ int right = length - 1;
+ int smallest = 0;
+ int biggest = 0;
+
+ while(left <= right) {
+ for(int i = left; i <= right; i++) {
+ Highlights.markArray(3, i);
+
+ if(Reads.compare(array[i], array[biggest]) == 1) {
+ biggest = i;
+ Highlights.markArray(1, biggest);
+ Delays.sleep(0.01);
+ }
+ if(Reads.compare(array[i], array[smallest]) == -1) {
+ smallest = i;
+ Highlights.markArray(2, smallest);
+ Delays.sleep(0.01);
+ }
+
+ Delays.sleep(0.01);
+ }
+ if(biggest == left)
+ biggest = smallest;
+
+ Writes.swap(array, left, smallest, 0.02, true, false);
+ Writes.swap(array, right, biggest, 0.02, true, false);
+
+ left++;
+ right--;
+
+ smallest = left;
+ biggest = right;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/DualPivotQuickSort.java b/src/sorts/DualPivotQuickSort.java
new file mode 100644
index 00000000..8d930fce
--- /dev/null
+++ b/src/sorts/DualPivotQuickSort.java
@@ -0,0 +1,102 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+final public class DualPivotQuickSort extends Sort {
+ public DualPivotQuickSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Dual-Pivot Quick");
+ this.setRunAllID("Dual-Pivot Quick Sort");
+ this.setReportSortID("Dual-Pivot Quicksort");
+ this.setCategory("Exchange Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ /*
+ * This example of a basic Dual-Pivot Quicksort may be found here, written by Sebastian Wild (Sebastian on StackOverflow):
+ * https://cs.stackexchange.com/questions/24092/dual-pivot-quicksort-reference-implementation
+ */
+ private void dualPivot(int[] a, int left, int right) {
+ if (right > left) {
+ if (Reads.compare(a[left], a[right]) == 1) {
+ Writes.swap(a, left, right, 1, true, false);
+ }
+
+ int p = a[left];
+ int q = a[right];
+
+ int l = left + 1;
+ int g = right - 1;
+ int k = l;
+
+ while (k <= g)
+ {
+ Delays.sleep(0.1);
+
+ if (Reads.compare(a[k], p) == -1) {
+ Writes.swap(a, k, l, 1, true, false);
+ Highlights.clearMark(2);
+
+ l++;
+ Highlights.markArray(4, l);
+ }
+ else if (Reads.compare(a[k], q) >= 0) {
+ while (Reads.compare(a[g], q) == 1 && k < g) {
+ g--;
+ Highlights.markArray(3, g);
+ Delays.sleep(0.2);
+ }
+
+ Writes.swap(a, k, g, 1, true, false);
+ Highlights.clearMark(2);
+
+ g--;
+ Highlights.markArray(3, g);
+
+ if (Reads.compare(a[k], p) == -1) {
+ Writes.swap(a, k, l, 0.2, true, false);
+ Highlights.clearMark(2);
+
+ ++l;
+ Highlights.markArray(4, l);
+ }
+ }
+ ++k;
+ Highlights.markArray(1, k);
+ Delays.sleep(0.2);
+ }
+
+ --l;
+ ++g;
+
+ Writes.swap(a, left, l, 1, true, false);
+
+ Highlights.clearMark(2);
+
+ Writes.swap(a, right, g, 1, true, false);
+
+ Highlights.clearMark(2);
+ Highlights.clearMark(3);
+ Highlights.clearMark(4);
+
+ this.dualPivot(a, left, l - 1);
+ this.dualPivot(a, l + 1, g - 1);
+ this.dualPivot(a, g + 1, right);
+ }
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ this.dualPivot(array, 0, currentLength - 1);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/ExchangeBogoSort.java b/src/sorts/ExchangeBogoSort.java
new file mode 100644
index 00000000..d5453ba3
--- /dev/null
+++ b/src/sorts/ExchangeBogoSort.java
@@ -0,0 +1,46 @@
+package sorts;
+
+import templates.BogoSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+final public class ExchangeBogoSort extends BogoSorting {
+ public ExchangeBogoSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Exchange Bogo");
+ this.setRunAllID("Exchange Bogo Sort");
+ this.setReportSortID("Exchange Bogosort");
+ this.setCategory("Exchange Sorts");
+ this.isComparisonBased(true); //Comparisons ARE used to swap elements
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(true);
+ this.setUnreasonableLimit(1024);
+ this.isBogoSort(true);
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLen, int bucketCount) {
+ while(!bogoIsSorted(array, currentLen)){
+ int index1 = (int) (Math.random() * currentLen),
+ index2 = (int) (Math.random() * currentLen);
+
+ Highlights.markArray(1, index1);
+ Highlights.markArray(2, index2);
+
+ if(index1 < index2) {
+ if(Reads.compare(array[index1], array[index2]) == 1){
+ Writes.swap(array, index1, index2, 1, true, false);
+ }
+ }
+ else {
+ if(Reads.compare(array[index1], array[index2]) == -1){
+ Writes.swap(array, index1, index2, 1, true, false);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/FlashSort.java b/src/sorts/FlashSort.java
new file mode 100644
index 00000000..075350ad
--- /dev/null
+++ b/src/sorts/FlashSort.java
@@ -0,0 +1,295 @@
+package sorts;
+
+import java.util.Arrays;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+final public class FlashSort extends Sort {
+ public FlashSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Flash");
+ this.setRunAllID("Flash Sort");
+ this.setReportSortID("Flashsort");
+ this.setCategory("Distributive Sorts");
+ this.isComparisonBased(false);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ private static int indexOfIntArray(int[] array, int length, int key) {
+ int returnvalue = -1;
+ for (int i = 0; i < length; ++i) {
+ if (key == array[i]) {
+ returnvalue = i;
+ break;
+ }
+ }
+ return returnvalue;
+ }
+
+ // The flashsort algorithm is attributed to Karl-Dietrich Neubert
+ // The translation to C++ is provided by Clint Jed Casper
+ // Refactored in Java by MusicTheorist
+ //
+ // sorts an array in place in O(n) time using 20% of the
+ // memory used by the array for storing intermediate,
+ // temporary computations
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ if(length == 0) return;
+
+ //20% of the number of elements or 0.2n classes will
+ //be used to distribute the input data set into
+ //there must be at least 2 classes (hence the addition)
+ int m = (int)((0.2 * length) + 2);
+
+ //-------CLASS FORMATION-------
+
+ //O(n)
+ //compute the max and min values of the input data
+ int min, max, maxIndex;
+ min = max = array[0];
+ maxIndex = 0;
+
+ for(int i = 1; i < length - 1; i += 2)
+ {
+ int small;
+ int big;
+ int bigIndex;
+
+ Highlights.markArray(1, i);
+
+ //which is bigger A(i) or A(i+1)
+ if(Reads.compare(array[i], array[i + 1]) == -1)
+ {
+ small = array[i];
+ big = array[i + 1];
+ bigIndex = i + 1;
+ }
+ else
+ {
+ big = array[i];
+ bigIndex = i;
+ small = array[i + 1];
+ }
+
+ if(big > max)
+ {
+ max = big;
+ maxIndex = bigIndex;
+ }
+
+ if(small < min)
+ {
+ min = small;
+ }
+
+ Delays.sleep(1);
+ }
+
+ //do the last element
+ Highlights.markArray(1, length - 1);
+ if(Reads.compare(array[length - 1], min) == -1)
+ {
+ min = array[length - 1];
+ }
+ else if(Reads.compare(array[length - 1], max) == 1)
+ {
+ max = array[length - 1];
+ maxIndex = length - 1;
+ }
+
+ Delays.sleep(1);
+ Highlights.clearMark(1);
+
+ if(max == min)
+ {
+ //all the elements are the same
+ return;
+ }
+
+ //dynamically allocate the storage for L
+ //note that L is in the range 1...m (hence
+ //the extra 1)
+ int[] L = new int[m + 1];
+
+ //O(m)
+ //initialize L to contain all zeros (L[0] is unused)
+ for(int t = 1; t <= m; t++)
+ {
+ Writes.write(L, t, 0, 0, false, true);
+ }
+
+ //O(n)
+ //use the function K(A(i)) = 1 + INT((m-1)(A(i)-Amin)/(Amax-Amin))
+ //to classify each A(i) into a number from 1...m
+ //(note that this is mainly just a percentage calculation)
+ //and then store a count of each distinct class K in L(K)
+ //For instance, if there are 22 A(i) values that fall into class
+ //K == 5 then the count in L(5) would be 22
+
+ //IMPORTANT: note that the class K == m only has elements equal to Amax
+
+ //precomputed constant
+ double c = (m - 1.0) / (max - min);
+ int K;
+ for(int h = 0; h < length; h++)
+ {
+
+ Highlights.markArray(1, h);
+
+ //classify the A(i) value
+ K = ((int)((array[h] - min) * c)) + 1;
+
+ //add one to the count for this class
+ Writes.write(L, K, L[K] + 1, 1, false, true);
+ }
+ Highlights.clearMark(1);
+
+ //O(m)
+ //sum over each L(i) such that each L(i) contains
+ //the number of A(i) values that are in the ith
+ //class or lower (see counting sort for more details)
+ for(K = 2; K <= m; K++)
+ {
+ Writes.write(L, K, L[K] + L[K - 1], 0, false, true);
+ }
+
+ //-------PERMUTATION-------
+
+ //swap the max value with the first value in the array
+ Writes.swap(array, maxIndex, 0, 1, true, false);
+ Highlights.clearMark(1);
+ Highlights.clearMark(2);
+
+ //Except when being iterated upwards,
+ //j always points to the first A(i) that starts
+ //a new class boundary && that class hasn't yet
+ //had all of its elements moved inside its borders;
+
+ //This is called a cycle leader since you know
+ //that you can begin permuting again here. You know
+ //this because it is the lowest index of the class
+ //and as such A(j) must be out of place or else all
+ //the elements of this class have already been placed
+ //within the borders of the this class (which means
+ //j wouldn't be pointing to this A(i) in the first place)
+ int j = 0;
+
+ //K is the class of an A(i) value. It is always in the range 1..m
+ K = m;
+
+ //the number of elements that have been moved
+ //into their correct class
+ int numMoves = 0;
+
+ //O(n)
+ //permute elements into their correct class; each
+ //time the class that j is pointing to fills up
+ //then iterate j to the next cycle leader
+ //
+ //do not use the n - 1 optimization because that last element
+ //will not have its count decreased (this causes trouble with
+ //determining the correct classSize in the last step)
+ while(numMoves < length)
+ {
+ //if j does not point to the beginning of a class
+ //that has at least 1 element still needing to be
+ //moved to within the borders of the class then iterate
+ //j upward until such a class is found (such a class
+ //must exist). In other words, find the next cycle leader
+ while(j >= L[K])
+ {
+ j++;
+ //classify the A(j) value
+ K = ((int)((array[j] - min) * c)) + 1;
+ }
+
+ //evicted always holds the value of an element whose location
+ //in the array is free to be written into //aka FLASH
+ int evicted = array[j];
+
+ //while j continues to meet the condition that it is
+ //pointing to the start of a class that has at least one
+ //element still outside its borders (the class isn't full)
+ while(j < L[K])
+ {
+ //compute the class of the evicted value
+ K = ((int)((evicted - min) * c)) + 1;
+
+ //get a location that is inside the evicted
+ //element's class boundaries
+ int location = L[K] - 1;
+
+ //swap the value currently residing at the new
+ //location with the evicted value
+ int temp = array[location];
+ Writes.write(array, location, evicted, 1, false, false);
+ Highlights.markArray(1, location);
+ evicted = temp;
+
+ //decrease the count for this class
+ //see counting sort for why this is done
+ Writes.write(L, K, L[K] - 1, 0, false, true);
+
+ //another element was moved
+ numMoves++;
+ }
+ }
+ Highlights.clearMark(1);
+
+ //-------RECURSION or STRAIGHT INSERTION-------
+
+ //if the classes do not have the A(i) values uniformly distributed
+ //into each of them then insertion sort will not produce O(n) results;
+
+ //look for classes that have too many elements; ideally each class
+ //(except the topmost or K == m class) should have about n/m elements;
+ //look for classes that exceed n/m elements by some threshold AND have
+ //more than some minimum number of elements to flashsort recursively
+
+ //if the class has 25% more elements than it should
+ int threshold = (int)(1.25 * ((length / m) + 1));
+ int minElements = 30;
+
+ //for each class decide whether to insertion sort its members
+ //or recursively flashsort its members;
+ //skip the K == m class because it is already sorted
+ //since all of the elements have the same value
+
+ for(K = m - 1; K >= 1; K--)
+ {
+ //determine the number of elments in the Kth class
+ int classSize = L[K + 1] - L[K];
+
+ //if the class size is larger than expected but not
+ //so small that insertion sort could make quick work
+ //of it then...
+ if(classSize > threshold && classSize > minElements)
+ {
+ //...attempt to flashsort the class. This will work
+ //well if the elements inside the class are uniformly
+ //distributed throughout the class otherwise it will
+ //perform badly, O(n^2) worst case, since we will have
+ //performed another classification and permutation step
+ //and not succeeded in making the problem significantly
+ //smaller for the next level of recursion. However,
+ //progress is assured since at each level the elements
+ //with the maximum value will get sorted.
+ runSort(Arrays.copyOfRange(array, L[K], L[K + 1]), classSize, 0);
+ }
+ }
+
+ InsertionSort insertSorter = new InsertionSort(this.Delays, this.Highlights, this.Reads, this.Writes);
+ insertSorter.customInsertSort(array, 0, length, 0.75, false);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/FlippedMinHeapSort.java b/src/sorts/FlippedMinHeapSort.java
new file mode 100644
index 00000000..6313c2b5
--- /dev/null
+++ b/src/sorts/FlippedMinHeapSort.java
@@ -0,0 +1,67 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+Copyright (c) rosettacode.org.
+Permission is granted to copy, distribute and/or modify this document
+under the terms of the GNU Free Documentation License, Version 1.2
+or any later version published by the Free Software Foundation;
+with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
+Texts. A copy of the license is included in the section entitled "GNU
+Free Documentation License".
+ *
+ */
+
+/*
+modified by Lucy Phipps from ../templates/HeapSorting.java and MinHeapSort.java
+the only real changes are subtracting every array access from (length - 1)
+and removing the Writes.reverse() at the end
+the rest is just compacting the code a bit
+*/
+
+final public class FlippedMinHeapSort extends Sort {
+ public FlippedMinHeapSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+ this.setSortPromptID("Flipped Min Heap");
+ this.setRunAllID("Flipped Min Heap Sort");
+ this.setReportSortID("Flipped Reverse Heapsort");
+ this.setCategory("Selection Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+ private void siftDown(int[] array, int length, int root, int dist) {
+ while (root <= dist / 2) {
+ int leaf = 2 * root;
+ if (leaf < dist && Reads.compare(array[length - leaf], array[length - leaf - 1]) == 1) {
+ leaf++;
+ }
+ Highlights.markArray(1, length - root);
+ Highlights.markArray(2, length - leaf);
+ Delays.sleep(1);
+ if (Reads.compare(array[length - root], array[length - leaf]) == 1) {
+ Writes.swap(array, length - root, length - leaf, 0, true, false);
+ root = leaf;
+ } else break;
+ }
+ }
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ for (int i = length / 2; i >= 1; i--) {
+ siftDown(array, length, i, length);
+ }
+ for (int i = length; i > 1; i--) {
+ Writes.swap(array, length - 1, length - i, 1, true, false);
+ siftDown(array, length, 1, i - 1);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/GnomeSort.java b/src/sorts/GnomeSort.java
new file mode 100644
index 00000000..6d8729aa
--- /dev/null
+++ b/src/sorts/GnomeSort.java
@@ -0,0 +1,51 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+final public class GnomeSort extends Sort {
+ public GnomeSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Gnome");
+ this.setRunAllID("Gnome Sort");
+ this.setReportSortID("Gnomesort");
+ this.setCategory("Exchange Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ // Code retrieved from http://www.algostructure.com/sorting/gnomesort.php
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ for (int i = 1; i < length;)
+ {
+ if (Reads.compare(array[i], array[i-1]) >= 0)
+ {
+ i++;
+ Highlights.markArray(1, i);
+ Delays.sleep(0.04);
+ }
+ else
+ {
+ Writes.swap(array, i, i - 1, 0.02, true, false);
+
+ Highlights.clearMark(2);
+
+ if (i > 1) {
+ i--;
+ Highlights.markArray(1, i);
+ Delays.sleep(0.02);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/GrailSort.java b/src/sorts/GrailSort.java
new file mode 100644
index 00000000..2651e1bc
--- /dev/null
+++ b/src/sorts/GrailSort.java
@@ -0,0 +1,98 @@
+package sorts;
+
+import templates.GrailSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+The MIT License (MIT)
+
+Copyright (c) 2013 Andrey Astrelin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/********* Grail sorting *********************************/
+/* */
+/* (c) 2013 by Andrey Astrelin */
+/* Refactored by MusicTheorist */
+/* */
+/* Stable sorting that works in O(N*log(N)) worst time */
+/* and uses O(1) extra memory */
+/* */
+/* Define int / SortComparator */
+/* and then call GrailSort() function */
+/* */
+/* For sorting w/ fixed external buffer (512 items) */
+/* use GrailSortWithBuffer() */
+/* */
+/* For sorting w/ dynamic external buffer (sqrt(length)) */
+/* use GrailSortWithDynBuffer() */
+/* */
+/*********************************************************/
+
+final public class GrailSort extends GrailSorting {
+ private int bufferType = 0;
+
+ public GrailSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Grail");
+ //this.setRunAllID("Grail Sort (Block Merge Sort)");
+ this.setRunAllID("Grail Sort [Block Merge Sort]");
+ this.setReportSortID("Grailsort");
+ this.setCategory("Hybrid Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ // 0 for no buffer, 1 for static buffer, 2 for dynamic buffer
+ public void chooseBuffer(int choice) {
+ bufferType = choice;
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ if(bufferType == 0) this.grailCommonSort(array, 0, length, null, 0, 0);
+ else if(bufferType == 1) {
+ int[] ExtBuf = new int[this.getStaticBuffer()];
+ this.grailCommonSort(array, 0, length, ExtBuf, 0, this.getStaticBuffer());
+ }
+ else if(bufferType == 2) {
+ int tempLen = 1;
+ while(tempLen * tempLen < length) tempLen *= 2;
+ int[] DynExtBuf = new int[tempLen];
+ this.grailCommonSort(array, 0, length, DynExtBuf, 0, tempLen);
+ }
+ else {
+ try {
+ throw new Exception("Invalid Grail buffer!!");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/GravitySort.java b/src/sorts/GravitySort.java
new file mode 100644
index 00000000..7df2ce27
--- /dev/null
+++ b/src/sorts/GravitySort.java
@@ -0,0 +1,99 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class GravitySort extends Sort {
+ public GravitySort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Gravity");
+ this.setRunAllID("Gravity Sort");
+ //this.setRunAllID("Gravity (Bead) Sort");
+ this.setReportSortID("Beadsort");
+ this.setCategory("Distributive Sorts");
+ this.isComparisonBased(false);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(true);
+ this.setUnreasonableLimit(4096);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ int max = Reads.analyzeMax(array, length, 1, true);
+ int[][] abacus = new int[length][max];
+
+ for(int i = 0; i < length; i++) {
+ for(int j = 0; j < array[i]; j++) {
+ Writes.multiDimWrite(abacus, i, abacus[0].length - j - 1, 1, 0, true, true);
+ }
+ }
+
+ //apply gravity
+ for(int i = 0; i < abacus[0].length; i++) {
+ for(int j = 0; j < abacus.length; j++) {
+ Highlights.markArray(1, j);
+ if(abacus[j][i] == 1) {
+ //Drop it
+ int dropPos = j;
+
+ Writes.startLap();
+ while(dropPos + 1 < abacus.length && abacus[dropPos][i] == 1) {
+ dropPos++;
+ }
+ Writes.stopLap();
+
+ if(abacus[dropPos][i] == 0) {
+ Writes.multiDimWrite(abacus, j, i, 0, 0, true, true);
+ Writes.multiDimWrite(abacus, dropPos, i, 1, 0, true, true);
+ }
+ }
+ }
+
+ int count = 0;
+ for(int x = 0; x < abacus.length; x++){
+ count = 0;
+
+ Writes.startLap();
+ for(int y = 0; y < abacus[0].length; y++) {
+ count += abacus[x][y];
+ }
+ Writes.stopLap();
+
+ Writes.write(array, x, count, 0.001, true, false);
+ }
+ Highlights.markArray(2, length - i - 1);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/HolyGrailSort.java b/src/sorts/HolyGrailSort.java
new file mode 100644
index 00000000..4e06c63d
--- /dev/null
+++ b/src/sorts/HolyGrailSort.java
@@ -0,0 +1,115 @@
+package sorts;
+
+import templates.HolyGrailSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+The MIT License (MIT)
+
+Copyright (c) 2013 Andrey Astrelin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/********* Holy Grail Sorting ****************************/
+/* */
+/* (c) 2013 by Andrey Astrelin */
+/* (c) 2019 modified by John Reynolds (MusicTheorist) */
+/* */
+/* Holy Grail Sort is a variant of Grail Sort, an */
+/* implementation of Block Merge Sort. */
+/* */
+/* Grail Sort is a stable sort that works in O(n log n) */
+/* worst time and uses O(1) extra memory. It splits */
+/* sorted ranges of an array into "blocks" of length */
+/* sqrt(n), combines pairs of ranges via swapping */
+/* blocks, and locally merges blocks together using */
+/* an internal buffer. It maintains stability by */
+/* collecting unique values throughout the array called */
+/* "keys", and tagging blocks with these keys to track */
+/* their movements (think of "key-value pairs"). */
+/* */
+/* Holy Grail Sort improves on these ideas by using */
+/* Binary Insertion Sort instead of Insertion Sort, */
+/* reducing the number of write operations in select */
+/* cases of Grail Sort's "Rotate" method, and */
+/* implementing a bidirectional internal buffer. These */
+/* optimizations result in an algorithm that is at least */
+/* 30% faster than Grail Sort. */
+/* */
+/* Define int / SortComparator */
+/* and then call HolyGrailSort() function */
+/* */
+/* For sorting w/ fixed external buffer (512 items) */
+/* use HolyGrailSortWithBuffer() */
+/* */
+/* For sorting w/ dynamic external buffer (sqrt(length)) */
+/* use HolyGrailSortWithDynBuffer() */
+/* */
+/*********************************************************/
+
+final public class HolyGrailSort extends HolyGrailSorting {
+ private int bufferType = 0;
+
+ public HolyGrailSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Holy Grail");
+ this.setRunAllID("Holy Grail Sort (Block Merge Sort)");
+ this.setReportSortID("Holygrailsort");
+ this.setCategory("Hybrid Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ // 0 for no buffer, 1 for static buffer, 2 for dynamic buffer
+ public void chooseBuffer(int choice) {
+ bufferType = choice;
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ if(bufferType == 0) this.grailCommonSort(array, 0, length, null, 0, 0);
+ else if(bufferType == 1) {
+ int[] ExtBuf = new int[this.getStaticBuffer()];
+ this.grailCommonSort(array, 0, length, ExtBuf, 0, this.getStaticBuffer());
+ }
+ else if(bufferType == 2) {
+ int tempLen = 1;
+ while(tempLen * tempLen < length) tempLen *= 2;
+ int[] DynExtBuf = new int[tempLen];
+ this.grailCommonSort(array, 0, length, DynExtBuf, 0, tempLen);
+ }
+ else {
+ try {
+ throw new Exception("Invalid Holy Grail buffer!!");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/HybridCombSort.java b/src/sorts/HybridCombSort.java
new file mode 100644
index 00000000..10c54346
--- /dev/null
+++ b/src/sorts/HybridCombSort.java
@@ -0,0 +1,57 @@
+package sorts;
+
+import templates.CombSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+The MIT License (MIT)
+
+Copyright (c) 2012 Daniel Imms, http://www.growingwiththeweb.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+final public class HybridCombSort extends CombSorting {
+ public HybridCombSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Hybrid Comb");
+ this.setRunAllID("Hybrid Comb Sort");
+ this.setReportSortID("Hybrid Combsort");
+ this.setCategory("Hybrid Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+
+ //Default options
+ this.setShrinkFactor(4); //Index 4 of Shrink Factors array
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ this.combSort(this.ArrayVisualizer, array, currentLength, true);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/InPlaceLSDRadixSort.java b/src/sorts/InPlaceLSDRadixSort.java
new file mode 100644
index 00000000..0c973d5d
--- /dev/null
+++ b/src/sorts/InPlaceLSDRadixSort.java
@@ -0,0 +1,88 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class InPlaceLSDRadixSort extends Sort {
+ public InPlaceLSDRadixSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("In-Place LSD Radix");
+ //this.setRunAllID("In-Place LSD Radix Sort, Base 2");
+ this.setRunAllID("In-Place LSD Radix Sort, Base 10");
+ this.setReportSortID("In-Place LSD Radix Sort");
+ this.setCategory("Distributive Sorts");
+ this.isComparisonBased(false);
+ this.isBucketSort(true);
+ this.isRadixSort(true);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ this.setRunAllID("In-Place LSD Radix Sort, Base " + bucketCount);
+
+ int pos = 0;
+ int[] vregs = new int[bucketCount-1];
+
+ int maxpower = Reads.analyzeMaxLog(array, length, bucketCount, 0.5, true);
+
+ for(int p = 0; p <= maxpower; p++){
+ for(int i = 0; i < vregs.length; i++) {
+ Writes.write(vregs, i, length - 1, 0, false, true);
+ }
+
+ pos = 0;
+
+ for(int i = 0; i < length; i++){
+ int digit = Reads.getDigit(array[pos], p, bucketCount);
+
+ if(digit == 0) {
+ pos++;
+ Highlights.markArray(0, pos);
+ }
+ else {
+ for(int j = 0; j < vregs.length;j++)
+ Highlights.markArray(j + 1, vregs[j]);
+
+ Writes.multiSwap(array, pos, vregs[digit - 1], bucketCount / 10000d, false, false);
+
+ for(int j = digit - 1; j > 0; j--) {
+ Writes.write(vregs, j - 1, vregs[j - 1] - 1, 0, false, true);
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/InPlaceMergeSort.java b/src/sorts/InPlaceMergeSort.java
new file mode 100644
index 00000000..3afadac6
--- /dev/null
+++ b/src/sorts/InPlaceMergeSort.java
@@ -0,0 +1,93 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class InPlaceMergeSort extends Sort {
+ public InPlaceMergeSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("In-Place Merge");
+ this.setRunAllID("In-Place Merge Sort");
+ this.setReportSortID("In-Place Mergesort");
+ this.setCategory("Merge Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ private void push(int[] array, int low, int high) {
+ for(int i = low; i < high; i++) {
+ if(Reads.compare(array[i], array[i + 1]) == 1) {
+ Writes.swap(array, i, i + 1, 0.035, true, false);
+ }
+ }
+ }
+
+ private void merge(int[] array, int min, int max, int mid) {
+ int i = min;
+ while(i <= mid) {
+ if(Reads.compare(array[i], array[mid + 1]) == 1){
+ Writes.swap(array, i, mid + 1, 0.035, true, false);
+ push(array, mid + 1, max);
+ }
+ i++;
+ Delays.sleep(0.035);
+ }
+ }
+
+ private void mergeSort(int[] array, int min,int max) {
+ if(max - min == 0) { //only one element.
+ Delays.sleep(1); //no swap
+ }
+ else if(max - min == 1) { //only two elements and swaps them
+ if(Reads.compare(array[min], array[max]) == 1) {
+ Writes.swap(array, min, max, 0.035, true, false);
+ }
+ }
+ else {
+ int mid = ((int) Math.floor((min + max) / 2)); //The midpoint
+
+ mergeSort(array, min, mid); //sort the left side
+ mergeSort(array, mid + 1, max); //sort the right side
+ merge(array, min, max, mid); //combines them
+ }
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ this.mergeSort(array, 0, currentLength - 1);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/InsertionSort.java b/src/sorts/InsertionSort.java
new file mode 100644
index 00000000..d0a42fc4
--- /dev/null
+++ b/src/sorts/InsertionSort.java
@@ -0,0 +1,59 @@
+package sorts;
+
+import templates.InsertionSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class InsertionSort extends InsertionSorting {
+ public InsertionSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Insertion");
+ this.setRunAllID("Insertion Sort");
+ this.setReportSortID("Insertsort");
+ this.setCategory("Insertion Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ public void customInsertSort(int[] array, int start, int end, double sleep, boolean auxwrite) {
+ this.insertionSort(array, start, end, sleep, auxwrite);
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ this.insertionSort(array, 0, currentLength, 0.015, false);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/IntroCircleSort.java b/src/sorts/IntroCircleSort.java
new file mode 100644
index 00000000..43bfb7e7
--- /dev/null
+++ b/src/sorts/IntroCircleSort.java
@@ -0,0 +1,52 @@
+package sorts;
+
+import templates.CircleSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+Copyright (c) rosettacode.org.
+Permission is granted to copy, distribute and/or modify this document
+under the terms of the GNU Free Documentation License, Version 1.2
+or any later version published by the Free Software Foundation;
+with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
+Texts. A copy of the license is included in the section entitled "GNU
+Free Documentation License".
+ *
+ */
+
+final public class IntroCircleSort extends CircleSorting {
+ public IntroCircleSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Intro Circle");
+ this.setRunAllID("Introspective Circle Sort");
+ this.setReportSortID("Introspective Circlesort");
+ this.setCategory("Hybrid Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ int iterations = 0;
+ int threshold = (int) (Math.log(length) / Math.log(2)) / 2;
+
+ do {
+ iterations++;
+
+ if(iterations >= threshold) {
+ BinaryInsertionSort binaryInserter = new BinaryInsertionSort(this.Delays, this.Highlights, this.Reads, this.Writes);
+ binaryInserter.customBinaryInsert(array, 0, length, 0.1);
+ break;
+ }
+ } while (this.circleSortRoutine(array, 0, length - 1, 0) != 0);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/IntroSort.java b/src/sorts/IntroSort.java
new file mode 100644
index 00000000..3b14abc0
--- /dev/null
+++ b/src/sorts/IntroSort.java
@@ -0,0 +1,145 @@
+package sorts;
+
+import java.util.Arrays;
+
+import templates.JErrorPane;
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+// original Copyright Ralph Unden,
+// http://ralphunden.net/content/tutorials/a-guide-to-introsort/?q=a-guide-to-introsort
+// Modifications: Bernhard Pfahringer
+// changes include: local insertion sort, no global array
+
+final public class IntroSort extends Sort {
+ private MaxHeapSort heapSorter;
+
+ private int middle;
+ private int sizeThreshold = 16;
+
+ public IntroSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Intro");
+ //this.setRunAllID("Introspective Sort (std::sort)");
+ this.setRunAllID("Introspective Sort [std::sort]");
+ this.setReportSortID("Introsort");
+ this.setCategory("Hybrid Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ private static int floorLogBaseTwo(int a) {
+ return (int) (Math.floor(Math.log(a) / Math.log(2)));
+ }
+
+ // Swaps the median of arr[left], arr[mid], and arr[right] to index left.
+ // taken from gcc source code found here: https://gcc.gnu.org/onlinedocs/gcc-4.7.2/libstdc++/api/a01462_source.html
+ private int gccmedianof3(int[] arr, int left, int mid, int right) {
+ if (Reads.compare(arr[left], arr[mid]) < 0) {
+ if (Reads.compare(arr[mid], arr[right]) < 0) {
+ Writes.swap(arr, left, mid, 1, true, false);
+ }
+ else if (Reads.compare(arr[left], arr[right]) < 0) {
+ Writes.swap(arr, left, right, 1, true, false);
+ }
+ }
+ else if (Reads.compare(arr[left], arr[right]) < 0) {
+ middle = left;
+ Highlights.markArray(3, left);
+ return arr[left];
+ }
+ else if (Reads.compare(arr[mid], arr[right]) < 0) {
+ Writes.swap(arr, left, right, 1, true, false);
+ }
+ else {
+ Writes.swap(arr, left, mid, 1, true, false);
+ }
+ middle = left;
+ Highlights.markArray(3, left);
+ return arr[left];
+ }
+
+ private int medianof3(int[] arr, int left, int mid, int right) {
+ if(Reads.compare(arr[right], arr[left]) == -1) {
+ Writes.swap(arr, left, right, 1, true, false);
+ }
+ if(Reads.compare(arr[mid], arr[left]) == -1) {
+ Writes.swap(arr, mid, left, 1, true, false);
+ }
+ if(Reads.compare(arr[right], arr[mid]) == -1) {
+ Writes.swap(arr, right, mid, 1, true, false);
+ }
+ middle = mid;
+ Highlights.markArray(3, mid);
+ return arr[mid];
+ }
+
+ private int partition(int[] a, int lo, int hi, int x) {
+ int i = lo, j = hi;
+ while (true) {
+ while (Reads.compare(a[i], x) == -1) {
+ Highlights.markArray(1, i);
+ Delays.sleep(0.5);
+ i++;
+ }
+
+ j--;
+
+ while (Reads.compare(x, a[j]) == -1) {
+ Highlights.markArray(2, j);
+ Delays.sleep(0.5);
+ j--;
+ }
+
+ if(!(i < j)) {
+ Highlights.markArray(1, i);
+ Delays.sleep(0.5);
+ return i;
+ }
+
+ // Follow the pivot and highlight it.
+ if(i == middle) {
+ Highlights.markArray(3, j);
+ }
+ if(j == middle) {
+ Highlights.markArray(3, i);
+ }
+
+ Writes.swap(a, i, j, 1, true, false);
+ i++;
+ }
+ }
+
+ private void introsortLoop (int[] a, int lo, int hi, int depthLimit) {
+ while (hi - lo > sizeThreshold) {
+ if (depthLimit == 0) {
+ heapSorter.customHeapSort(a, lo, hi, 1);
+ return;
+ }
+ depthLimit--;
+ int p = partition(a, lo, hi, medianof3(a, lo, lo + ((hi - lo) / 2), hi - 1));
+ introsortLoop(a, p, hi, depthLimit);
+ hi = p;
+ }
+ return;
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ heapSorter = new MaxHeapSort(this.Delays, this.Highlights, this.Reads, this.Writes);
+
+ introsortLoop(array, 0, length, 2 * floorLogBaseTwo(length));
+ Highlights.clearAllMarks();
+
+ InsertionSort sort = new InsertionSort(this.Delays, this.Highlights, this.Reads, this.Writes);
+ sort.customInsertSort(array, 0, length, 0.5, false);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/IterativeBitonicSort.java b/src/sorts/IterativeBitonicSort.java
new file mode 100644
index 00000000..a0bada8c
--- /dev/null
+++ b/src/sorts/IterativeBitonicSort.java
@@ -0,0 +1,49 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ * This version of Bitonic Sort was taken from here, written by Nikos Pitsianis:
+ * https://www2.cs.duke.edu/courses/fall08/cps196.1/Pthreads/bitonic.c
+ */
+
+final public class IterativeBitonicSort extends Sort {
+ public IterativeBitonicSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Iterative Bitonic");
+ this.setRunAllID("Iterative Bitonic Sort");
+ this.setReportSortID("Iterative Bitonic Sort");
+ this.setCategory("Concurrent Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ int i, j, k;
+
+ for(k = 2; k <= currentLength; k = 2 * k) {
+ for(j = k >> 1; j > 0; j = j >> 1) {
+ for(i = 0; i < currentLength; i++) {
+ int ij = i ^ j;
+
+ if((ij) > i) {
+ if((i & k) == 0 && Reads.compare(array[i], array[ij]) == 1)
+ Writes.swap(array, i, ij, 1, true, false);
+ if((i & k) != 0 && Reads.compare(array[i], array[ij]) == -1)
+ Writes.swap(array, i, ij, 1, true, false);
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/IterativeOddEvenMergeSort.java b/src/sorts/IterativeOddEvenMergeSort.java
new file mode 100644
index 00000000..ee2443ae
--- /dev/null
+++ b/src/sorts/IterativeOddEvenMergeSort.java
@@ -0,0 +1,44 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ * This version of Odd-Even Merge Sort was taken from here, written by wkpark on StackOverflow:
+ * https://stackoverflow.com/questions/34426337/how-to-fix-this-non-recursive-odd-even-merge-sort-algorithm
+ */
+
+final public class IterativeOddEvenMergeSort extends Sort {
+ public IterativeOddEvenMergeSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Iter. Odd-Even Merge");
+ this.setRunAllID("Iterative Odd-Even Merge Sort");
+ this.setReportSortID("Iterative Odd-Even Mergesort");
+ this.setCategory("Concurrent Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ for (int p = 1; p < currentLength; p += p)
+ for (int k = p; k > 0; k /= 2)
+ for (int j = k % p; j + k < currentLength; j += k + k)
+ for (int i = 0; i < k; i++)
+ if ((i + j)/(p + p) == (i + j + k)/(p + p)) {
+ Highlights.markArray(1, i + j);
+ Highlights.markArray(2, i + j + k);
+ Delays.sleep(1);
+ if(Reads.compare(array[i + j], array[i + j + k]) > 0)
+ Writes.swap(array, i + j, i + j + k, 1, true, false);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/IterativePairwiseSort.java b/src/sorts/IterativePairwiseSort.java
new file mode 100644
index 00000000..0255cd0b
--- /dev/null
+++ b/src/sorts/IterativePairwiseSort.java
@@ -0,0 +1,102 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+Copyright (c) 2019 PiotrGrochowski
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class IterativePairwiseSort extends Sort {
+ public IterativePairwiseSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Iterative Pairwise");
+ this.setRunAllID("Iterative Pairwise Sorting Network");
+ this.setReportSortID("Iterative Pairwise Sort");
+ this.setCategory("Concurrent Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ private void iterativepairwise(int[] array, int length, double sleep) {
+ int a = 1;
+ int b = 0;
+ int c = 0;
+ int d = 0;
+ int e = 0;
+ while (a < length){
+ b = a;
+ c = 0;
+ while (b < length){
+ Delays.sleep(sleep);
+ Highlights.markArray(1, b - a);
+ Highlights.markArray(2, b);
+ if(Reads.compare(array[b - a], array[b]) == 1) {
+ Writes.swap(array, b - a, b, sleep, true, false);
+ }
+ c = (c + 1) % a;
+ b++;
+ if (c == 0){
+ b += a;
+ }
+ }
+ a *= 2;
+ }
+ a /= 4;
+ e = 1;
+ while (a > 0){
+ d = e;
+ while (d > 0){
+ b = ((d + 1) * a);
+ c = 0;
+ while (b < length){
+ Delays.sleep(sleep);
+ Highlights.markArray(1, b - (d * a));
+ Highlights.markArray(2, b);
+ if(Reads.compare(array[b - (d * a)], array[b]) == 1) {
+ Writes.swap(array, b - (d * a), b, sleep, true, false);
+ }
+ c = (c + 1) % a;
+ b++;
+ if (c == 0){
+ b += a;
+ }
+ }
+ d /= 2;
+ }
+ a /= 2;
+ e = (e * 2) + 1;
+ }
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ this.iterativepairwise(array, length, 1);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/LLQuickSort.java b/src/sorts/LLQuickSort.java
new file mode 100644
index 00000000..b948b261
--- /dev/null
+++ b/src/sorts/LLQuickSort.java
@@ -0,0 +1,53 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+final public class LLQuickSort extends Sort {
+ public LLQuickSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("LL Quick");
+ this.setRunAllID("Quick Sort, Left/Left Pointers");
+ this.setReportSortID("Left/Left Quicksort");
+ this.setCategory("Exchange Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ private int partition(int[] array, int lo, int hi) {
+ int pivot = array[hi];
+ int i = lo;
+
+ for(int j = lo; j < hi; j++) {
+ Highlights.markArray(1, j);
+ if(Reads.compare(array[j], pivot) < 0) {
+ Writes.swap(array, i, j, 1, true, false);
+ i++;
+ }
+ Delays.sleep(1);
+ }
+ Writes.swap(array, i, hi, 1, true, false);
+ return i;
+ }
+
+ private void quickSort(int[] array, int lo, int hi) {
+ if(lo < hi) {
+ int p = this.partition(array, lo, hi);
+ this.quickSort(array, lo, p - 1);
+ this.quickSort(array, p + 1, hi);
+ }
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ this.quickSort(array, 0, currentLength - 1);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/LRQuickSort.java b/src/sorts/LRQuickSort.java
new file mode 100644
index 00000000..e88bb428
--- /dev/null
+++ b/src/sorts/LRQuickSort.java
@@ -0,0 +1,75 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+final public class LRQuickSort extends Sort {
+ public LRQuickSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("LR Quick");
+ this.setRunAllID("Quick Sort, Left/Right Pointers");
+ this.setReportSortID("Left/Right Quicksort");
+ this.setCategory("Exchange Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ // Thanks to Timo Bingmann for providing a good reference for Quick Sort w/ LR pointers.
+ private void quickSort(int[] a, int p, int r) {
+ int pivot = p + (r - p) / 2;
+ int x = a[pivot];
+
+ int i = p;
+ int j = r;
+
+ Highlights.markArray(3, pivot);
+
+ while (i <= j) {
+ while (Reads.compare(a[i], x) == -1){
+ i++;
+ Highlights.markArray(1, i);
+ Delays.sleep(0.5);
+ }
+ while (Reads.compare(a[j], x) == 1){
+ j--;
+ Highlights.markArray(2, j);
+ Delays.sleep(0.5);
+ }
+
+ if (i <= j) {
+ // Follow the pivot and highlight it.
+ if(i == pivot) {
+ Highlights.markArray(3, j);
+ }
+ if(j == pivot) {
+ Highlights.markArray(3, i);
+ }
+
+ Writes.swap(a, i, j, 1, true, false);
+
+ i++;
+ j--;
+ }
+ }
+
+ if(p < j) {
+ this.quickSort(a, p, j);
+ }
+ if(i < r) {
+ this.quickSort(a, i, r);
+ }
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ this.quickSort(array, 0, currentLength - 1);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/LSDRadixSort.java b/src/sorts/LSDRadixSort.java
new file mode 100644
index 00000000..5a6a1d3c
--- /dev/null
+++ b/src/sorts/LSDRadixSort.java
@@ -0,0 +1,78 @@
+package sorts;
+
+import java.util.ArrayList;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class LSDRadixSort extends Sort {
+ public LSDRadixSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("LSD Radix");
+ this.setRunAllID("Least Significant Digit Radix Sort, Base 4");
+ this.setReportSortID("Least Significant Digit Radixsort");
+ this.setCategory("Distributive Sorts");
+ this.isComparisonBased(false);
+ this.isBucketSort(true);
+ this.isRadixSort(true);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ this.setRunAllID("Least Significant Digit Radix Sort, Base " + bucketCount);
+
+ int highestpower = Reads.analyzeMaxLog(array, length, bucketCount, 0.5, true);
+
+ @SuppressWarnings("unchecked")
+ ArrayList[] registers = new ArrayList[bucketCount];
+
+ for(int i = 0; i < bucketCount; i++)
+ registers[i] = new ArrayList<>();
+
+ for(int p = 0; p <= highestpower; p++){
+ for(int i = 0; i < length; i++){
+ Highlights.markArray(1, i);
+
+ int digit = Reads.getDigit(array[i], p, bucketCount);
+ registers[digit].add(array[i]);
+
+ Writes.mockWrite(length, digit, array[i], 1);
+ }
+
+ Writes.fancyTranscribe(array, length, registers, bucketCount * 0.8);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/LazyStableSort.java b/src/sorts/LazyStableSort.java
new file mode 100644
index 00000000..92c3e814
--- /dev/null
+++ b/src/sorts/LazyStableSort.java
@@ -0,0 +1,64 @@
+package sorts;
+
+import templates.GrailSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+The MIT License (MIT)
+
+Copyright (c) 2013 Andrey Astrelin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/********* Lazy stable sorting ***********/
+/* */
+/* (c) 2013 by Andrey Astrelin */
+/* Refactored by MusicTheorist */
+/* */
+/* Simple in-place stable sorting, */
+/* methods copied from Grail Sort */
+/* */
+/*****************************************/
+
+final public class LazyStableSort extends GrailSorting {
+ public LazyStableSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Lazy Stable");
+ this.setRunAllID("Lazy Stable Sort");
+ this.setReportSortID("Lazy Stable Sort");
+ this.setCategory("Merge Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ this.grailLazyStableSort(array, 0, length);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/LessBogoSort.java b/src/sorts/LessBogoSort.java
new file mode 100644
index 00000000..813322a3
--- /dev/null
+++ b/src/sorts/LessBogoSort.java
@@ -0,0 +1,37 @@
+package sorts;
+
+import templates.BogoSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+final public class LessBogoSort extends BogoSorting {
+ public LessBogoSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Less Bogo");
+ this.setRunAllID("Less Bogo Sort");
+ this.setReportSortID("Less Bogosort");
+ this.setCategory("Distributive Sorts");
+ this.isComparisonBased(false); //Comparisons are not used to swap elements
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(true);
+ this.setUnreasonableLimit(512);
+ this.isBogoSort(true);
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLen, int bucketCount) {
+ int iterator = 0;
+
+ while(iterator != currentLen) {
+ while(!this.isMinSorted(array, currentLen, iterator)) {
+ this.bogoSwap(array, currentLen, iterator);
+ }
+ //Highlights.markArray(1, iterator);
+ iterator++;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/MSDRadixSort.java b/src/sorts/MSDRadixSort.java
new file mode 100644
index 00000000..52b49190
--- /dev/null
+++ b/src/sorts/MSDRadixSort.java
@@ -0,0 +1,97 @@
+package sorts;
+
+import java.util.ArrayList;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class MSDRadixSort extends Sort {
+ public MSDRadixSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("MSD Radix");
+ //this.setRunAllID("Most Significant Digit Radix Sort");
+ this.setRunAllID("Most Significant Digit Radix Sort, Base 4");
+ this.setReportSortID("Most Significant Digit Radixsort");
+ this.setCategory("Distributive Sorts");
+ this.isComparisonBased(false);
+ this.isBucketSort(true);
+ this.isRadixSort(true);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ private void radixMSD(int[] array, int length, int min, int max, int radix, int pow) {
+ if(min >= max || pow < 0)
+ return;
+
+ Highlights.markArray(2, max - 1);
+ Highlights.markArray(3, min);
+
+ @SuppressWarnings("unchecked")
+ ArrayList[] registers = new ArrayList[radix];
+
+ for(int i = 0; i < radix; i++)
+ registers[i] = new ArrayList<>();
+
+ for(int i = min; i < max; i++) {
+ Highlights.markArray(1, i);
+
+ int digit = Reads.getDigit(array[i], pow, radix);
+ registers[digit].add(array[i]);
+
+ Writes.mockWrite(length, digit, array[i], 1);
+ }
+
+ Highlights.clearMark(2);
+ Highlights.clearMark(3);
+
+ Writes.transcribeMSD(array, registers, 0, min, 0.8, true, false);
+
+ int sum = 0;
+ for(int i = 0; i < registers.length; i++) {
+ this.radixMSD(array, length, sum + min, sum + min + registers[i].size(), radix, pow-1);
+
+ sum += registers[i].size();
+ registers[i].clear();
+ Writes.changeTempWrites(registers[i].size());
+ }
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ int highestpower = Reads.analyzeMaxLog(array, length, bucketCount, 0.5, true);
+
+ radixMSD(array, length, 0, length, bucketCount, highestpower);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/MaxHeapSort.java b/src/sorts/MaxHeapSort.java
new file mode 100644
index 00000000..96df9c4a
--- /dev/null
+++ b/src/sorts/MaxHeapSort.java
@@ -0,0 +1,45 @@
+package sorts;
+
+import templates.HeapSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+Copyright (c) rosettacode.org.
+Permission is granted to copy, distribute and/or modify this document
+under the terms of the GNU Free Documentation License, Version 1.2
+or any later version published by the Free Software Foundation;
+with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
+Texts. A copy of the license is included in the section entitled "GNU
+Free Documentation License".
+ *
+ */
+
+final public class MaxHeapSort extends HeapSorting {
+ public MaxHeapSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Max Heap");
+ this.setRunAllID("Max Heap Sort");
+ this.setReportSortID("Heapsort");
+ this.setCategory("Selection Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ public void customHeapSort(int[] array, int start, int length, double sleep) {
+ this.heapSort(array, start, length, sleep, true);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ this.heapSort(array, 0, length, 1, true);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/MergeSort.java b/src/sorts/MergeSort.java
new file mode 100644
index 00000000..7f84fccc
--- /dev/null
+++ b/src/sorts/MergeSort.java
@@ -0,0 +1,55 @@
+package sorts;
+
+import templates.MergeSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class MergeSort extends MergeSorting {
+ public MergeSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Merge");
+ this.setRunAllID("Merge Sort");
+ this.setReportSortID("Mergesort");
+ this.setCategory("Merge Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ this.mergeSort(array, length, false);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/MinHeapSort.java b/src/sorts/MinHeapSort.java
new file mode 100644
index 00000000..41264696
--- /dev/null
+++ b/src/sorts/MinHeapSort.java
@@ -0,0 +1,41 @@
+package sorts;
+
+import templates.HeapSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+Copyright (c) rosettacode.org.
+Permission is granted to copy, distribute and/or modify this document
+under the terms of the GNU Free Documentation License, Version 1.2
+or any later version published by the Free Software Foundation;
+with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
+Texts. A copy of the license is included in the section entitled "GNU
+Free Documentation License".
+ *
+ */
+
+final public class MinHeapSort extends HeapSorting {
+ public MinHeapSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Min Heap");
+ this.setRunAllID("Min Heap Sort");
+ this.setReportSortID("Reverse Heapsort");
+ this.setCategory("Selection Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ this.heapSort(array, 0, length, 1, false);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/OddEvenMergeSort.java b/src/sorts/OddEvenMergeSort.java
new file mode 100644
index 00000000..1fec0c98
--- /dev/null
+++ b/src/sorts/OddEvenMergeSort.java
@@ -0,0 +1,71 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ * This version of Odd-Even Merge Sort was taken from here, written by H.W. Lang:
+ * http://www.inf.fh-flensburg.de/lang/algorithmen/sortieren/networks/oemen.htm
+ */
+
+final public class OddEvenMergeSort extends Sort {
+ public OddEvenMergeSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Odd-Even Merge");
+ this.setRunAllID("Batcher's Odd-Even Merge Sort");
+ this.setReportSortID("Odd-Even Mergesort");
+ this.setCategory("Concurrent Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ private void oddEvenMergeCompare(int[] array, int i, int j) {
+ if (Reads.compare(array[i], array[j]) > 0)
+ Writes.swap(array, i, j, 1, true, false);
+ }
+
+ /** lo is the starting position and
+ * n is the length of the piece to be merged,
+ * r is the distance of the elements to be compared
+ */
+ private void oddEvenMerge(int[] array, int lo, int n, int r) {
+ int m = r * 2;
+ if (m < n) {
+ this.oddEvenMerge(array, lo, n, m); // even subsequence
+ this.oddEvenMerge(array, lo+r, n, m); // odd subsequence
+
+ for (int i = lo + r; i + r < lo + n; i += m) {
+ Highlights.markArray(1, i);
+ Highlights.markArray(2, i + r);
+ this.oddEvenMergeCompare(array, i, i + r);
+ }
+ }
+ else {
+ Highlights.markArray(1, lo + r);
+ Highlights.markArray(2, lo);
+ this.oddEvenMergeCompare(array, lo, lo+r);
+ }
+ }
+
+ private void oddEvenMergeSort(int[] array, int lo, int n) {
+ if (n > 1) {
+ int m = n / 2;
+ this.oddEvenMergeSort(array, lo, m);
+ this.oddEvenMergeSort(array, lo + m, m);
+ this.oddEvenMerge(array, lo, n, 1);
+ }
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ this.oddEvenMergeSort(array, 0, currentLength);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/OddEvenSort.java b/src/sorts/OddEvenSort.java
new file mode 100644
index 00000000..3ee15457
--- /dev/null
+++ b/src/sorts/OddEvenSort.java
@@ -0,0 +1,58 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ * This version of Odd-Even Sort was taken from here, written by Rachit Belwariar:
+ * https://www.geeksforgeeks.org/odd-even-sort-brick-sort/
+ */
+
+final public class OddEvenSort extends Sort {
+ public OddEvenSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Odd-Even");
+ this.setRunAllID("Odd-Even Sort");
+ this.setReportSortID("Odd-Even Sort");
+ this.setCategory("Exchange Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ boolean sorted = false;
+
+ while (!sorted) {
+ sorted = true;
+
+ for (int i = 1; i < length - 1; i += 2) {
+ if(Reads.compare(array[i], array[i + 1]) == 1) {
+ Writes.swap(array, i, i + 1, 0.075, true, false);
+ sorted = false;
+ }
+
+ Highlights.markArray(1, i);
+ Delays.sleep(0.025);
+ }
+
+ for (int i = 0; i < length - 1; i += 2) {
+ if(Reads.compare(array[i], array[i + 1]) == 1) {
+ Writes.swap(array, i, i + 1, 0.075, true, false);
+ sorted = false;
+ }
+
+ Highlights.markArray(2, i);
+ Delays.sleep(0.025);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/OptimizedDualPivotQuickSort.java b/src/sorts/OptimizedDualPivotQuickSort.java
new file mode 100644
index 00000000..6c530e59
--- /dev/null
+++ b/src/sorts/OptimizedDualPivotQuickSort.java
@@ -0,0 +1,130 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+// code retrieved from https://codeblab.com/wp-content/uploads/2009/09/DualPivotQuicksort.pdf
+// written by Vladimir Yaroslavskiy
+
+final public class OptimizedDualPivotQuickSort extends Sort {
+ private InsertionSort insertSorter;
+
+ public OptimizedDualPivotQuickSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Opti. Dual-Pivot Quick");
+ //this.setRunAllID("Optimized Dual-Pivot Quick Sort");
+ this.setRunAllID("Optimized Dual-Pivot Quick Sort [Arrays.sort]");
+ this.setReportSortID("Optimized Dual-Pivot Quicksort");
+ this.setCategory("Hybrid Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ private void dualPivot(int[] array, int left, int right, int divisor) {
+ int length = right - left;
+
+ // insertion sort for tiny array
+ if(length < 27) {
+ Highlights.clearMark(2);
+ insertSorter.customInsertSort(array, left, right + 1, 1, false);
+ return;
+ }
+
+ int third = length / divisor;
+
+ // "medians"
+ int med1 = left + third;
+ int med2 = right - third;
+
+ if(med1 <= left) {
+ med1 = left + 1;
+ }
+ if(med2 >= right) {
+ med2 = right - 1;
+ }
+ if(Reads.compare(array[med1], array[med2]) == -1) {
+ Writes.swap(array, med1, left, 1, true, false);
+ Writes.swap(array, med2, right, 1, true, false);
+ }
+ else {
+ Writes.swap(array, med1, right, 1, true, false);
+ Writes.swap(array, med2, left, 1, true, false);
+ }
+
+ // pivots
+ int pivot1 = array[left];
+ int pivot2 = array[right];
+
+ // pointers
+ int less = left + 1;
+ int great = right - 1;
+
+ // sorting
+ for(int k = less; k <= great; k++) {
+ if(Reads.compare(array[k], pivot1) == -1) {
+ Writes.swap(array, k, less++, 1, true, false);
+ }
+ else if(Reads.compare(array[k], pivot2) == 1) {
+ while(k < great && Reads.compare(array[great], pivot2) == 1) {
+ great--;
+ Highlights.markArray(3, great);
+ Delays.sleep(1);
+ }
+ Writes.swap(array, k, great--, 1, true, false);
+ Highlights.clearMark(3);
+
+ if(Reads.compare(array[k], pivot1) == -1) {
+ Writes.swap(array, k, less++, 1, true, false);
+ }
+ }
+ }
+
+ // swaps
+ int dist = great - less;
+
+ if(dist < 13) {
+ divisor++;
+ }
+ Writes.swap(array, less - 1, left, 1, true, false);
+ Writes.swap(array, great + 1, right, 1, true, false);
+
+ // subarrays
+ this.dualPivot(array, left, less - 2, divisor);
+ this.dualPivot(array, great + 2, right, divisor);
+
+ // equal elements
+ if(dist > length - 13 && pivot1 != pivot2) {
+ for(int k = less; k <= great; k++) {
+ if(Reads.compare(array[k], pivot1) == 0) {
+ Writes.swap(array, k, less++, 1, true, false);
+ }
+ else if(Reads.compare(array[k], pivot2) == 0) {
+ Writes.swap(array, k, great--, 1, true, false);
+
+ if(Reads.compare(array[k], pivot1) == 0) {
+ Writes.swap(array, k, less++, 1, true, false);
+ }
+ }
+ }
+ }
+
+ // subarray
+ if(pivot1 < pivot2) {
+ this.dualPivot(array, less, great, divisor);
+ }
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ this.insertSorter = new InsertionSort(Delays, Highlights, Reads, Writes);
+ this.dualPivot(array, 0, currentLength - 1, 3);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/PancakeSort.java b/src/sorts/PancakeSort.java
new file mode 100644
index 00000000..852fed1d
--- /dev/null
+++ b/src/sorts/PancakeSort.java
@@ -0,0 +1,78 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ * IDeserve
+ * https://www.youtube.com/c/IDeserve">https://www.youtube.com/c/IDeserve
+ * Given an array, sort the array using Pancake sort.
+ *
+ * @author Saurabh
+ * https://www.ideserve.co.in/learn/pancake-sorting
+ */
+
+final public class PancakeSort extends Sort {
+ public PancakeSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Pancake");
+ this.setRunAllID("Pancake Sorting");
+ this.setReportSortID("Pancake Sort");
+ this.setCategory("Miscellaneous Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ private boolean sorted(int[] array, int length) {
+ for(int i = 0; i < length; i++) {
+ Highlights.markArray(1, i);
+ Delays.sleep(0.025);
+
+ if(Reads.compare(array[i], array[i + 1]) > 0) return false;
+ }
+ return true;
+ }
+
+ private int findMax(int[] arr, int end) {
+ int index = 0, max = Integer.MIN_VALUE;
+ for (int i = 0; i <= end; i++) {
+ Highlights.markArray(1, i);
+
+ if (Reads.compare(arr[i], max) == 1) {
+ max = arr[i];
+ index = i;
+ Highlights.markArray(2, i);
+ }
+
+ Delays.sleep(0.025);
+ Highlights.clearMark(1);
+ }
+ return index;
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ for (int i = length - 1; i >= 0; i--) {
+ if(!this.sorted(array, i)) {
+ int index = this.findMax(array, i);
+
+ if(index == 0) {
+ Writes.reversal(array, 0, i, 0.05, true, false);
+ }
+ else if(index != i) {
+ Writes.reversal(array, 0, index, 0.05, true, false);
+ Writes.reversal(array, 0, i, 0.05, true, false);
+ }
+ }
+ else break;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/PatienceSort.java b/src/sorts/PatienceSort.java
new file mode 100644
index 00000000..c02494e3
--- /dev/null
+++ b/src/sorts/PatienceSort.java
@@ -0,0 +1,121 @@
+package sorts;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.PriorityQueue;
+import java.util.Stack;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+ Copyright (c) rosettacode.org.
+ Permission is granted to copy, distribute and/or modify this document
+ under the terms of the GNU Free Documentation License, Version 1.2
+ or any later version published by the Free Software Foundation;
+ with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
+ Texts. A copy of the license is included in the section entitled "GNU
+ Free Documentation License".
+ *
+ */
+
+final public class PatienceSort extends Sort {
+ public PatienceSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Patience");
+ this.setRunAllID("Patience Sort");
+ this.setReportSortID("Patience Sort");
+ this.setCategory("Insertion Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ final private class Pile extends Stack implements Comparable {
+ private static final long serialVersionUID = 1L;
+
+ public int compare(Pile y) {
+ return Reads.compare(peek(), y.peek());
+ }
+
+ @Override
+ public int compareTo(Pile y) {
+ return peek().compareTo(y.peek());
+ }
+ }
+
+ private void binarySearch(ArrayList list, Pile find) {
+ int at = list.size() / 2;
+ int change = list.size() / 4;
+
+ while(list.get(at).compare(find) != 0 && change > 0){
+ Highlights.markArray(1, at);
+ Delays.sleep(0.5);
+
+ if(list.get(at).compare(find) < 0)
+ at += change;
+ else
+ at -= change;
+
+ change /= 2;
+ }
+
+ Highlights.markArray(1, at);
+ Delays.sleep(0.5);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ ArrayList piles = new ArrayList<>();
+
+ // sort into piles
+ for (int x = 0; x < length; x++) {
+ Pile newPile = new Pile();
+
+ Highlights.markArray(2, x);
+ Writes.mockWrite(length, Math.min(newPile.size(), length - 1), array[x], 1);
+
+ newPile.push(array[x]);
+
+ int i = Collections.binarySearch(piles, newPile);
+ if(!piles.isEmpty()) {
+ this.binarySearch(piles, newPile);
+ }
+ if (i < 0) i = ~i;
+ if (i != piles.size()) {
+ Writes.mockWrite(length, Math.min(piles.get(i).size(), length - 1), array[x], 0);
+ piles.get(i).push(array[x]);
+ }
+ else {
+ Writes.mockWrite(length, Math.min(piles.size(), length - 1), newPile.get(0), 0);
+ piles.add(newPile);
+ }
+ }
+
+ Highlights.clearMark(2);
+
+ // priority queue allows us to retrieve least pile efficiently
+ PriorityQueue heap = new PriorityQueue<>(piles);
+
+ for (int c = 0; c < length; c++) {
+ Writes.mockWrite(length, Math.min(heap.size(), length - 1), 0, 0);
+ Pile smallPile = heap.poll();
+
+ Writes.mockWrite(length, Math.min(smallPile.size(), length - 1), 0, 0);
+ Writes.write(array, c, smallPile.pop(), 1, true, false);
+
+ if (!smallPile.isEmpty()) {
+ Writes.mockWrite(length, Math.min(heap.size(), length - 1), smallPile.get(0), 0);
+ heap.offer(smallPile);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/PigeonholeSort.java b/src/sorts/PigeonholeSort.java
new file mode 100644
index 00000000..8fc85d10
--- /dev/null
+++ b/src/sorts/PigeonholeSort.java
@@ -0,0 +1,72 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ * THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE").
+ * THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS
+ * LICENSE OR COPYRIGHT LAW IS PROHIBITED.
+ *
+ * BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE.
+ * TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN
+ * CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
+ */
+
+// Code refactored from the Python implementation found here: https://en.wikipedia.org/wiki/Pigeonhole_sort
+
+final public class PigeonholeSort extends Sort {
+ public PigeonholeSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Pigeonhole");
+ this.setRunAllID("Pigeonhole Sort");
+ this.setReportSortID("Pigeonhole Sort");
+ this.setCategory("Distributive Sorts");
+ this.isComparisonBased(false);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ int min = Integer.MAX_VALUE;
+ int max = Integer.MIN_VALUE;
+
+ for(int i = 0; i < length; i++) {
+ if(array[i] < min) {
+ min = array[i];
+ }
+ if(array[i] > max) {
+ max = array[i];
+ }
+ }
+
+ int mi = min;
+ int size = max - mi + 1;
+ int[] holes = new int[size];
+
+ for(int x = 0; x < length; x++) {
+ Writes.write(holes, array[x] - mi, holes[array[x] - mi] + 1, 1, false, true);
+ Highlights.markArray(1, x);
+ }
+
+ int j = 0;
+
+ for(int count = 0; count < size; count++) {
+ while(holes[count] > 0) {
+ Writes.write(holes, count, holes[count] - 1, 0, false, true);
+ Writes.write(array, j, count + mi, 1, false, false);
+
+ Highlights.markArray(1, j);
+ j++;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/PoplarHeapSort.java b/src/sorts/PoplarHeapSort.java
new file mode 100644
index 00000000..701c3e96
--- /dev/null
+++ b/src/sorts/PoplarHeapSort.java
@@ -0,0 +1,205 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017-2019 Morwenn
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+public class PoplarHeapSort extends Sort {
+ public PoplarHeapSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Poplar Heap");
+ this.setRunAllID("Poplar Heap Sort");
+ this.setReportSortID("Poplar Heapsort");
+ this.setCategory("Selection Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ ////////////////////////////////////////////////////////////
+ // Generic helper functions
+ ////////////////////////////////////////////////////////////
+
+ // Returns 2^floor(log2(n)), assumes n > 0
+ private static int hyperfloor(int n) {
+ return (int) Math.pow(2, Math.floor(Math.log(n) / Math.log(2)));
+ }
+
+ // Insertion sort which doesn't check for empty sequences
+ private void unchecked_insertion_sort(int[] array, int first, int last) {
+ for (int cur = first + 1; cur != last; ++cur) {
+ int sift = cur;
+ int sift_1 = cur - 1;
+
+ // Compare first so we can avoid 2 moves for
+ // an element already positioned correctly
+ if (Reads.compare(array[sift], array[sift_1]) == -1) {
+ int tmp = array[sift];
+ do {
+ Writes.write(array, sift, array[sift_1], 0.25, true, false);
+ } while (--sift != first && Reads.compare(tmp, array[--sift_1]) == -1);
+ Writes.write(array, sift, tmp, 0.25, true, false);
+ }
+ }
+ }
+
+ private void insertion_sort(int[] array, int first, int last) {
+ if (first == last) return;
+ unchecked_insertion_sort(array, first, last);
+ }
+
+ ////////////////////////////////////////////////////////////
+ // Poplar heap specific helper functions
+ ////////////////////////////////////////////////////////////
+
+ private void sift(int[] array, int first, int size) {
+ if (size < 2) return;
+
+ int root = first + (size - 1);
+ int child_root1 = root - 1;
+ int child_root2 = first + (size / 2 - 1);
+
+ while (true) {
+ int max_root = root;
+ if (Reads.compare(array[max_root], array[child_root1]) == -1) {
+ max_root = child_root1;
+ }
+ if (Reads.compare(array[max_root], array[child_root2]) == -1) {
+ max_root = child_root2;
+ }
+ if (max_root == root) return;
+
+ Writes.swap(array, root, max_root, 0.75, true, false);
+ Highlights.clearMark(2);
+
+ size /= 2;
+ if (size < 2) return;
+
+ root = max_root;
+ child_root1 = root - 1;
+ child_root2 = max_root - (size - size / 2);
+ }
+ }
+
+ private void pop_heap_with_size(int[] array, int first, int last, int size) {
+ int poplar_size = PoplarHeapSort.hyperfloor(size + 1) - 1;
+ int last_root = last - 1;
+ int bigger = last_root;
+ int bigger_size = poplar_size;
+
+ // Look for the bigger poplar root
+ int it = first;
+ while (true) {
+ int root = it + poplar_size - 1;
+ if (root == last_root) break;
+ if (Reads.compare(array[bigger], array[root]) == -1) {
+ bigger = root;
+ bigger_size = poplar_size;
+ }
+ it = root + 1;
+
+ size -= poplar_size;
+ poplar_size = PoplarHeapSort.hyperfloor(size + 1) - 1;
+ }
+
+ // If a poplar root was bigger than the last one, exchange
+ // them and sift
+ if (bigger != last_root) {
+ Writes.swap(array, bigger, last_root, 0.75, true, false);
+ Highlights.clearMark(2);
+ this.sift(array, bigger - (bigger_size - 1), bigger_size);
+ }
+ }
+
+ private void make_heap(int[] array, int first, int last) {
+ int size = last - first;
+ if (size < 2) return;
+
+ // A sorted collection is a valid poplar heap; whenever the heap
+ // is small, using insertion sort should be faster, which is why
+ // we start by constructing 15-element poplars instead of 1-element
+ // ones as the base case
+ int small_poplar_size = 15;
+ if (size <= small_poplar_size) {
+ this.unchecked_insertion_sort(array, first, last);
+ return;
+ }
+
+ // Determines the "level" of the poplars seen so far; the log2 of this
+ // variable will be used to make the binary carry sequence
+ int poplar_level = 1;
+
+ int it = first;
+ int next = it + small_poplar_size;
+ while (true) {
+ // Make a 15 element poplar
+ this.unchecked_insertion_sort(array, it, next);
+
+ int poplar_size = small_poplar_size;
+
+ // Bit trick iterate without actually having to compute log2(poplar_level)
+ for (int i = (poplar_level & (0 - poplar_level)) >> 1; i != 0; i >>= 1) {
+ it -= poplar_size;
+ poplar_size = 2 * poplar_size + 1;
+ this.sift(array, it, poplar_size);
+ ++next;
+ }
+
+ if ((last - next) <= small_poplar_size) {
+ this.insertion_sort(array, next, last);
+ return;
+ }
+
+ it = next;
+ next += small_poplar_size;
+ ++poplar_level;
+ }
+ }
+
+ private void sort_heap(int[] array, int first, int last) {
+ int size = last - first;
+ if (size < 2) return;
+
+ do {
+ this.pop_heap_with_size(array, first, last, size);
+ --last;
+ --size;
+ } while (size > 1);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ this.make_heap(array, 0, length);
+ this.sort_heap(array, 0, length);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/RecursiveBinaryQuickSort.java b/src/sorts/RecursiveBinaryQuickSort.java
new file mode 100644
index 00000000..5928fd5c
--- /dev/null
+++ b/src/sorts/RecursiveBinaryQuickSort.java
@@ -0,0 +1,47 @@
+package sorts;
+
+import templates.BinaryQuickSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/**
+ * Binary MSD Radix Sort / Binary Quicksort.
+ *
+ * Implemented as recursive decent, and via task queue, see:
+ * * binaryQuickSortRecursive, and
+ * * binaryQuickSort respectively.
+ *
+ * Both of which are in-place sorting algorithms, with the recursive utilizing
+ * the stack for divide-and-conquer, while the non-recursive utilizes a queue.
+ *
+ * Can be extended to support unsigned integers, by sorting the first bit rin
+ * reverse. Can be made stable at the cost of O(n) memory. Can be parallalized
+ * to O(log2(n)) subtasks / threads.
+ *
+ * @author Skeen
+ */
+
+final public class RecursiveBinaryQuickSort extends BinaryQuickSorting {
+ public RecursiveBinaryQuickSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Recurs. Binary Quick");
+ this.setRunAllID("Recursive Binary Quick Sort");
+ this.setReportSortID("Recursive Binary Quicksort");
+ this.setCategory("Distribution Sorts");
+ this.isComparisonBased(false);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ int mostSignificantBit = Reads.analyzeBit(array, length);
+ this.binaryQuickSortRecursive(array, 0, length - 1, mostSignificantBit);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/RecursiveBitonicSort.java b/src/sorts/RecursiveBitonicSort.java
new file mode 100644
index 00000000..dd811f72
--- /dev/null
+++ b/src/sorts/RecursiveBitonicSort.java
@@ -0,0 +1,88 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ * This version of Bitonic Sort was taken from here, written by H.W. Lang:
+ * http://www.inf.fh-flensburg.de/lang/algorithmen/sortieren/bitonic/oddn.htm
+ */
+
+final public class RecursiveBitonicSort extends Sort {
+ private boolean direction = true;
+
+ public RecursiveBitonicSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Recursive Bitonic");
+ this.setRunAllID("Batcher's Bitonic Sort");
+ this.setReportSortID("Recursive Bitonic Sort");
+ this.setCategory("Concurrent Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ private static int greatestPowerOfTwoLessThan(int n){
+ int k = 1;
+ while (k < n) {
+ k = k << 1;
+ }
+ return k >> 1;
+ }
+
+ private void compare(int[] A, int i, int j, boolean dir)
+ {
+ Highlights.markArray(1, i);
+ Highlights.markArray(2, j);
+
+ Delays.sleep(0.5);
+
+ int cmp = Reads.compare(A[i], A[j]);
+
+ if (dir == (cmp == 1)) Writes.swap(A, i, j, 1, true, false);
+ }
+
+ private void bitonicMerge(int[] A, int lo, int n, boolean dir)
+ {
+ if (n > 1)
+ {
+ int m = RecursiveBitonicSort.greatestPowerOfTwoLessThan(n);
+
+ for (int i = lo; i < lo + n - m; i++) {
+ this.compare(A, i, i+m, dir);
+ }
+
+ this.bitonicMerge(A, lo, m, dir);
+ this.bitonicMerge(A, lo + m, n - m, dir);
+ }
+ }
+
+ private void bitonicSort(int[] A, int lo, int n, boolean dir)
+ {
+ if (n > 1)
+ {
+ int m = n / 2;
+ this.bitonicSort(A, lo, m, !dir);
+ this.bitonicSort(A, lo + m, n - m, dir);
+ this.bitonicMerge(A, lo, n, dir);
+ }
+ }
+
+ public void changeDirection(String choice) throws Exception {
+ if(choice.equals("forward")) this.direction = true;
+ else if(choice.equals("backward")) this.direction = false;
+ else throw new Exception("Invalid direction for Bitonic Sort!");
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ this.bitonicSort(array, 0, currentLength, this.direction);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/RecursiveCombSort.java b/src/sorts/RecursiveCombSort.java
new file mode 100644
index 00000000..2f51344c
--- /dev/null
+++ b/src/sorts/RecursiveCombSort.java
@@ -0,0 +1,98 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+Copyright (c) 2019 w0rthy
+Copyright (c) 2019 PiotrGrochowski
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class RecursiveCombSort extends Sort {
+ public RecursiveCombSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Recursive Comb");
+ this.setRunAllID("Recursive Comb Sort");
+ this.setReportSortID("Recursive Combsort");
+ this.setCategory("Exchange Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ private void recursivecomb(int[] array, int start, int end, int gap, double sleep) {
+ if (start == (end - gap)){
+ return;
+ }
+ if (((end - start) / gap) % 2 == 0){
+ this.recursivecomb(array, start, end, gap * 2, sleep);
+ this.recursivecomb(array, start + gap, end + gap, gap * 2, sleep);
+ }
+ else{
+ this.recursivecomb(array, start, end + gap, gap * 2, sleep);
+ this.recursivecomb(array, start + gap, end, gap * 2, sleep);
+ }
+ this.finishrecucomb(array, start, end, gap, sleep);
+ }
+
+ private void finishrecucomb(int[] array, int start, int end, int gap, double sleep) {
+ if (start >= (end - gap)){
+ return;
+ }
+ if (((end - start) / gap) % 3 == 2){
+ this.finishrecucomb(array, start, end + gap, gap * 3, sleep);
+ this.finishrecucomb(array, start + gap, end + gap + gap, gap * 3, sleep);
+ this.finishrecucomb(array, start + gap + gap, end, gap * 3, sleep);
+ }
+ else{
+ if (((end - start) / gap) % 3 == 1){
+ this.finishrecucomb(array, start, end + gap + gap, gap * 3, sleep);
+ this.finishrecucomb(array, start + gap, end, gap * 3, sleep);
+ this.finishrecucomb(array, start + gap + gap, end + gap, gap * 3, sleep);
+ }
+ else{
+ this.finishrecucomb(array, start, end, gap * 3, sleep);
+ this.finishrecucomb(array, start + gap, end + gap, gap * 3, sleep);
+ this.finishrecucomb(array, start + gap + gap, end + gap + gap, gap * 3, sleep);
+ }
+ }
+ for (int i = start; i < (end - gap); i += gap){
+ Delays.sleep(sleep);
+ Highlights.markArray(1, i);
+ Highlights.markArray(2, i + gap);
+ if(Reads.compare(array[i], array[i + gap]) == 1) {
+ Writes.swap(array, i, i + gap, sleep, true, false);
+ }
+ }
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ this.recursivecomb(array, 0, length, 1, 1);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/RecursiveOddEvenMergeSort.java b/src/sorts/RecursiveOddEvenMergeSort.java
new file mode 100644
index 00000000..1da743f8
--- /dev/null
+++ b/src/sorts/RecursiveOddEvenMergeSort.java
@@ -0,0 +1,74 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ * This version of Odd-Even Merge Sort was taken from here, written by H.W. Lang:
+ * http://www.inf.fh-flensburg.de/lang/algorithmen/sortieren/networks/oemen.htm
+ */
+
+final public class RecursiveOddEvenMergeSort extends Sort {
+ public RecursiveOddEvenMergeSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Rec. Odd-Even Merge");
+ this.setRunAllID("Batcher's Odd-Even Merge Sort");
+ this.setReportSortID("Recursive Odd-Even Mergesort");
+ this.setCategory("Concurrent Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ private void oddEvenMergeCompare(int[] array, int i, int j) {
+ Highlights.markArray(1, i);
+ Highlights.markArray(2, j);
+ Delays.sleep(1);
+ if (Reads.compare(array[i], array[j]) > 0)
+ Writes.swap(array, i, j, 1, true, false);
+ }
+
+ /** lo is the starting position and
+ * n is the length of the piece to be merged,
+ * r is the distance of the elements to be compared
+ */
+ private void oddEvenMerge(int[] array, int lo, int n, int r) {
+ int m = r * 2;
+ if (m < n) {
+ this.oddEvenMerge(array, lo, n, m); // even subsequence
+ this.oddEvenMerge(array, lo+r, n, m); // odd subsequence
+
+ for (int i = lo + r; i + r < lo + n; i += m) {
+ Highlights.markArray(1, i);
+ Highlights.markArray(2, i + r);
+ this.oddEvenMergeCompare(array, i, i + r);
+ }
+ }
+ else {
+ Highlights.markArray(1, lo + r);
+ Highlights.markArray(2, lo);
+ this.oddEvenMergeCompare(array, lo, lo+r);
+ }
+ }
+
+ private void oddEvenMergeSort(int[] array, int lo, int n) {
+ if (n > 1) {
+ int m = n / 2;
+ this.oddEvenMergeSort(array, lo, m);
+ this.oddEvenMergeSort(array, lo + m, m);
+ this.oddEvenMerge(array, lo, n, 1);
+ }
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ this.oddEvenMergeSort(array, 0, currentLength);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/RecursivePairwiseSort.java b/src/sorts/RecursivePairwiseSort.java
new file mode 100644
index 00000000..4bbd00bb
--- /dev/null
+++ b/src/sorts/RecursivePairwiseSort.java
@@ -0,0 +1,95 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+Copyright (c) 2019 PiotrGrochowski
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class RecursivePairwiseSort extends Sort {
+ public RecursivePairwiseSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Recursive Pairwise");
+ this.setRunAllID("Recursive Pairwise Sorting Network");
+ this.setReportSortID("Recursive Pairwise Sort");
+ this.setCategory("Concurrent Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ private void pairwiserecursive(int[] array, int start, int end, int gap, double sleep) {
+ if (start == end - gap){
+ return;
+ }
+ int b = start + gap;
+ while (b < end){
+ Delays.sleep(sleep);
+ Highlights.markArray(1, b - gap);
+ Highlights.markArray(2, b);
+ if(Reads.compare(array[b - gap], array[b]) == 1) {
+ Writes.swap(array, b - gap, b, sleep, true, false);
+ }
+ b += (2 * gap);
+ }
+ if (((end - start) / gap)%2 == 0){
+ this.pairwiserecursive(array, start, end, gap * 2, 1);
+ this.pairwiserecursive(array, start + gap, end + gap, gap * 2, 1);
+ }
+ else{
+ this.pairwiserecursive(array, start, end + gap, gap * 2, 1);
+ this.pairwiserecursive(array, start + gap, end, gap * 2, 1);
+ }
+ int a = 1;
+ while (a < ((end - start) / gap)){
+ a = (a * 2) + 1;
+ }
+ b = start + gap;
+ while (b + gap < end){
+ int c = a;
+ while (c > 1){
+ c /= 2;
+ if (b + (c * gap) < end){
+ Delays.sleep(sleep);
+ Highlights.markArray(1, b);
+ Highlights.markArray(2, b + (c * gap));
+ if(Reads.compare(array[b], array[b + (c * gap)]) == 1) {
+ Writes.swap(array, b, b + (c * gap), sleep, true, false);
+ }
+ }
+ }
+ b += (2 * gap);
+ }
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ this.pairwiserecursive(array, 0, length, 1, 1);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/RotateMergeSort.java b/src/sorts/RotateMergeSort.java
new file mode 100644
index 00000000..36e42298
--- /dev/null
+++ b/src/sorts/RotateMergeSort.java
@@ -0,0 +1,72 @@
+package sorts;
+
+import templates.GrailSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+The MIT License (MIT)
+
+Copyright (c) 2013 Andrey Astrelin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/********* Classic in-place merge ********/
+/* */
+/* (c) 2013 by Andrey Astrelin */
+/* Refactored by MusicTheorist */
+/* */
+/* Classic implementation of in-place */
+/* merge sort; uses binary search and */
+/* rotations */
+/* */
+/* Copied from GrailSort.h */
+/* */
+/*****************************************/
+
+final public class RotateMergeSort extends GrailSorting {
+ public RotateMergeSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Rotate Merge");
+ this.setRunAllID("Rotate Merge Sort");
+ //this.setRunAllID("In-Place Merge Sort with Rotations");
+ this.setReportSortID("In-Place Rotate Mergesort");
+ this.setCategory("Merge Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ public void customRotateMerge(int[] array, int start, int end) {
+ this.grailInPlaceMergeSort(array, start, end);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ this.grailInPlaceMergeSort(array, 0, length);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/SelectionSort.java b/src/sorts/SelectionSort.java
new file mode 100644
index 00000000..3f418ddc
--- /dev/null
+++ b/src/sorts/SelectionSort.java
@@ -0,0 +1,69 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class SelectionSort extends Sort {
+ public SelectionSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Selection");
+ this.setRunAllID("Selection Sort");
+ this.setReportSortID("Selection Sort");
+ this.setCategory("Selection Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ for (int i = 0; i < length - 1; i++) {
+ int lowestindex = i;
+
+ for (int j = i + 1; j < length; j++) {
+ Highlights.markArray(2, j);
+ Delays.sleep(0.01);
+
+ if (Reads.compare(array[j], array[lowestindex]) == -1){
+ lowestindex = j;
+ Highlights.markArray(1, lowestindex);
+ Delays.sleep(0.01);
+ }
+ }
+ Writes.swap(array, i, lowestindex, 0.02, true, false);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/ShatterSort.java b/src/sorts/ShatterSort.java
new file mode 100644
index 00000000..c1314f53
--- /dev/null
+++ b/src/sorts/ShatterSort.java
@@ -0,0 +1,55 @@
+package sorts;
+
+import templates.ShatterSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class ShatterSort extends ShatterSorting {
+ public ShatterSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Shatter");
+ this.setRunAllID("Shatter Sort");
+ this.setReportSortID("Shatter Sort");
+ this.setCategory("Distributive Sorts");
+ this.isComparisonBased(false);
+ this.isBucketSort(true);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ this.shatterSort(array, length, bucketCount);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/ShellSort.java b/src/sorts/ShellSort.java
new file mode 100644
index 00000000..fc6f9750
--- /dev/null
+++ b/src/sorts/ShellSort.java
@@ -0,0 +1,36 @@
+package sorts;
+
+import templates.ShellSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+// Shell sort variant retrieved from:
+// https://www.cs.princeton.edu/~rs/talks/shellsort.ps
+
+final public class ShellSort extends ShellSorting {
+ public ShellSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Shell");
+ this.setRunAllID("Shell Sort");
+ this.setReportSortID("Shellsort");
+ this.setCategory("Insertion Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ public void finishQuickShell(int[] array, int currentLen) {
+ this.quickShellSort(array, 0, currentLen);
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ this.shellSort(this.ArrayVisualizer, array, currentLength);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/SillySort.java b/src/sorts/SillySort.java
new file mode 100644
index 00000000..25a0eecb
--- /dev/null
+++ b/src/sorts/SillySort.java
@@ -0,0 +1,63 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+// Written by Tom Duff, and found here: http://home.tiac.net/~cri_d/cri/2001/badsort.html
+// from https://stackoverflow.com/questions/2609857/are-there-any-worse-sorting-algorithms-than-bogosort-a-k-a-monkey-sort/
+
+final public class SillySort extends Sort {
+ public SillySort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Silly");
+ this.setRunAllID("Silly Sort");
+ this.setReportSortID("Sillysort");
+ this.setCategory("Exchange Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(true);
+ this.setUnreasonableLimit(512);
+ this.isBogoSort(false);
+ }
+
+ private void sillySort(int[] array, int i, int j) {
+ int m;
+
+ Delays.sleep(1);
+
+ if (i < j) {
+ /* find the middle of the array */
+ m = i + ((j - i) / 2);
+
+ /*
+ * use this function (recursively) to find put the minimum elements of
+ * each half into the first elements of each half
+ */
+ this.sillySort(array, i, m);
+ this.sillySort(array, m + 1, j);
+
+ /*
+ * Choose the smallest element of the two halves, and put that element in
+ * the first position
+ */
+ if (Reads.compare(array[i], array[m + 1]) >= 0) {
+ Writes.swap(array, i, m + 1, 1, true, false);
+ }
+
+ Highlights.markArray(1, i);
+ Highlights.markArray(2, m + 1);
+
+ this.sillySort(array, i + 1, j);
+ }
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ this.sillySort(array, 0, currentLength - 1);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/SimpleShatterSort.java b/src/sorts/SimpleShatterSort.java
new file mode 100644
index 00000000..5b4e33c4
--- /dev/null
+++ b/src/sorts/SimpleShatterSort.java
@@ -0,0 +1,55 @@
+package sorts;
+
+import templates.ShatterSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class SimpleShatterSort extends ShatterSorting {
+ public SimpleShatterSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Simple Shatter");
+ this.setRunAllID("Simple Shatter Sort");
+ this.setReportSortID("Simple Shatter Sort");
+ this.setCategory("Distributive Sorts");
+ this.isComparisonBased(false);
+ this.isBucketSort(true);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ this.simpleShatterSort(array, length, bucketCount, (int) (Math.log(length) / Math.log(2)) / 2);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/SkaSort_disabled.java b/src/sorts/SkaSort_disabled.java
new file mode 100644
index 00000000..feb4762c
--- /dev/null
+++ b/src/sorts/SkaSort_disabled.java
@@ -0,0 +1,209 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+// Copyright Malte Skarupke 2016.
+// Distributed under the Boost Software License, Version 1.0.
+// (See http://www.boost.org/LICENSE_1_0.txt)
+
+
+final class PartitionInfo {
+ private int count;
+ private int offset;
+ private int next_offset;
+
+ public PartitionInfo() {
+ this.setCount(0);
+ }
+
+ public int getCount() {
+ return this.count;
+ }
+
+ public void setCount(int count) {
+ this.count = count;
+ }
+
+ public void incrementCount() {
+ ++this.count;
+ }
+
+ public int getOffset() {
+ return this.offset;
+ }
+
+ public void setOffset(int offset) {
+ this.offset = offset;
+ }
+
+ public int getNextOffset() {
+ return this.next_offset;
+ }
+
+ public void setNextOffset(int next_offset) {
+ this.next_offset = next_offset;
+ }
+}
+
+public class SkaSort extends Sort {
+ final private static int StdSortThreshold = 128;
+ final private static int AmericanFlagSortThreshold = 1024;
+
+ public SkaSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID(""); // Sort disabled
+ this.setRunAllID("Ska Sort");
+ this.setReportSortID("Skasort");
+ this.setCategory("Distributive Sorts");
+ this.isComparisonBased(false);
+ this.isBucketSort(true);
+ this.isRadixSort(true);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ /*
+
+ template
+ inline void unroll_loop_four_times(It begin, size_t iteration_count, Func && to_call)
+ {
+ size_t loop_count = iteration_count / 4;
+ size_t remainder_count = iteration_count - loop_count * 4;
+ for (; loop_count > 0; --loop_count)
+ {
+ to_call(begin);
+ ++begin;
+ to_call(begin);
+ ++begin;
+ to_call(begin);
+ ++begin;
+ to_call(begin);
+ ++begin;
+ }
+ switch(remainder_count)
+ {
+ case 3:
+ to_call(begin);
+ ++begin;
+ case 2:
+ to_call(begin);
+ ++begin;
+ case 1:
+ to_call(begin);
+ }
+ }
+
+ template
+ inline It custom_std_partition(It begin, It end, F && func)
+ {
+ for (;; ++begin)
+ {
+ if (begin == end)
+ return end;
+ if (!func(*begin))
+ break;
+ }
+ It it = begin;
+ for(++it; it != end; ++it)
+ {
+ if (!func(*it))
+ continue;
+
+ std::iter_swap(begin, it);
+ ++begin;
+ }
+ return begin;
+ }
+
+ private void ska_byte_sort(int[] array, int begin, int end) {
+ PartitionInfo partitions[] = new PartitionInfo[256];
+ for (int it = begin; it != end; ++it) {
+ partitions[array[it]].incrementCount();
+ }
+ int remaining_partitions[] = new int[256];
+ int total = 0;
+ int num_partitions = 0;
+ for (int i = 0; i < 256; ++i) {
+ int count = partitions[i].getCount();
+ if (count != 0){
+ partitions[i].setOffset(total);
+ total += count;
+ remaining_partitions[num_partitions] = i;
+ ++num_partitions;
+ }
+ partitions[i].setNextOffset(total);
+ }
+ for (int last_remaining = remaining_partitions[num_partitions], int end_partition = remaining_partitions[1]; last_remaining > end_partition;) {
+ last_remaining = custom_std_partition(remaining_partitions, last_remaining, [&](uint8_t partition)
+ {
+ size_t & begin_offset = partitions[partition].offset;
+ size_t & end_offset = partitions[partition].next_offset;
+ if (begin_offset == end_offset)
+ return false;
+
+ unroll_loop_four_times(begin + begin_offset, end_offset - begin_offset, [partitions = partitions, begin, &extract_key, sort_data](It it)
+ {
+ uint8_t this_partition = current_byte(extract_key(*it), sort_data);
+ size_t offset = partitions[this_partition].offset++;
+ std::iter_swap(it, begin + offset);
+ });
+ return begin_offset != end_offset;
+ });
+ }
+ if (Offset + 1 != NumBytes || next_sort)
+ {
+ for (int it = remaining_partitions + num_partitions; it != remaining_partitions; --it)
+ {
+ int partition = partitions[it - 1];
+ int start_offset = (partition == 0 ? 0 : partitions[partition - 1].next_offset);
+ int end_offset = partitions[partition].next_offset;
+ int partition_begin = begin + start_offset;
+ int partition_end = begin + end_offset;
+ int num_elements = end_offset - start_offset;
+ if (!StdSortIfLessThanThreshold(array, partition_begin, partition_end, num_elements)) {
+ this.sort(array, partition_begin, partition_end, num_elements);
+ }
+ }
+ }
+ }
+
+ private boolean StdSortIfLessThanThreshold(int[] array, int begin, int end, int num_elements) {
+ if (num_elements <= 1)
+ return true;
+ if (num_elements >= StdSortThreshold)
+ return false;
+ BranchedPDQSort pdqSort = new BranchedPDQSort(Delays, Highlights, Reads, Writes);
+ pdqSort.customSort(array, begin, end);
+ return true;
+ }
+
+ private void sort(int[] array, int begin, int end, int num_elements) {
+ if (num_elements < AmericanFlagSortThreshold) {
+ AmericanFlagSort flagSort = new AmericanFlagSort(Delays, Highlights, Reads, Writes);
+ flagSort.runSort(array, num_elements, 128);
+ }
+ else {
+ this.ska_byte_sort(array, begin, end);
+ }
+ }
+
+ private void inplace_radix_sort(int[] array, int begin, int end) {
+ this.sort(array, begin, end, end - begin);
+ }
+
+ private void ska_sort(int[] array, int begin, int end) {
+ this.inplace_radix_sort(array, begin, end);
+ }
+*/
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ //this.ska_sort(array, 0, length);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/SlowSort.java b/src/sorts/SlowSort.java
new file mode 100644
index 00000000..7be2b1d7
--- /dev/null
+++ b/src/sorts/SlowSort.java
@@ -0,0 +1,53 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+// Code refactored from Python: http://wiki.c2.com/?SlowSort
+
+final public class SlowSort extends Sort {
+ public SlowSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Slow");
+ this.setRunAllID("Slow Sort");
+ this.setReportSortID("Slowsort");
+ this.setCategory("Exchange Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(true);
+ this.setUnreasonableLimit(512);
+ this.isBogoSort(false);
+ }
+
+ private void slowSort(int[] A, int i, int j) {
+ Delays.sleep(1);
+
+ if (i >= j) {
+ return;
+ }
+
+ int m = i + ((j - i) / 2);
+
+ this.slowSort(A, i, m);
+ this.slowSort(A, m + 1, j);
+
+ if (Reads.compare(A[m], A[j]) == 1) {
+ Writes.swap(A, m, j, 1, true, false);
+ }
+
+ Highlights.markArray(1, j);
+ Highlights.markArray(2, m);
+
+ this.slowSort(A, i, j - 1);
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ this.slowSort(array, 0, currentLength - 1);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/SmartBubbleSort.java b/src/sorts/SmartBubbleSort.java
new file mode 100644
index 00000000..1352d402
--- /dev/null
+++ b/src/sorts/SmartBubbleSort.java
@@ -0,0 +1,68 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class SmartBubbleSort extends Sort {
+ public SmartBubbleSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Smart Bubble");
+ this.setRunAllID("Optimized Bubble Sort");
+ this.setReportSortID("Optimized Bubblesort");
+ this.setCategory("Exchange Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ for(int i = length - 1; i > 0; i--) {
+ boolean sorted = true;
+ for(int j = 0; j < i; j++) {
+ if(Reads.compare(array[j], array[j + 1]) == 1){
+ Writes.swap(array, j, j + 1, 0.075, true, false);
+ sorted = false;
+ }
+
+ Highlights.markArray(1, j);
+ Highlights.markArray(2, j + 1);
+ Delays.sleep(0.025);
+ }
+ if(sorted) break;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/SmartCocktailSort.java b/src/sorts/SmartCocktailSort.java
new file mode 100644
index 00000000..df688c6c
--- /dev/null
+++ b/src/sorts/SmartCocktailSort.java
@@ -0,0 +1,90 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class SmartCocktailSort extends Sort {
+ public SmartCocktailSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Smart Cocktail");
+ this.setRunAllID("Optimized Cocktail Shaker Sort");
+ this.setReportSortID("Optimized Cocktail Shaker Sort");
+ this.setCategory("Exchange Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ private void smartCocktailShaker(int[] array, int start, int end, double sleep) {
+ int i = start;
+ while(i < ((end / 2) + start)) {
+ boolean sorted = true;
+ for(int j = i; j < end + start - i - 1; j++) {
+ if(Reads.compare(array[j], array[j + 1]) == 1) {
+ Writes.swap(array, j, j + 1, sleep, true, false);
+ sorted = false;
+ }
+
+ Highlights.markArray(1, j);
+ Highlights.markArray(2, j + 1);
+
+ Delays.sleep(sleep / 2);
+ }
+ for(int j = end + start - i - 1; j > i; j--){
+ if(Reads.compare(array[j], array[j - 1]) == -1) {
+ Writes.swap(array, j, j - 1, sleep, true, false);
+ sorted = false;
+ }
+
+ Highlights.markArray(1, j);
+ Highlights.markArray(2, j - 1);
+
+ Delays.sleep(sleep / 2);
+ }
+ if(sorted) break;
+ else i++;
+ }
+ }
+
+ public void customSort(int[] array, int start, int end) {
+ this.smartCocktailShaker(array, start, end, 1);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ this.smartCocktailShaker(array, 0, length, 0.1);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/SmartGnomeSort.java b/src/sorts/SmartGnomeSort.java
new file mode 100644
index 00000000..f65aab3c
--- /dev/null
+++ b/src/sorts/SmartGnomeSort.java
@@ -0,0 +1,47 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+final public class SmartGnomeSort extends Sort {
+ public SmartGnomeSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Smart Gnome");
+ this.setRunAllID("Optimized Gnome Sort");
+ this.setReportSortID("Optimized Gnomesort");
+ this.setCategory("Exchange Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ // Taken from https://en.wikipedia.org/wiki/Gnome_sort
+ private void smartGnomeSort(int[] array, int upperBound, double sleep) {
+ int pos = upperBound;
+
+ while(pos > 0 && Reads.compare(array[pos - 1], array[pos]) == 1) {
+ Writes.swap(array, pos - 1, pos, sleep, true, false);
+ pos--;
+ }
+ }
+
+ public void customSort(int[] array, int low, int high, double sleep) {
+ for(int i = low + 1; i < high; i++) {
+ smartGnomeSort(array, i, sleep);
+ }
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ for(int i = 1; i < length; i++) {
+ smartGnomeSort(array, i, 0.05);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/SmoothSort.java b/src/sorts/SmoothSort.java
new file mode 100644
index 00000000..36206881
--- /dev/null
+++ b/src/sorts/SmoothSort.java
@@ -0,0 +1,202 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+final public class SmoothSort extends Sort {
+ public SmoothSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Smooth");
+ this.setRunAllID("Smooth Sort");
+ this.setReportSortID("Smoothsort");
+ this.setCategory("Selection Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ // SMOOTH SORT - Provided here:
+ // https://stackoverflow.com/questions/1390832/how-to-sort-nearly-sorted-array-in-the-fastest-time-possible-java/28352545#28352545
+
+ static final int LP[] = { 1, 1, 3, 5, 9, 15, 25, 41, 67, 109,
+ 177, 287, 465, 753, 1219, 1973, 3193, 5167, 8361, 13529, 21891,
+ 35421, 57313, 92735, 150049, 242785, 392835, 635621, 1028457,
+ 1664079, 2692537, 4356617, 7049155, 11405773, 18454929, 29860703,
+ 48315633, 78176337, 126491971, 204668309, 331160281, 535828591,
+ 866988873 // the next number is > 31 bits.
+ }; //TODO: Truncate values in array that are over ArrayVisualizer max length.
+
+ private void sift(int[] A, int pshift, int head)
+ {
+ // we do not use Floyd's improvements to the heapsort sift, because we
+ // are not doing what heapsort does - always moving nodes from near
+ // the bottom of the tree to the root.
+
+ int val = A[head];
+
+ while (pshift > 1)
+ {
+ int rt = head - 1;
+ int lf = head - 1 - LP[pshift - 2];
+
+ Highlights.markArray(2, rt);
+ Highlights.markArray(3, lf);
+
+ Delays.sleep(0.325);
+
+ if (Reads.compare(val, A[lf]) >= 0 && Reads.compare(val, A[rt]) >= 0)
+ break;
+
+ if (Reads.compare(A[lf], A[rt]) >= 0) {
+ Writes.write(A, head, A[lf], 0.65, true, false);
+ head = lf;
+ pshift -= 1;
+ }
+ else {
+ Writes.write(A, head, A[rt], 0.65, true, false);
+ head = rt;
+ pshift -= 2;
+ }
+ }
+ Writes.write(A, head, val, 0.65, true, false);
+
+ Highlights.clearMark(2);
+ Highlights.clearMark(3);
+ }
+
+ private void trinkle(int[] A, int p, int pshift, int head, boolean isTrusty)
+ {
+ int val = A[head];
+
+ while (p != 1)
+ {
+ int stepson = head - LP[pshift];
+
+ if (Reads.compare(A[stepson], val) <= 0)
+ break; // current node is greater than head. sift.
+
+ // no need to check this if we know the current node is trusty,
+ // because we just checked the head (which is val, in the first
+ // iteration)
+ if (!isTrusty && pshift > 1) {
+ int rt = head - 1;
+ int lf = head - 1 - LP[pshift - 2];
+ Highlights.markArray(2, rt);
+ Highlights.markArray(3, lf);
+
+ Delays.sleep(0.325);
+
+ if (Reads.compare(A[rt], A[stepson]) >= 0 ||
+ Reads.compare(A[lf], A[stepson]) >= 0)
+ break;
+ }
+ Writes.write(A, head, A[stepson], 0.65, true, false);
+
+ Highlights.clearMark(2);
+ Highlights.clearMark(3);
+
+ head = stepson;
+ //int trail = Integer.numberOfTrailingZeros(p & ~1);
+ int trail = Integer.numberOfTrailingZeros(p & ~1);
+ p >>= trail;
+ pshift += trail;
+ isTrusty = false;
+ }
+
+ if (!isTrusty) {
+ Writes.write(A, head, val, 0.65, true, false);
+ this.sift(A, pshift, head);
+ }
+ }
+
+ private void smoothSort(int[] A, int lo, int hi)
+ {
+ int head = lo; // the offset of the first element of the prefix into m
+
+ // These variables need a little explaining. If our string of heaps
+ // is of length 38, then the heaps will be of size 25+9+3+1, which are
+ // Leonardo numbers 6, 4, 2, 1.
+ // Turning this into a binary number, we get b01010110 = 0x56. We represent
+ // this number as a pair of numbers by right-shifting all the zeros and
+ // storing the mantissa and exponent as "p" and "pshift".
+ // This is handy, because the exponent is the index into L[] giving the
+ // size of the rightmost heap, and because we can instantly find out if
+ // the rightmost two heaps are consecutive Leonardo numbers by checking
+ // (p&3)==3
+
+ int p = 1; // the bitmap of the current standard concatenation >> pshift
+ int pshift = 1;
+
+ while (head < hi)
+ {
+ if ((p & 3) == 3) {
+ // Add 1 by merging the first two blocks into a larger one.
+ // The next Leonardo number is one bigger.
+ this.sift(A, pshift, head);
+ p >>= 2;
+ pshift += 2;
+ }
+ else {
+ // adding a new block of length 1
+ if (LP[pshift - 1] >= hi - head) {
+ // this block is its final size.
+ this.trinkle(A, p, pshift, head, false);
+ } else {
+ // this block will get merged. Just make it trusty.
+ this.sift(A, pshift, head);
+ }
+
+ if (pshift == 1) {
+ // LP[1] is being used, so we add use LP[0]
+ p <<= 1;
+ pshift--;
+ } else {
+ // shift out to position 1, add LP[1]
+ p <<= (pshift - 1);
+ pshift = 1;
+ }
+ }
+ p |= 1;
+ head++;
+ }
+
+ this.trinkle(A, p, pshift, head, false);
+
+ while (pshift != 1 || p != 1)
+ {
+ if (pshift <= 1) {
+ // block of length 1. No fiddling needed
+ int trail = Integer.numberOfTrailingZeros(p & ~1);
+ p >>= trail;
+ pshift += trail;
+ }
+ else {
+ p <<= 2;
+ p ^= 7;
+ pshift -= 2;
+
+ // This block gets broken into three bits. The rightmost bit is a
+ // block of length 1. The left hand part is split into two, a block
+ // of length LP[pshift+1] and one of LP[pshift]. Both these two
+ // are appropriately heapified, but the root nodes are not
+ // necessarily in order. We therefore semitrinkle both of them
+
+ this.trinkle(A, p >> 1, pshift + 1, head - LP[pshift] - 1, true);
+ this.trinkle(A, p, pshift, head - 1, true);
+ }
+ head--;
+ }
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ this.smoothSort(array, 0, currentLength - 1);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/SqrtSort.java b/src/sorts/SqrtSort.java
new file mode 100644
index 00000000..88cf0d2d
--- /dev/null
+++ b/src/sorts/SqrtSort.java
@@ -0,0 +1,424 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+The MIT License (MIT)
+
+Copyright (c) 2014 Andrey Astrelin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/********* Sqrt sorting **********************************/
+/* */
+/* (c) 2014 by Andrey Astrelin */
+/* Refactored by MusicTheorist */
+/* */
+/* Stable sorting that works in O(N*log(N)) worst time */
+/* and uses O(sqrt(N)) extra memory */
+/* */
+/* Define SORT_TYPE and SORT_CMP */
+/* and then call SqrtSort() function */
+/* */
+/*********************************************************/
+
+final class SqrtState {
+ private int leftOverLen;
+ private int leftOverFrag;
+
+ protected SqrtState(int len, int frag) {
+ this.leftOverLen = len;
+ this.leftOverFrag = frag;
+ }
+
+ protected int getLeftOverLen() {
+ return leftOverLen;
+ }
+
+ protected int getLeftOverFrag() {
+ return leftOverFrag;
+ }
+}
+
+final public class SqrtSort extends Sort {
+ private InsertionSort insertSorter;
+
+ public SqrtSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Sqrt");
+ //this.setRunAllID("Square Root Sort (Block Merge Sort)");
+ this.setRunAllID("Square Root Sort [Block Merge Sort]");
+ this.setReportSortID("Sqrtsort");
+ this.setCategory("Hybrid Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ private void sqrtSwap(int[] arr, int a, int b, boolean auxwrite) {
+ Writes.swap(arr, a, b, 1, true, auxwrite);
+ Highlights.clearMark(2);
+ }
+
+ private void sqrtMultiSwap(int[] arr, int a, int b, int swapsLeft, boolean auxwrite) {
+ while(swapsLeft != 0) {
+ this.sqrtSwap(arr, a++, b++, auxwrite);
+ swapsLeft--;
+ }
+ }
+
+ private void sqrtInsertSort(int[] arr, int pos, int len, boolean auxwrite) {
+ insertSorter.customInsertSort(arr, pos, len, 0.25, auxwrite);
+ }
+
+ private void sqrtMergeRight(int[] arr, int pos, int leftLen, int rightLen, int dist, boolean auxwrite) {
+ int mergedPos = leftLen + rightLen + dist - 1;
+ int right = leftLen + rightLen - 1;
+ int left = leftLen - 1;
+
+ while(left >= 0) {
+ Highlights.markArray(2, pos + left);
+ Highlights.markArray(3, pos + right);
+
+ if(right < leftLen || Reads.compare(arr[pos + left], arr[pos + right]) > 0) {
+ Writes.write(arr, pos + (mergedPos--), arr[pos + (left--)], 1, true, auxwrite);
+ }
+ else Writes.write(arr, pos + (mergedPos--), arr[pos + (right--)], 1, true, auxwrite);
+ }
+
+ Highlights.clearMark(3);
+
+ if(right != mergedPos) {
+ while(right >= leftLen) {
+ Writes.write(arr, pos + (mergedPos--), arr[pos + (right--)], 1, true, auxwrite);
+ Highlights.markArray(2, pos + right);
+ }
+ }
+
+ Highlights.clearMark(2);
+ }
+
+ // arr[dist..-1] - free, arr[0, leftEnd - 1] ++ arr[leftEnd, leftEnd + rightEnd - 1]
+ // -> arr[dist, dist + leftEnd + rightEnd - 1]
+ private void sqrtMergeLeftWithXBuf(int[] arr, int pos, int leftEnd, int rightEnd, int dist, boolean auxwrite) {
+ int left = 0;
+ int right = leftEnd;
+ rightEnd += leftEnd;
+
+ while(right < rightEnd) {
+ if(left == leftEnd || Reads.compare(arr[pos + left], arr[pos + right]) > 0) {
+ Writes.write(arr, pos + dist++, arr[pos + right++], 1, true, auxwrite);
+ }
+ else Writes.write(arr, pos + dist++, arr[pos + left++], 1, true, auxwrite);
+
+ Highlights.markArray(2, pos + left);
+ Highlights.markArray(3, pos + right);
+ }
+
+ Highlights.clearMark(3);
+
+ if(dist != left) {
+ while(left < leftEnd) {
+ Writes.write(arr, pos + dist++, arr[pos + left++], 1, true, auxwrite);
+ Highlights.markArray(2, pos + left);
+ }
+ }
+
+ Highlights.clearMark(2);
+ }
+
+ // arr[0,L1-1] ++ arr2[0,L2-1] -> arr[-L1,L2-1], arr2 is "before" arr1
+ private void sqrtMergeDown(int[] arr, int arrPos, int[] buffer, int bufPos, int leftLen, int rightLen, boolean auxwrite) {
+ int arrMerge = 0, bufMerge = 0;
+ int dist = 0 - rightLen;
+
+ while(bufMerge < rightLen) {
+ if(arrMerge == leftLen || Reads.compare(arr[arrPos + arrMerge], buffer[bufPos + bufMerge]) >= 0) {
+ Writes.write(arr, arrPos + dist++, buffer[bufPos + bufMerge++], 1, true, auxwrite);
+ }
+ else Writes.write(arr, arrPos + dist++, arr[arrPos + arrMerge++], 1, true, auxwrite);
+
+ Highlights.markArray(2, arrPos + arrMerge);
+ Highlights.markArray(3, bufPos + bufMerge);
+ }
+
+ Highlights.clearMark(3);
+
+ if(dist != arrMerge) {
+ while(arrMerge < leftLen) {
+ Writes.write(arr, arrPos + dist++, arr[arrPos + arrMerge++], 1, true, auxwrite);
+ Highlights.markArray(2, arrPos + arrMerge);
+ }
+ }
+
+ Highlights.clearMark(2);
+ }
+
+ //returns the leftover length, then the leftover fragment
+ private SqrtState sqrtSmartMergeWithXBuf(int[] arr, int pos, int leftOverLen, int leftOverFrag, int blockLen, boolean auxwrite) {
+ int dist = 0 - blockLen, left = 0, right = leftOverLen, leftEnd = right, rightEnd = right + blockLen;
+ int typeFrag = 1 - leftOverFrag; // 1 if inverted
+
+ while(left < leftEnd && right < rightEnd) {
+ if(Reads.compare(arr[pos + left], arr[pos + right]) - typeFrag < 0) {
+ Writes.write(arr, pos + dist++, arr[pos + left++], 1, true, auxwrite);
+ }
+ else Writes.write(arr, pos + dist++, arr[pos + right++], 1, true, auxwrite);
+
+ Highlights.markArray(2, pos + left);
+ Highlights.markArray(3, pos + right);
+ }
+
+ Highlights.clearMark(3);
+
+ int length, fragment = leftOverFrag;
+
+ if(left < leftEnd) {
+ length = leftEnd - left;
+
+ while(left < leftEnd) {
+ Writes.write(arr, pos + --rightEnd, arr[pos + --leftEnd], 1, true, auxwrite);
+ Highlights.markArray(2, pos + leftEnd);
+ }
+ }
+ else {
+ length = rightEnd - right;
+ fragment = typeFrag;
+ }
+
+ Highlights.clearMark(2);
+
+ return new SqrtState(length, fragment);
+ }
+
+ // arr - starting array. arr[0 - regBlockLen..-1] - buffer (if havebuf).
+ // regBlockLen - length of regular blocks. First blockCount blocks are stable sorted by 1st elements and key-coded
+ // keysPos - where keys are in array, in same order as blocks. keysPos < midkey means stream A
+ // aBlockCount are regular blocks from stream A.
+ // lastLen is length of last (irregular) block from stream B, that should go before aCountBlock blocks.
+ // lastLen = 0 requires aBlockCount = 0 (no irregular blocks). lastLen > 0, aBlockCount = 0 is possible.
+ private void sqrtMergeBuffersLeftWithXBuf(int[] keys, int midkey, int[] arr, int pos, int blockCount, int regBlockLen,
+ int aBlockCount, int lastLen, boolean auxwrite) {
+
+ if(blockCount == 0) {
+ int aBlocksLen = aBlockCount * regBlockLen;
+ this.sqrtMergeLeftWithXBuf(arr, pos, aBlocksLen, lastLen, 0 - regBlockLen, auxwrite);
+ return;
+ }
+
+ int leftOverLen = regBlockLen;
+ int leftOverFrag = Reads.compare(keys[0], midkey) < 0 ? 0 : 1;
+ int processIndex = regBlockLen;
+
+ int restToProcess;
+
+ for(int keyIndex = 1; keyIndex < blockCount; keyIndex++, processIndex += regBlockLen) {
+ restToProcess = processIndex - leftOverLen;
+ int nextFrag = Reads.compare(keys[keyIndex], midkey) < 0 ? 0 : 1;
+
+ if(nextFrag == leftOverFrag) {
+ Writes.arraycopy(arr, pos + restToProcess, arr, pos + restToProcess - regBlockLen, leftOverLen, 1, true, auxwrite);
+
+ restToProcess = processIndex;
+ leftOverLen = regBlockLen;
+ }
+ else {
+ SqrtState results = this.sqrtSmartMergeWithXBuf(arr, pos + restToProcess, leftOverLen, leftOverFrag, regBlockLen, auxwrite);
+
+ leftOverLen = results.getLeftOverLen();
+ leftOverFrag = results.getLeftOverFrag();
+ }
+ }
+
+ restToProcess = processIndex - leftOverLen;
+
+ if(lastLen != 0) {
+ if(leftOverFrag != 0) {
+ Writes.arraycopy(arr, pos + restToProcess, arr, pos + restToProcess - regBlockLen, leftOverLen, 1, true, auxwrite);
+
+ restToProcess = processIndex;
+ leftOverLen = regBlockLen * aBlockCount;
+ leftOverFrag = 0;
+ }
+ else {
+ leftOverLen += regBlockLen * aBlockCount;
+ }
+ this.sqrtMergeLeftWithXBuf(arr, pos + restToProcess, leftOverLen, lastLen, 0 - regBlockLen, auxwrite);
+ }
+ else {
+ Writes.arraycopy(arr, pos + restToProcess, arr, pos + restToProcess - regBlockLen, leftOverLen, 1, true, auxwrite);
+ }
+ }
+
+ // build blocks of length buildLen
+ // input: [-buildLen,-1] elements are buffer
+ // output: first buildLen elements are buffer, blocks 2 * buildLen and last subblock sorted
+ private void sqrtBuildBlocks(int[] arr, int pos, int len, int buildLen, boolean auxwrite) {
+ int extraDist, part;
+
+ for(int dist = 1; dist < len; dist += 2) {
+ extraDist = 0;
+ if(Reads.compare(arr[pos + (dist - 1)], arr[pos + dist]) > 0) extraDist = 1;
+
+ Writes.write(arr, pos + dist - 3, arr[pos + dist - 1 + extraDist], 1, true, auxwrite);
+ Writes.write(arr, pos + dist - 2, arr[pos + dist - extraDist], 1, true, auxwrite);
+ }
+ if(len % 2 != 0) Writes.write(arr, pos + len - 3, arr[pos + len - 1], 1, true, auxwrite);
+
+ pos -= 2;
+
+ for(part = 2; part < buildLen; part *= 2) {
+ int left = 0;
+ int right = len - 2 * part;
+
+ while(left <= right) {
+ this.sqrtMergeLeftWithXBuf(arr, pos + left, part, part, 0 - part, auxwrite);
+ left += 2 * part;
+ }
+
+ int rest = len - left;
+
+ if(rest > part) {
+ this.sqrtMergeLeftWithXBuf(arr, pos + left, part, rest - part, 0 - part, auxwrite);
+ }
+ else {
+ while(left < len) Writes.write(arr, pos + left - part, arr[pos + left++], 1, true, auxwrite);
+ }
+
+ pos -= part;
+ }
+ int restToBuild = len % (2 * buildLen);
+ int leftOverPos = len - restToBuild;
+
+ if(restToBuild <= buildLen) {
+ Writes.arraycopy(arr, pos + leftOverPos, arr, pos + leftOverPos + buildLen, restToBuild, 1, true, auxwrite);
+ }
+ else this.sqrtMergeRight(arr, pos + leftOverPos, buildLen, restToBuild - buildLen, buildLen, auxwrite);
+
+ while(leftOverPos > 0) {
+ leftOverPos -= 2 * buildLen;
+ this.sqrtMergeRight(arr, pos + leftOverPos, buildLen, buildLen, buildLen, auxwrite);
+ }
+ }
+
+ // keys are on the left of arr. Blocks of length buildLen combined. We'll combine them in pairs
+ // buildLen and numKeys are powers of 2. (2 * buildLen / regBlockLen) keys are guaranteed
+ private void sqrtCombineBlocks(int[] arr, int pos, int len, int buildLen, int regBlockLen, int[] tags, boolean auxwrite) {
+ int combineLen = len / (2 * buildLen);
+ int leftOver = len % (2 * buildLen);
+
+ if(leftOver <= buildLen) {
+ len -= leftOver;
+ leftOver = 0;
+ }
+
+ int leftIndex = 0;
+
+ for(int i = 0; i <= combineLen; i++) {
+ if(i == combineLen && leftOver == 0) break;
+
+ int blockPos = pos + i * 2 * buildLen;
+ int blockCount = (i == combineLen ? leftOver : 2 * buildLen) / regBlockLen;
+
+ int tagIndex = blockCount + (i == combineLen ? 1 : 0);
+ for(int j = 0; j <= tagIndex; j++) Writes.write(tags, j, j, 1, true, true);
+
+ int midkey = buildLen / regBlockLen;
+
+ for(tagIndex = 1; tagIndex < blockCount; tagIndex++) {
+ leftIndex = tagIndex - 1;
+
+ for(int rightIndex = tagIndex; rightIndex < blockCount; rightIndex++) {
+ int rightComp = Reads.compare(arr[blockPos + leftIndex * regBlockLen],
+ arr[blockPos + rightIndex * regBlockLen]);
+ if(rightComp > 0 || (rightComp == 0 && tags[leftIndex] > tags[rightIndex])) leftIndex = rightIndex;
+ }
+
+ if(leftIndex != tagIndex - 1) {
+ this.sqrtMultiSwap(arr, blockPos + (tagIndex - 1) * regBlockLen, blockPos + leftIndex * regBlockLen, regBlockLen, auxwrite);
+ this.sqrtSwap(tags, tagIndex - 1, leftIndex, true);
+ }
+ }
+ int aBlockCount = 0;
+ int lastLen = 0;
+
+ if(i == combineLen) lastLen = leftOver % regBlockLen;
+
+ if(lastLen != 0) {
+ while(aBlockCount < blockCount && Reads.compare(arr[blockPos + blockCount * regBlockLen],
+ arr[blockPos + (blockCount - aBlockCount - 1) * regBlockLen]) < 0) {
+ aBlockCount++;
+ }
+ }
+ this.sqrtMergeBuffersLeftWithXBuf(tags, midkey, arr, blockPos, blockCount - aBlockCount, regBlockLen, aBlockCount, lastLen, auxwrite);
+ }
+ for(leftIndex = len - 1; leftIndex >= 0; leftIndex--) Writes.write(arr, pos + leftIndex, arr[pos + leftIndex - regBlockLen], 1, true, auxwrite);
+ }
+
+ private void sqrtCommonSort(int[] arr, int pos, int len, int[] extBuf, int extBufPos, int[] tags, boolean auxwrite) {
+ insertSorter = new InsertionSort(this.Delays, this.Highlights, this.Reads, this.Writes);
+
+ if(len <= 16) {
+ this.sqrtInsertSort(arr, pos, len, auxwrite);
+ Highlights.clearAllMarks();
+ return;
+ }
+
+ int blockLen = 1;
+ while((blockLen * blockLen) < len) blockLen *= 2;
+
+ Writes.arraycopy(arr, pos, extBuf, extBufPos, blockLen, 1, true, auxwrite);
+
+ this.sqrtCommonSort(extBuf, extBufPos, blockLen, arr, pos, tags, !auxwrite);
+
+ this.sqrtBuildBlocks(arr, pos + blockLen, len - blockLen, blockLen, auxwrite);
+
+ int buildLen = blockLen;
+
+ while(len > (buildLen *= 2)) {
+ this.sqrtCombineBlocks(arr, pos + blockLen, len - blockLen, buildLen, blockLen, tags, auxwrite);
+ }
+ this.sqrtMergeDown(arr, pos + blockLen, extBuf, extBufPos, len - blockLen, blockLen, auxwrite);
+
+ Highlights.clearAllMarks();
+ }
+
+ @Override
+ public void runSort(int[] array, int len, int bucketCount) {
+ int bufferLen = 1;
+
+ while(bufferLen * bufferLen < len) bufferLen *= 2;
+ int numKeys = (len - 1) / bufferLen + 2;
+
+ int[] extBuf = new int[bufferLen];
+ int[] tags = new int[numKeys];
+
+ this.sqrtCommonSort(array, 0, len, extBuf, 0, tags, false);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/StableQuickSort.java b/src/sorts/StableQuickSort.java
new file mode 100644
index 00000000..5c83e71a
--- /dev/null
+++ b/src/sorts/StableQuickSort.java
@@ -0,0 +1,109 @@
+package sorts;
+
+import java.util.ArrayList;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2017 Rodney Shaghoulian
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class StableQuickSort extends Sort {
+ public StableQuickSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Stable Quick");
+ this.setRunAllID("Stable Quick Sort");
+ this.setReportSortID("Stable Quicksort");
+ this.setCategory("Exchange Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ // Author: Rodney Shaghoulian
+ // Github: github.com/RodneyShag
+
+ private void copy(ArrayList list, int [] array, int startIndex) {
+ for (int num : list) {
+ Writes.write(array, startIndex++, num, 0.25, false, false);
+ Highlights.markArray(1, startIndex);
+ }
+ }
+
+ /* Partition/Quicksort "Stable Sort" version using O(n) space */
+ private int stablePartition(int[] array, int start, int end) {
+ int pivotValue = array[start]; //poor pivot choice
+ Highlights.markArray(3, start);
+
+ ArrayList leftList = new ArrayList<>();
+ ArrayList rightList = new ArrayList<>();
+
+ for (int i = start + 1 ; i <= end; i++) {
+ Highlights.markArray(1, i);
+
+ if (Reads.compare(array[i], pivotValue) == -1) {
+ Writes.mockWrite(end - start, leftList.size(), array[i], 0.25);
+ leftList.add(array[i]);
+ }
+ else {
+ Writes.mockWrite(end - start, rightList.size(), array[i], 0.25);
+ rightList.add(array[i]);
+ }
+ }
+
+ /* Recreate array */
+ this.copy(leftList, array, start);
+
+ int newPivotIndex = start + leftList.size();
+
+ Writes.write(array, newPivotIndex, pivotValue, 0.25, false, false);
+ Highlights.markArray(1, newPivotIndex);
+
+ this.copy(rightList, array, newPivotIndex + 1);
+
+ return newPivotIndex;
+ }
+
+ private void stableQuickSort(int [] array, int start, int end) {
+ if (start < end) {
+ int pivotIndex = this.stablePartition(array, start, end);
+ this.stableQuickSort(array, start, pivotIndex - 1);
+ this.stableQuickSort(array, pivotIndex + 1, end);
+ }
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ this.stableQuickSort(array, 0, length - 1);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/StoogeSort.java b/src/sorts/StoogeSort.java
new file mode 100644
index 00000000..c537f9bb
--- /dev/null
+++ b/src/sorts/StoogeSort.java
@@ -0,0 +1,62 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ * THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE").
+ * THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS
+ * LICENSE OR COPYRIGHT LAW IS PROHIBITED.
+ *
+ * BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE.
+ * TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN
+ * CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
+ */
+
+// Code refactored from: https://en.wikipedia.org/wiki/Stooge_sort
+final public class StoogeSort extends Sort {
+ public StoogeSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Stooge");
+ this.setRunAllID("Stooge Sort");
+ this.setReportSortID("Stoogesort");
+ this.setCategory("Exchange Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(true);
+ this.setUnreasonableLimit(2048);
+ this.isBogoSort(false);
+ }
+
+ private void stoogeSort(int[] A, int i, int j) {
+ if (Reads.compare(A[i], A[j]) == 1) {
+ Writes.swap(A, i, j, 0.005, true, false);
+ }
+
+ Delays.sleep(0.0025);
+
+ Highlights.markArray(1, i);
+ Highlights.markArray(2, j);
+
+ if (j - i + 1 >= 3) {
+ int t = (j - i + 1) / 3;
+
+ Highlights.markArray(3, j - t);
+ Highlights.markArray(4, i + t);
+
+ this.stoogeSort(A, i, j-t);
+ this.stoogeSort(A, i+t, j);
+ this.stoogeSort(A, i, j-t);
+ }
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ this.stoogeSort(array, 0, currentLength - 1);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/TernaryHeapSort.java b/src/sorts/TernaryHeapSort.java
new file mode 100644
index 00000000..c80f0204
--- /dev/null
+++ b/src/sorts/TernaryHeapSort.java
@@ -0,0 +1,83 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+final public class TernaryHeapSort extends Sort {
+ public TernaryHeapSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Ternary Heap");
+ this.setRunAllID("Ternary Heap Sort");
+ this.setReportSortID("Ternary Heapsort");
+ this.setCategory("Selection Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ // TERNARY HEAP SORT - written by qbit
+ // https://codereview.stackexchange.com/questions/63384/binary-heapsort-and-ternary-heapsort-implementation
+
+ private int heapSize;
+
+ private static int leftBranch(int i) {
+ return 3 * i + 1;
+ }
+
+ private static int middleBranch(int i) {
+ return 3 * i + 2;
+ }
+
+ private static int rightBranch(int i) {
+ return 3 * i + 3;
+ }
+
+ private void maxHeapify(int[] array, int i) {
+
+ int leftChild = TernaryHeapSort.leftBranch(i);
+ int rightChild = TernaryHeapSort.rightBranch(i);
+ int middleChild = TernaryHeapSort.middleBranch(i);
+ int largest;
+
+ largest = leftChild <= heapSize && Reads.compare(array[leftChild], array[i]) > 0 ? leftChild : i;
+
+ if(rightChild <= heapSize && Reads.compare(array[rightChild], array[largest]) > 0) {
+ largest = rightChild;
+ }
+
+ if(middleChild <= heapSize && Reads.compare(array[middleChild], array[largest]) > 0) {
+ largest = middleChild;
+ }
+
+
+ if(largest != i) {
+ Writes.swap(array, i, largest, 1, true, false);
+ this.maxHeapify(array, largest);
+ }
+ }
+
+ private void buildMaxTernaryHeap(int[] array, int length) {
+ heapSize = length - 1;
+ for(int i = length - 1 / 3; i >= 0; i--)
+ this.maxHeapify(array, i);
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ this.buildMaxTernaryHeap(array, length);
+
+ for(int i = length - 1; i >= 0; i--){
+ Writes.swap(array, 0, i, 1, true, false); //add last element on array, i.e heap root
+
+ heapSize = heapSize - 1; //shrink heap by 1
+ this.maxHeapify(array, 0);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/TimSort.java b/src/sorts/TimSort.java
new file mode 100644
index 00000000..abf33192
--- /dev/null
+++ b/src/sorts/TimSort.java
@@ -0,0 +1,54 @@
+package sorts;
+
+import templates.Sort;
+import templates.TimSorting;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class TimSort extends Sort {
+ private TimSorting timSortInstance; // TimSort cannot be simply written off as an abstract class, as it creates an instance of itself
+ // in order to track its state. Plus, it contains both instance and static methods, requiring even
+ // more refactoring, which would be just doing unnecessary busy work. Instead of what we've done for
+ // the rest of the algorithms, we'll favor composition over inheritance here and pass "util" objects
+ // to it.
+
+ public TimSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Tim");
+ this.setRunAllID("Tim Sort");
+ this.setReportSortID("Timsort");
+ this.setCategory("Hybrid Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ this.timSortInstance = new TimSorting(array, currentLength, this.Delays, this.Highlights, this.Reads, this.Writes);
+
+ TimSorting.sort(this.timSortInstance, array, currentLength);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/TimeSort.java b/src/sorts/TimeSort.java
new file mode 100644
index 00000000..50b39f69
--- /dev/null
+++ b/src/sorts/TimeSort.java
@@ -0,0 +1,119 @@
+package sorts;
+
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import main.ArrayVisualizer;
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class TimeSort extends Sort {
+ private InsertionSort insertSorter;
+
+ private volatile int next = 0;
+
+ public TimeSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Time");
+ //this.setRunAllID("Time Sort");
+ this.setRunAllID("Time Sort, Mul 10");
+ this.setReportSortID("Timesort");
+ this.setCategory("Distributive Sorts");
+ this.isComparisonBased(false);
+ this.isBucketSort(false); // *Does not* use buckets! "magnitude" is only a multiplier.
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(true);
+ this.setUnreasonableLimit(2); //See threads.RunDistributionSort for details
+ this.isBogoSort(false);
+ }
+
+ private synchronized void report(int[] array, int a){
+ Writes.write(array, next, a, 0, true, false);
+ next++;
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int magnitude) {
+ insertSorter = new InsertionSort(this.Delays, this.Highlights, this.Reads, this.Writes);
+
+ final int A = magnitude;
+ next = 0;
+
+ ArrayList threads = new ArrayList<>();
+
+ final int[] tmp = new int[length];
+
+ for(int i = 0; i < length; i++) {
+ Writes.write(tmp, i, array[i], 0.25, true, true);
+ }
+
+ double temp = Delays.getCurrentDelay();
+ Delays.setCurrentDelay(magnitude);
+
+ for(int i = 0; i < length; i++){
+ final int c = i;
+
+ threads.add(new Thread(){
+ @Override
+ public void run() {
+ int a = tmp[c];
+
+ try {
+ sleep(a*A);
+ Writes.addTime(A);
+ }
+ catch (InterruptedException ex) {
+ Logger.getLogger(ArrayVisualizer.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ TimeSort.this.report(array, a);
+ }
+ });
+ }
+
+ for(Thread t : threads)
+ t.start();
+
+ try {
+ Thread.sleep(length * A);
+ }
+ catch (InterruptedException e) {
+ Logger.getLogger(ArrayVisualizer.class.getName()).log(Level.SEVERE, null, e);
+ }
+
+ Delays.setCurrentDelay(temp);
+ Writes.setTime(length * A);
+
+ insertSorter.customInsertSort(array, 0, length, 0.2, false);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/TournamentSort.java b/src/sorts/TournamentSort.java
new file mode 100644
index 00000000..66aeb9df
--- /dev/null
+++ b/src/sorts/TournamentSort.java
@@ -0,0 +1,164 @@
+package sorts;
+
+import java.util.ArrayList;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ * "Some implementations of tournament sort in various languages"
+ Copyright (C) 2019 Guy Argo (rugyoga on GitHub)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+ */
+
+final public class TournamentSort extends Sort {
+ private int[] matches;
+ private int tourney;
+
+ public TournamentSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Tournament");
+ this.setRunAllID("Tournament Sort");
+ this.setReportSortID("Tournament Sort");
+ this.setCategory("Selection Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ private int tourneyCompare(int a, int b) {
+ Highlights.markArray(2, a);
+ Highlights.markArray(3, b);
+
+ Delays.sleep(1);
+
+ return Reads.compare(a, b);
+ }
+
+ private static boolean isPlayer(int i) {
+ return i <= 0;
+ }
+
+ private void setWinner(int root, int winner) {
+ Writes.write(matches, root, winner, 0, false, true);
+ }
+ private void setWinners(int root, int winners) {
+ Writes.write(matches, root + 1, winners, 0, false, true);
+ }
+ private void setLosers(int root, int losers) {
+ Writes.write(matches, root + 2, losers, 0, false, true);
+ }
+ private int getWinner(int root) {
+ return matches[root];
+ }
+ private int getWinners(int root) {
+ return matches[root + 1];
+ }
+ private int getLosers(int root) {
+ return matches[root + 2];
+ }
+ private void setMatch(int root, int winner, int winners, int losers) {
+ this.setWinner(root, winner);
+ this.setWinners(root, winners);
+ this.setLosers(root, losers);
+ }
+
+ private int getPlayer(int i) {
+ return i <= 0 ? Math.abs(i) : this.getWinner(i);
+ }
+
+ private int pop(int[] arr) {
+ int result = arr[this.getPlayer(tourney)];
+ tourney = TournamentSort.isPlayer(tourney) ? 0 : this.rebuild(arr, tourney);
+ return result;
+ }
+
+ private static int makePlayer(int i) {
+ return -i;
+ }
+
+ private int makeMatch(int[] arr, int top, int bot, int root) {
+ int top_w = this.getPlayer(top);
+ int bot_w = this.getPlayer(bot);
+
+ if (tourneyCompare(arr[top_w], arr[bot_w]) <= 0)
+ this.setMatch(root, top_w, top, bot);
+ else
+ this.setMatch(root, bot_w, bot, top);
+
+ return root;
+ }
+
+ private int knockout(int[] arr, int i, int k, int root) {
+ if (i == k) return TournamentSort.makePlayer(i);
+
+ int j = (i + k) / 2;
+ return this.makeMatch(arr, this.knockout(arr, i, j, 2 * root), this.knockout(arr,j + 1, k, (2 * root) + 3), root);
+ }
+
+ private int rebuild(int[] arr, int root) {
+ if (TournamentSort.isPlayer(this.getWinners(root)))
+ return this.getLosers(root);
+
+ this.setWinners(root, this.rebuild(arr, this.getWinners(root)));
+
+ if (tourneyCompare(arr[this.getPlayer(this.getLosers(root))], arr[this.getPlayer(this.getWinners(root))]) < 0) {
+ this.setWinner(root, this.getPlayer(this.getLosers(root)));
+
+ int temp = this.getLosers(root);
+
+ this.setLosers(root, this.getWinners(root));
+ this.setWinners(root, temp);
+ }
+ else {
+ this.setWinner(root, this.getPlayer(this.getWinners(root)));
+ }
+
+ return root;
+ }
+
+ private void sort(int[] arr, int currentLen) {
+ ArrayList copy = new ArrayList<>(currentLen);
+ for (int i = 0; i < currentLen; i++) {
+ int result = this.pop(arr);
+
+ Writes.mockWrite(currentLen, copy.size(), result, 0);
+ copy.add(result);
+ }
+
+ Highlights.clearAllMarks();
+
+ for (int i = 0; i < currentLen; i++) {
+ Integer selected = copy.get(i);
+
+ Writes.write(arr, i, selected, 1, true, false);
+ }
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ this.matches = new int[6 * currentLength];
+ this.tourney = this.knockout(array, 0, currentLength - 1, 3);
+
+ this.sort(array, currentLength);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/TreeSort.java b/src/sorts/TreeSort.java
new file mode 100644
index 00000000..3e2bc4b9
--- /dev/null
+++ b/src/sorts/TreeSort.java
@@ -0,0 +1,95 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+final public class TreeSort extends Sort {
+ public TreeSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Tree");
+ this.setRunAllID("Tree Sort (Unbalanced)");
+ this.setReportSortID("Treesort");
+ this.setCategory("Insertion Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ // Code retrieved from https://www.geeksforgeeks.org/tree-sort/
+
+ final private class Node {
+ int key;
+ Node left, right;
+
+ public Node(int item) {
+ key = item;
+ left = right = null;
+ }
+ }
+
+ private Node root;
+ private int index, length;
+
+ private Node treeWrite(Node element, int at) {
+ Node node = new Node(0);
+
+ if(at > 0 && at < this.length) Highlights.markArray(1, at - 1);
+ Writes.changeTempWrites(1);
+ Writes.startLap();
+ node = element;
+ Writes.stopLap();
+
+ Delays.sleep(0.25);
+
+ return node;
+ }
+
+ private void insert(int key) {
+ this.root = this.treeWrite(insertRec(this.root, key, 1), 1);
+ }
+
+ private Node insertRec(Node root, int key, int depth) {
+ if (root == null) {
+ root = treeWrite(new Node(key), 1);
+ return root;
+ }
+
+ if (Reads.compare(key, root.key) == -1)
+ root.left = treeWrite(insertRec(root.left, key, depth * 2), depth * 2);
+ else if (Reads.compare(key, root.key) == 1)
+ root.right = treeWrite(insertRec(root.right, key, (depth * 2) + 1), (depth * 2) + 1);
+
+ return root;
+ }
+
+ private void traverseRec(Node root, int[] array) {
+ if (root != null) {
+ this.traverseRec(root.left, array);
+ Writes.write(array, this.index++, root.key, 1, true, false);
+ this.traverseRec(root.right, array);
+ }
+ }
+
+ private void treeIns(int arr[]) {
+ for(int i = 0; i < this.length; i++) {
+ Highlights.markArray(2, i);
+ this.insert(arr[i]);
+ }
+ Highlights.clearMark(2);
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ this.length = currentLength;
+ this.treeIns(array);
+ this.index = 0;
+ this.traverseRec(this.root, array);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/WeakHeapSort.java b/src/sorts/WeakHeapSort.java
new file mode 100644
index 00000000..a2e27a97
--- /dev/null
+++ b/src/sorts/WeakHeapSort.java
@@ -0,0 +1,86 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+// Refactored from C++ code written by Manish Bhojasia, found here:
+// https://www.sanfoundry.com/cpp-program-implement-weak-heap/
+final public class WeakHeapSort extends Sort {
+ public WeakHeapSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Weak Heap");
+ this.setRunAllID("Weak Heap Sort");
+ this.setReportSortID("Weak Heapsort");
+ this.setCategory("Selection Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ private static int getBitwiseFlag(int[] bits, int x) {
+ return ((bits[(x) >> 3] >> ((x) & 7)) & 1);
+ }
+
+ private void toggleBitwiseFlag(int[] bits, int x) {
+ int flag = bits[(x) >> 3];
+ flag ^= 1 << ((x) & 7);
+
+ Writes.write(bits, (x) >> 3, flag, 0, true, true);
+ }
+
+ /*
+ * Merge Weak Heap
+ */
+ private void weakHeapMerge(int[] array, int[] bits, int i, int j) {
+ if (Reads.compare(array[i], array[j]) == -1)
+ {
+ this.toggleBitwiseFlag(bits, j);
+ Writes.swap(array, i, j, 1, true, false);
+ }
+ }
+
+ @Override
+ public void runSort(int[] array, int length, int bucketCount) {
+ int n = length;
+ int i, j, x, y, Gparent;
+
+ int bitsLength = (n + 7) / 8;
+ int[] bits = new int[bitsLength];
+
+ for (i = 0; i < n / 8; ++i) {
+ Writes.write(bits, i, 0, 0, false, true);
+ }
+
+ for (i = n - 1; i > 0; --i) {
+ j = i;
+
+ while ((j & 1) == WeakHeapSort.getBitwiseFlag(bits, j >> 1))
+ j >>= 1;
+ Gparent = j >> 1;
+
+ this.weakHeapMerge(array, bits, Gparent, i);
+ }
+
+ for (i = n - 1; i >= 2; --i) {
+ Writes.swap(array, 0, i, 1, true, false);
+
+ x = 1;
+
+ while ((y = 2 * x + WeakHeapSort.getBitwiseFlag(bits, x)) < i)
+ x = y;
+
+ while (x > 0) {
+ this.weakHeapMerge(array, bits, 0, x);
+ x >>= 1;
+ }
+ }
+ Writes.swap(array, 0, 1, 1, true, false);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/WeaveMergeSort.java b/src/sorts/WeaveMergeSort.java
new file mode 100644
index 00000000..974a0740
--- /dev/null
+++ b/src/sorts/WeaveMergeSort.java
@@ -0,0 +1,101 @@
+package sorts;
+
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class WeaveMergeSort extends Sort {
+ public WeaveMergeSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Weave Merge");
+ this.setRunAllID("Weave Merge Sort");
+ this.setReportSortID("Weave Mergesort");
+ this.setCategory("Hybrid Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ private void weaveInsert(int[] arr, int start, int end) {
+ int pos;
+
+ for(int j = start; j < end; j++){
+ pos = j;
+
+ Highlights.markArray(1, j);
+ Highlights.clearMark(2);
+
+ while(pos > start && Reads.compare(arr[pos], arr[pos - 1]) < 1) {
+ Writes.swap(arr, pos, pos - 1, 0.2, true, false);
+ pos--;
+ }
+ }
+ }
+
+ private void weaveMerge(int[] arr, int min, int max, int mid) {
+ int i = 1;
+ int target = (mid - min);
+
+ while(i <= target) {
+ Writes.multiSwap(arr, mid + i, min + (i * 2) - 1, 0.05, true, false);
+ i++;
+ }
+
+ this.weaveInsert(arr, min, max + 1);
+ }
+
+ private void weaveMergeSort(int[] array, int min, int max) {
+ if(max - min == 0) { //only one element.
+ Delays.sleep(1); //no swap
+ }
+ else if(max - min == 1) { //only two elements and swaps them
+ if(Reads.compare(array[min], array[max]) == 1) {
+ Writes.swap(array, min, max, 0.01, true, false);
+ }
+ }
+ else {
+ int mid = (int) Math.floor((min + max) / 2); //The midpoint
+
+ this.weaveMergeSort(array, min, mid); //sort the left side
+ this.weaveMergeSort(array, mid + 1, max); //sort the right side
+ this.weaveMerge(array, min, max, mid); //combines them
+ }
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ this.weaveMergeSort(array, 0, currentLength - 1);
+ }
+}
\ No newline at end of file
diff --git a/src/sorts/WikiSort.java b/src/sorts/WikiSort.java
new file mode 100644
index 00000000..a47fe131
--- /dev/null
+++ b/src/sorts/WikiSort.java
@@ -0,0 +1,83 @@
+package sorts;
+
+import templates.Sort;
+import templates.WikiSorting;
+
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to
+ *
+ */
+
+public class WikiSort extends Sort {
+ private WikiSorting wikiSortInstance; // Just like TimSort, WikiSort cannot be simply written off as an abstract class, as it creates an
+ // instance of itself in order to track its state. Plus, it contains both instance and static methods,
+ // requiring even more refactoring, which would be just doing unnecessary busy work. Instead of what
+ // we've done for the rest of the algorithms, we'll favor composition over inheritance here and pass
+ // "util" objects to it.
+
+ private InsertionSort insertionSort;
+
+ // Cache sizes for WikiSort
+
+ // final private static int halfSize = (currentLen + 1) / 2;
+ // final private static int squareRoot = (int) (Math.sqrt((currentLen + 1) / 2) + 1);
+ // final private static int staticBuffer = 32;
+ // final private static int noBuffer = 0;
+
+ private int cache;
+
+ public WikiSort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+
+ this.setSortPromptID("Wiki");
+ //this.setRunAllID("Wiki Sort (Block Merge Sort)");
+ this.setRunAllID("Wiki Sort [Block Merge Sort]");
+ this.setReportSortID("Wikisort");
+ this.setCategory("Hybrid Sorts");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false);
+ this.isUnreasonablySlow(false);
+ this.setUnreasonableLimit(0);
+ this.isBogoSort(false);
+ }
+
+ @Override
+ public void runSort(int[] array, int currentLength, int bucketCount) {
+ this.cache = 0;
+
+ this.insertionSort = new InsertionSort(this.Delays, this.Highlights, this.Reads, this.Writes);
+ this.wikiSortInstance = new WikiSorting(this.insertionSort, this.Delays, this.Highlights, this.Reads, this.Writes, this.cache);
+
+ WikiSorting.sort(this.wikiSortInstance, array, currentLength);
+ }
+}
\ No newline at end of file
diff --git a/src/soundfont/SFXFetcher.java b/src/soundfont/SFXFetcher.java
new file mode 100644
index 00000000..25ec52a9
--- /dev/null
+++ b/src/soundfont/SFXFetcher.java
@@ -0,0 +1,15 @@
+package soundfont;
+
+import java.io.InputStream;
+
+final public class SFXFetcher {
+ private InputStream sfxFile;
+
+ public SFXFetcher() {
+ this.sfxFile = this.getClass().getResourceAsStream("sfx.sf2");
+ }
+
+ public InputStream getSFXFile() {
+ return this.sfxFile;
+ }
+}
\ No newline at end of file
diff --git a/src/soundfont/sfx.sf2 b/src/soundfont/sfx.sf2
new file mode 100644
index 00000000..6445a80a
Binary files /dev/null and b/src/soundfont/sfx.sf2 differ
diff --git a/src/templates/BinaryInsertionSorting.java b/src/templates/BinaryInsertionSorting.java
new file mode 100644
index 00000000..6e28953b
--- /dev/null
+++ b/src/templates/BinaryInsertionSorting.java
@@ -0,0 +1,76 @@
+package templates;
+
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+public abstract class BinaryInsertionSorting extends Sort {
+ protected BinaryInsertionSorting(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+ }
+
+ protected void binaryInsertSort(int[] array, int start, int end, double compSleep, double writeSleep) {
+ for (int i = start; i < end; i++) {
+ int num = array[i];
+ int lo = start, hi = i;
+
+ while (lo < hi) {
+ int mid = lo + ((hi - lo) / 2); // avoid int overflow!
+ Highlights.markArray(1, lo);
+ Highlights.markArray(2, mid);
+ Highlights.markArray(3, hi);
+
+ Delays.sleep(compSleep);
+
+ if (Reads.compare(num, array[mid]) < 0) { // do NOT move equal elements to right of inserted element; this maintains stability!
+ hi = mid;
+ }
+ else {
+ lo = mid + 1;
+ }
+ }
+
+ Highlights.clearMark(3);
+
+ // item has to go into position lo
+
+ int j = i - 1;
+
+ while (j >= lo)
+ {
+ Writes.write(array, j + 1, array[j], writeSleep, true, false);
+ j--;
+ }
+ Writes.write(array, lo, num, writeSleep, true, false);
+
+ Highlights.clearAllMarks();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/templates/BinaryQuickSorting.java b/src/templates/BinaryQuickSorting.java
new file mode 100644
index 00000000..bec3d986
--- /dev/null
+++ b/src/templates/BinaryQuickSorting.java
@@ -0,0 +1,102 @@
+package templates;
+
+import java.util.LinkedList;
+import java.util.Queue;
+
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/**
+ * Binary MSD Radix Sort / Binary Quicksort.
+ *
+ * Implemented as recursive decent, and via task queue, see:
+ * * binaryQuickSortRecursive, and
+ * * binaryQuickSort respectively.
+ *
+ * Both of which are in-place sorting algorithms, with the recursive utilizing
+ * the stack for divide-and-conquer, while the non-recursive utilizes a queue.
+ *
+ * Can be extended to support unsigned integers, by sorting the first bit rin
+ * reverse. Can be made stable at the cost of O(n) memory. Can be parallalized
+ * to O(log2(n)) subtasks / threads.
+ *
+ * @author Skeen
+ */
+
+public abstract class BinaryQuickSorting extends Sort {
+ protected BinaryQuickSorting(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+ }
+
+ public static class Task {
+ public int p;
+ public int r;
+ public int bit;
+
+ public Task(int p, int r, int bit)
+ {
+ this.p = p;
+ this.r = r;
+ this.bit = bit;
+ }
+ }
+
+ public void binaryQuickSortRecursive(int[] array, int p, int r, int bit)
+ {
+ if (p < r && bit >= 0)
+ {
+ int q = partition(array, p, r, bit);
+ Delays.sleep(1);
+ binaryQuickSortRecursive(array, p, q, bit-1);
+ binaryQuickSortRecursive(array, q+1, r, bit-1);
+ }
+ }
+
+ public void binaryQuickSort(int[] array, int p, int r, int bit)
+ {
+ Queue tasks = new LinkedList<>();
+ tasks.add(new Task(p, r, bit));
+
+ while (tasks.isEmpty() == false)
+ {
+ Task task = tasks.remove();
+ if (task.p < task.r && task.bit >= 0)
+ {
+ int q = partition(array, task.p, task.r, task.bit);
+ Delays.sleep(1);
+ tasks.add(new Task(task.p, q, task.bit-1));
+ tasks.add(new Task(q+1, task.r, task.bit-1));
+ }
+ }
+ }
+
+ public int partition(int[] array, int p, int r, int bit)
+ {
+ int i = p - 1;
+ int j = r + 1;
+
+ while (true) {
+ // Left is not set
+ i++;
+ while(i < r && !Reads.getBit(array[i], bit)) {
+ i++;
+ Highlights.markArray(1, i);
+ Delays.sleep(0.45);
+ }
+ // Right is set
+ j--;
+ while(j > p && Reads.getBit(array[j], bit)) {
+ j--;
+ Highlights.markArray(2, j);
+ Delays.sleep(0.45);
+ }
+ // If i is less than j, we swap, otherwise we are done
+ if (i < j)
+ Writes.swap(array, i, j, 1, true, false);
+ else
+ return j;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/templates/BogoSorting.java b/src/templates/BogoSorting.java
new file mode 100644
index 00000000..55ae58d9
--- /dev/null
+++ b/src/templates/BogoSorting.java
@@ -0,0 +1,93 @@
+package templates;
+
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+public abstract class BogoSorting extends Sort {
+ protected BogoSorting(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+ }
+
+ private static int randomPosition(int length, int offset) {
+ return (int) (Math.random() * (length - offset));
+ }
+
+ protected void bogoSwap(int[] array, int length, int offset){
+ for(int i = offset; i < length; i++) {
+ Writes.swap(array, i, BogoSorting.randomPosition(length, i) + i, 0, true, false);
+ }
+ }
+
+ protected boolean bogoIsSorted(int[] array, int length){
+ for(int i = 0; i < length - 1; i++) {
+ if(Reads.compare(array[i], array[i + 1]) == 1) {
+ Highlights.markArray(1, i);
+ return false;
+ }
+ }
+ Highlights.clearMark(1);
+ return true;
+ }
+
+ protected boolean isMinSorted(int[] array, int length, int offset) {
+ Highlights.clearAllMarks();
+
+ //Highlights.markArray(2, offset);
+ //Highlights.markArray(3, length);
+
+ for(int i = offset + 1; i < length; i++) {
+ Highlights.markArray(1, i);
+ Delays.sleep(0.075);
+
+ if(Reads.compare(array[offset], array[i]) == 1) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ protected boolean isMaxSorted(int[] array, int minIterator, int maxIterator) {
+ Highlights.clearAllMarks();
+
+ //Highlights.markArray(2, minIterator);
+ //Highlights.markArray(3, maxIterator);
+
+ for(int i = maxIterator; i >= minIterator; i--) {
+ Highlights.markArray(1, i);
+ Delays.sleep(0.075);
+
+ if(Reads.compare(array[maxIterator], array[i]) == -1) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/templates/CircleSorting.java b/src/templates/CircleSorting.java
new file mode 100644
index 00000000..387662ed
--- /dev/null
+++ b/src/templates/CircleSorting.java
@@ -0,0 +1,56 @@
+package templates;
+
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+Copyright (c) rosettacode.org.
+Permission is granted to copy, distribute and/or modify this document
+under the terms of the GNU Free Documentation License, Version 1.2
+or any later version published by the Free Software Foundation;
+with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
+Texts. A copy of the license is included in the section entitled "GNU
+Free Documentation License".
+ *
+ */
+
+public abstract class CircleSorting extends Sort {
+ protected CircleSorting(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+ }
+
+ protected int circleSortRoutine(int[] array, int lo, int hi, int swapCount) {
+ if (lo == hi)
+ return swapCount;
+
+ int high = hi;
+ int low = lo;
+ int mid = (hi - lo) / 2;
+
+ while (lo < hi) {
+ if (Reads.compare(array[lo], array[hi]) > 0) {
+ Writes.swap(array, lo, hi, 1, true, false);
+ swapCount++;
+ }
+ lo++;
+ hi--;
+
+ Highlights.markArray(1, lo);
+ Highlights.markArray(2, hi);
+ Delays.sleep(0.5);
+ }
+
+ if (lo == hi && Reads.compare(array[lo], array[hi + 1]) > 0) {
+ Writes.swap(array, lo, hi + 1, 1, true, false);
+ swapCount++;
+ }
+
+ swapCount = this.circleSortRoutine(array, low, low + mid, swapCount);
+ swapCount = this.circleSortRoutine(array, low + mid + 1, high, swapCount);
+
+ return swapCount;
+ }
+}
\ No newline at end of file
diff --git a/src/templates/CombSorting.java b/src/templates/CombSorting.java
new file mode 100644
index 00000000..ce22b5f3
--- /dev/null
+++ b/src/templates/CombSorting.java
@@ -0,0 +1,106 @@
+package templates;
+
+import main.ArrayVisualizer;
+import sorts.InsertionSort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+The MIT License (MIT)
+
+Copyright (c) 2012 Daniel Imms, http://www.growingwiththeweb.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+public abstract class CombSorting extends Sort {
+ protected ArrayVisualizer ArrayVisualizer; // Instance of ArrayVisualizer used to print
+ // Combsort's current gap to the statistics
+
+ private InsertionSort insertSorter;
+
+ final private double[] shrinkFactors = {1.1, 1.15, 1.2, 1.25, 1.3, 1.35, 1.4, 1.45, 1.5};
+ private int shrinkChoice;
+
+ protected CombSorting(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+ }
+
+ protected double[] getShrinkFactors() {
+ return this.shrinkFactors;
+ }
+
+ protected void setShrinkFactor(int choice) {
+ this.shrinkChoice = choice;
+ }
+
+ public void setArrayVisualizer(ArrayVisualizer av) {
+ this.ArrayVisualizer = av;
+ }
+
+ protected void combSort(ArrayVisualizer ArrayVisualizer, int[] array, int length, boolean hybrid) {
+ insertSorter = new InsertionSort(this.Delays, this.Highlights, this.Reads, this.Writes);
+ double shrink = shrinkFactors[this.shrinkChoice];
+
+ boolean swapped = false;
+ int gap = length;
+
+ double tempDelay = Delays.getSleepRatio();
+
+ while ((gap > 1) || swapped)
+ {
+ Highlights.clearMark(2);
+
+ if (gap > 1) {
+ gap = (int) ((float) gap / shrink);
+ //ArrayVisualizer.setCurrentGap(gap);
+
+ if(gap == 1)
+ Delays.setSleepRatio(3);
+ }
+
+ swapped = false;
+
+ for (int i = 0; (gap + i) < length; ++i)
+ {
+ if(hybrid && (gap <= Math.min(8, length * 0.03125))) {
+ gap = 0;
+
+ insertSorter.customInsertSort(array, 0, length, 0.5, false);
+ break;
+ }
+ if (Reads.compare(array[i], array[i + gap]) == 1)
+ {
+ Writes.swap(array, i, i+gap, 0.75, true, false);
+ swapped = true;
+ }
+ Highlights.markArray(1, i);
+ Highlights.markArray(2, i + gap);
+
+ Delays.sleep(0.25);
+ Highlights.clearMark(1);
+ }
+ }
+
+ Delays.setSleepRatio(tempDelay);
+ }
+}
\ No newline at end of file
diff --git a/src/templates/Frame.java b/src/templates/Frame.java
new file mode 100644
index 00000000..abb641e1
--- /dev/null
+++ b/src/templates/Frame.java
@@ -0,0 +1,37 @@
+package templates;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+/**
+ *
+ * @author S630690
+ */
+public interface Frame {
+ abstract void reposition();
+ abstract boolean isVisible();
+ abstract void dispose();
+}
\ No newline at end of file
diff --git a/src/templates/GrailSorting.java b/src/templates/GrailSorting.java
new file mode 100644
index 00000000..7a3027d4
--- /dev/null
+++ b/src/templates/GrailSorting.java
@@ -0,0 +1,774 @@
+package templates;
+
+import sorts.SmartGnomeSort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+The MIT License (MIT)
+
+Copyright (c) 2013 Andrey Astrelin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/********* Grail sorting *********************************/
+/* */
+/* (c) 2013 by Andrey Astrelin */
+/* Refactored by MusicTheorist */
+/* */
+/* Stable sorting that works in O(N*log(N)) worst time */
+/* and uses O(1) extra memory */
+/* */
+/* Define int / SortComparator */
+/* and then call GrailSort() function */
+/* */
+/* For sorting w/ fixed external buffer (512 items) */
+/* use GrailSortWithBuffer() */
+/* */
+/* For sorting w/ dynamic external buffer (sqrt(length)) */
+/* use GrailSortWithDynBuffer() */
+/* */
+/*********************************************************/
+
+final class GrailPair {
+ private int leftOverLen;
+ private int leftOverFrag;
+
+ protected GrailPair(int len, int frag) {
+ this.leftOverLen = len;
+ this.leftOverFrag = frag;
+ }
+
+ protected int getLeftOverLen() {
+ return leftOverLen;
+ }
+
+ protected int getLeftOverFrag() {
+ return leftOverFrag;
+ }
+}
+
+public abstract class GrailSorting extends Sort {
+ private SmartGnomeSort grailInsertSorter;
+
+ final private int grailStaticBufferLen = 32; //Buffer length changed due to less numbers in this program being sorted than what Mr. Astrelin used for testing.
+
+ protected GrailSorting(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+ }
+
+ protected int getStaticBuffer() {
+ return this.grailStaticBufferLen;
+ }
+
+ private void grailSwap(int[] arr, int a, int b) {
+ Writes.swap(arr, a, b, 1, true, false);
+ }
+
+ private void grailMultiSwap(int[] arr, int a, int b, int swapsLeft) {
+ while(swapsLeft != 0) {
+ this.grailSwap(arr, a++, b++);
+ swapsLeft--;
+ }
+ }
+
+ private void grailRotate(int[] array, int pos, int lenA, int lenB) {
+ while(lenA != 0 && lenB != 0) {
+ if(lenA <= lenB) {
+ this.grailMultiSwap(array, pos, pos + lenA, lenA);
+ pos += lenA;
+ lenB -= lenA;
+ }
+ else {
+ this.grailMultiSwap(array, pos + (lenA - lenB), pos + lenA, lenB);
+ lenA -= lenB;
+ }
+ }
+ }
+
+ private void grailInsertSort(int[] arr, int pos, int len) {
+ grailInsertSorter.customSort(arr, pos, len, 0.75);
+ }
+
+ //boolean argument determines direction
+ private int grailBinSearch(int[] arr, int pos, int len, int keyPos, boolean isLeft) {
+ int left = -1, right = len;
+ while(left < right - 1) {
+ int mid = left + ((right - left) >> 1);
+ if(isLeft) {
+ if(Reads.compare(arr[pos + mid], arr[keyPos]) >= 0) {
+ right = mid;
+ } else {
+ left = mid;
+ }
+ } else {
+ if(Reads.compare(arr[pos + mid], arr[keyPos]) > 0) {
+ right = mid;
+ } else left = mid;
+ }
+ Highlights.markArray(1, pos + mid);
+ }
+ return right;
+ }
+
+ // cost: 2 * len + numKeys^2 / 2
+ private int grailFindKeys(int[] arr, int pos, int len, int numKeys) {
+ int dist = 1, foundKeys = 1, firstKey = 0; // first key is always here
+
+ while(dist < len && foundKeys < numKeys) {
+ //Binary Search left
+ int loc = this.grailBinSearch(arr, pos + firstKey, foundKeys, pos + dist, true);
+ if(loc == foundKeys || Reads.compare(arr[pos + dist], arr[pos + (firstKey + loc)]) != 0) {
+ this.grailRotate(arr, pos + firstKey, foundKeys, dist - (firstKey + foundKeys));
+ firstKey = dist - foundKeys;
+ this.grailRotate(arr, pos + (firstKey + loc), foundKeys - loc, 1);
+ foundKeys++;
+ }
+ dist++;
+ }
+ this.grailRotate(arr, pos, firstKey, foundKeys);
+
+ Highlights.clearMark(2);
+
+ return foundKeys;
+ }
+
+ // cost: min(len1, len2)^2 + max(len1, len2)
+ private void grailMergeWithoutBuffer(int[] arr, int pos, int len1, int len2) {
+ if(len1 < len2) {
+ while(len1 != 0) {
+ //Binary Search left
+ int loc = this.grailBinSearch(arr, pos + len1, len2, pos, true);
+ if(loc != 0) {
+ this.grailRotate(arr, pos, len1, loc);
+ pos += loc;
+ len2 -= loc;
+ }
+ if(len2 == 0) break;
+ do {
+ pos++;
+ len1--;
+ } while(len1 != 0 && Reads.compare(arr[pos], arr[pos + len1]) <= 0);
+ }
+ } else {
+ while(len2 != 0) {
+ //Binary Search right
+ int loc = this.grailBinSearch(arr, pos, len1, pos + (len1 + len2 - 1), false);
+ if(loc != len1) {
+ this.grailRotate(arr, pos + loc, len1 - loc, len2);
+ len1 = loc;
+ }
+ if(len1 == 0) break;
+ do {
+ len2--;
+ } while(len2 != 0 && Reads.compare(arr[pos + len1 - 1], arr[pos + len1 + len2 - 1]) <= 0);
+ }
+ }
+ }
+
+ // arr - starting array. arr[0 - regBlockLen..-1] - buffer (if havebuf).
+ // regBlockLen - length of regular blocks. First blockCount blocks are stable sorted by 1st elements and key-coded
+ // keysPos - arrays of keys, in same order as blocks. keysPos < midkey means stream A
+ // aBlockCount are regular blocks from stream A.
+ // lastLen is length of last (irregular) block from stream B, that should go before nblock2 blocks.
+ // lastLen = 0 requires aBlockCount = 0 (no irregular blocks). lastLen > 0, aBlockCount = 0 is possible.
+ private void grailMergeBuffersLeft(int[] arr, int keysPos, int midkey, int pos,
+ int blockCount, int blockLen, boolean havebuf, int aBlockCount,
+ int lastLen) {
+
+ if(blockCount == 0) {
+ int aBlocksLen = aBlockCount * blockLen;
+ if(havebuf) this.grailMergeLeft(arr, pos, aBlocksLen, lastLen, 0 - blockLen);
+ else this.grailMergeWithoutBuffer(arr, pos, aBlocksLen, lastLen);
+ return;
+ }
+
+ int leftOverLen = blockLen;
+ int leftOverFrag = Reads.compare(arr[keysPos], arr[midkey]) < 0 ? 0 : 1;
+ int processIndex = blockLen;
+ int restToProcess;
+
+ for(int keyIndex = 1; keyIndex < blockCount; keyIndex++, processIndex += blockLen) {
+ restToProcess = processIndex - leftOverLen;
+ int nextFrag = Reads.compare(arr[keysPos + keyIndex], arr[midkey]) < 0 ? 0 : 1;
+
+ if(nextFrag == leftOverFrag) {
+ if(havebuf) this.grailMultiSwap(arr, pos + restToProcess - blockLen, pos + restToProcess, leftOverLen);
+ restToProcess = processIndex;
+ leftOverLen = blockLen;
+ } else {
+ if(havebuf) {
+ GrailPair results = this.grailSmartMergeWithBuffer(arr, pos + restToProcess, leftOverLen, leftOverFrag, blockLen);
+ leftOverLen = results.getLeftOverLen();
+ leftOverFrag = results.getLeftOverFrag();
+ } else {
+ GrailPair results = this.grailSmartMergeWithoutBuffer(arr, pos + restToProcess, leftOverLen, leftOverFrag, blockLen);
+ leftOverLen = results.getLeftOverLen();
+ leftOverFrag = results.getLeftOverFrag();
+ }
+ }
+ }
+ restToProcess = processIndex - leftOverLen;
+
+ if(lastLen != 0) {
+ if(leftOverFrag != 0) {
+ if(havebuf) {
+ this.grailMultiSwap(arr, pos + restToProcess - blockLen, pos + restToProcess, leftOverLen);
+ }
+ restToProcess = processIndex;
+ leftOverLen = blockLen * aBlockCount;
+ leftOverFrag = 0;
+ } else {
+ leftOverLen += blockLen * aBlockCount;
+ }
+ if(havebuf) {
+ this.grailMergeLeft(arr, pos + restToProcess, leftOverLen, lastLen, -blockLen);
+ }
+ else {
+ this.grailMergeWithoutBuffer(arr, pos + restToProcess, leftOverLen, lastLen);
+ }
+ } else {
+ if(havebuf) {
+ this.grailMultiSwap(arr, pos + restToProcess, pos + (restToProcess - blockLen), leftOverLen);
+ }
+ }
+ }
+
+ // arr[dist..-1] - buffer, arr[0, leftLen - 1] ++ arr[leftLen, leftLen + rightLen - 1]
+ // -> arr[dist, dist + leftLen + rightLen - 1]
+ private void grailMergeLeft(int[] arr, int pos, int leftLen, int rightLen, int dist) {
+ int left = 0;
+ int right = leftLen;
+
+ rightLen += leftLen;
+
+ while(right < rightLen) {
+ if(left == leftLen || Reads.compare(arr[pos + left], arr[pos + right]) > 0) {
+ this.grailSwap(arr, pos + (dist++), pos + (right++));
+ }
+ else this.grailSwap(arr, pos + (dist++), pos + (left++));
+ Highlights.markArray(3, pos + left);
+ Highlights.markArray(4, pos + right);
+ }
+ Highlights.clearMark(3);
+ Highlights.clearMark(4);
+
+ if(dist != left) this.grailMultiSwap(arr, pos + dist, pos + left, leftLen - left);
+ }
+ private void grailMergeRight(int[] arr, int pos, int leftLen, int rightLen, int dist) {
+ int mergedPos = leftLen + rightLen + dist - 1;
+ int right = leftLen + rightLen - 1;
+ int left = leftLen - 1;
+
+ while(left >= 0) {
+ if(right < leftLen || Reads.compare(arr[pos + left], arr[pos + right]) > 0) {
+ this.grailSwap(arr, pos + (mergedPos--), pos + (left--));
+ }
+ else this.grailSwap(arr, pos + (mergedPos--), pos + (right--));
+ Highlights.markArray(3, pos + left);
+ Highlights.markArray(4, pos + right);
+ }
+ Highlights.clearMark(3);
+ Highlights.clearMark(4);
+
+ if(right != mergedPos) {
+ while(right >= leftLen) this.grailSwap(arr, pos + (mergedPos--), pos + (right--));
+ }
+ }
+
+ //returns the leftover length, then the leftover fragment
+ private GrailPair grailSmartMergeWithoutBuffer(int[] arr, int pos, int leftOverLen, int leftOverFrag, int regBlockLen) {
+ if(regBlockLen == 0) return new GrailPair(leftOverLen, leftOverFrag);
+
+ int len1 = leftOverLen;
+ int len2 = regBlockLen;
+ int typeFrag = 1 - leftOverFrag; //1 if inverted
+
+ if(len1 != 0 && Reads.compare(arr[pos + (len1 - 1)], arr[pos + len1]) - typeFrag >= 0) {
+
+ while(len1 != 0) {
+ int foundLen;
+ if (typeFrag != 0) {
+ //Binary Search left
+ foundLen = this.grailBinSearch(arr, pos + len1, len2, pos, true);
+ } else {
+ //Binary Search right
+ foundLen = this.grailBinSearch(arr, pos + len1, len2, pos, false);
+ }
+ if(foundLen != 0) {
+ this.grailRotate(arr, pos, len1, foundLen);
+ pos += foundLen;
+ len2 -= foundLen;
+ }
+ if(len2 == 0) {
+ return new GrailPair(len1, leftOverFrag);
+ }
+ do {
+ pos++;
+ len1--;
+ } while(len1 != 0 && Reads.compare(arr[pos], arr[pos + len1]) - typeFrag < 0);
+ }
+ }
+ return new GrailPair(len2, typeFrag);
+ }
+
+ //returns the leftover length, then the leftover fragment
+ private GrailPair grailSmartMergeWithBuffer(int[] arr, int pos, int leftOverLen, int leftOverFrag, int blockLen) {
+ int dist = 0 - blockLen, left = 0, right = leftOverLen, leftEnd = right, rightEnd = right + blockLen;
+ int typeFrag = 1 - leftOverFrag; // 1 if inverted
+
+ while(left < leftEnd && right < rightEnd) {
+ if(Reads.compare(arr[pos + left], arr[pos + right]) - typeFrag < 0) {
+ this.grailSwap(arr, pos + (dist++), pos + (left++));
+ }
+ else this.grailSwap(arr, pos + (dist++), pos + (right++));
+ Highlights.markArray(3, pos + left);
+ Highlights.markArray(4, pos + right);
+ }
+ Highlights.clearMark(3);
+ Highlights.clearMark(4);
+
+ int length, fragment = leftOverFrag;
+ if(left < leftEnd) {
+ length = leftEnd - left;
+ while(left < leftEnd) this.grailSwap(arr, pos + (--leftEnd), pos + (--rightEnd));
+ } else {
+ length = rightEnd - right;
+ fragment = typeFrag;
+ }
+ return new GrailPair(length, fragment);
+ }
+
+
+ /***** Sort With Extra Buffer *****/
+
+ //returns the leftover length, then the leftover fragment
+ private GrailPair grailSmartMergeWithXBuf(int[] arr, int pos, int leftOverLen, int leftOverFrag, int blockLen) {
+ int dist = 0 - blockLen, left = 0, right = leftOverLen, leftEnd = right, rightEnd = right + blockLen;
+ int typeFrag = 1 - leftOverFrag; // 1 if inverted
+
+ Highlights.clearMark(2);
+
+ while(left < leftEnd && right < rightEnd) {
+ if(Reads.compare(arr[pos + left], arr[pos + right]) - typeFrag < 0) {
+ Writes.write(arr, pos + dist++, arr[pos + left++], 1, true, false);
+ }
+ else Writes.write(arr, pos + dist++, arr[pos + right++], 1, true, false);
+ Highlights.markArray(2, pos + left);
+ Highlights.markArray(3, pos + right);
+ }
+ Highlights.clearMark(2);
+ Highlights.clearMark(3);
+
+ int length, fragment = leftOverFrag;
+ if(left < leftEnd) {
+ length = leftEnd - left;
+ while(left < leftEnd) Writes.write(arr, pos + --rightEnd, arr[pos + --leftEnd], 1, true, false);
+ } else {
+ length = rightEnd - right;
+ fragment = typeFrag;
+ }
+ return new GrailPair(length, fragment);
+ }
+
+ // arr[dist..-1] - free, arr[0, leftEnd - 1] ++ arr[leftEnd, leftEnd + rightEnd - 1]
+ // -> arr[dist, dist + leftEnd + rightEnd - 1]
+ private void grailMergeLeftWithXBuf(int[] arr, int pos, int leftEnd, int rightEnd, int dist) {
+ int left = 0;
+ int right = leftEnd;
+ rightEnd += leftEnd;
+
+ Highlights.clearMark(2);
+
+ while(right < rightEnd) {
+ if(left == leftEnd || Reads.compare(arr[pos + left], arr[pos + right]) > 0) {
+ Writes.write(arr, pos + dist++, arr[pos + right++], 1, true, false);
+ }
+ else Writes.write(arr, pos + dist++, arr[pos + left++], 1, true, false);
+ Highlights.markArray(2, pos + left);
+ Highlights.markArray(3, pos + right);
+ }
+ Highlights.clearMark(2);
+ Highlights.clearMark(3);
+
+ if(dist != left) {
+ while(left < leftEnd) Writes.write(arr, pos + dist++, arr[pos + left++], 1, true, false);
+ }
+ }
+
+ // arr - starting array. arr[0 - regBlockLen..-1] - buffer (if havebuf).
+ // regBlockLen - length of regular blocks. First blockCount blocks are stable sorted by 1st elements and key-coded
+ // keysPos - where keys are in array, in same order as blocks. keysPos < midkey means stream A
+ // aBlockCount are regular blocks from stream A.
+ // lastLen is length of last (irregular) block from stream B, that should go before aCountBlock blocks.
+ // lastLen = 0 requires aBlockCount = 0 (no irregular blocks). lastLen > 0, aBlockCount = 0 is possible.
+ private void grailMergeBuffersLeftWithXBuf(int[] arr, int keysPos, int midkey, int pos,
+ int blockCount, int regBlockLen, int aBlockCount, int lastLen) {
+
+ Highlights.clearMark(2);
+
+ if(blockCount == 0) {
+ int aBlocksLen = aBlockCount * regBlockLen;
+ this.grailMergeLeftWithXBuf(arr, pos, aBlocksLen, lastLen, 0 - regBlockLen);
+ return;
+ }
+
+ int leftOverLen = regBlockLen;
+ int leftOverFrag = Reads.compare(arr[keysPos], arr[midkey]) < 0 ? 0 : 1;
+ int processIndex = regBlockLen;
+
+ int restToProcess;
+ for(int keyIndex = 1; keyIndex < blockCount; keyIndex++, processIndex += regBlockLen) {
+ restToProcess = processIndex - leftOverLen;
+ int nextFrag = Reads.compare(arr[keysPos + keyIndex], arr[midkey]) < 0 ? 0 : 1;
+
+ if(nextFrag == leftOverFrag) {
+ Writes.arraycopy(arr, pos + restToProcess, arr, pos + restToProcess - regBlockLen, leftOverLen, 1, true, false);
+
+ restToProcess = processIndex;
+ leftOverLen = regBlockLen;
+ } else {
+ GrailPair results = this.grailSmartMergeWithXBuf(arr, pos + restToProcess, leftOverLen, leftOverFrag, regBlockLen);
+ leftOverLen = results.getLeftOverLen();
+ leftOverFrag = results.getLeftOverFrag();
+ }
+ }
+ restToProcess = processIndex - leftOverLen;
+
+ if(lastLen != 0) {
+ if(leftOverFrag != 0) {
+ Writes.arraycopy(arr, pos + restToProcess, arr, pos + restToProcess - regBlockLen, leftOverLen, 1, true, false);
+
+ restToProcess = processIndex;
+ leftOverLen = regBlockLen * aBlockCount;
+ leftOverFrag = 0;
+ } else {
+ leftOverLen += regBlockLen * aBlockCount;
+ }
+ this.grailMergeLeftWithXBuf(arr, pos + restToProcess, leftOverLen, lastLen, 0 - regBlockLen);
+ } else {
+ Writes.arraycopy(arr, pos + restToProcess, arr, pos + restToProcess - regBlockLen, leftOverLen, 1, true, false);
+ }
+ }
+
+ /***** End Sort With Extra Buffer *****/
+
+ // build blocks of length buildLen
+ // input: [-buildLen, -1] elements are buffer
+ // output: first buildLen elements are buffer, blocks 2 * buildLen and last subblock sorted
+ private void grailBuildBlocks(int[] arr, int pos, int len, int buildLen,
+ int[] extbuf, int bufferPos, int extBufLen) {
+
+ int buildBuf = buildLen < extBufLen ? buildLen : extBufLen;
+ while((buildBuf & (buildBuf - 1)) != 0) buildBuf &= buildBuf - 1; // max power or 2 - just in case
+
+ int extraDist, part;
+ if(buildBuf != 0) {
+ Writes.arraycopy(arr, pos - buildBuf, extbuf, bufferPos, buildBuf, 1, true, true);
+
+ for(int dist = 1; dist < len; dist += 2) {
+ extraDist = 0;
+ if(Reads.compare(arr[pos + (dist - 1)], arr[pos + dist]) > 0) extraDist = 1;
+ Writes.write(arr, pos + dist - 3, arr[pos + dist - 1 + extraDist], 1, true, false);
+ Writes.write(arr, pos + dist - 2, arr[pos + dist - extraDist], 1, true, false);
+ }
+ if(len % 2 != 0) Writes.write(arr, pos + len - 3, arr[pos + len - 1], 1, true, false);
+ pos -= 2;
+
+ for(part = 2; part < buildBuf; part *= 2) {
+ int left = 0;
+ int right = len - 2 * part;
+ while(left <= right) {
+ this.grailMergeLeftWithXBuf(arr, pos + left, part, part, 0 - part);
+ left += 2 * part;
+ }
+ int rest = len - left;
+
+ if(rest > part) {
+ this.grailMergeLeftWithXBuf(arr, pos + left, part, rest - part, 0 - part);
+ } else {
+ for(; left < len; left++) Writes.write(arr, pos + left - part, arr[pos + left], 1, true, false);
+ }
+ pos -= part;
+ }
+ Writes.arraycopy(extbuf, bufferPos, arr, pos + len, buildBuf, 1, true, false);
+ }
+ else {
+ for(int dist = 1; dist < len; dist += 2) {
+ extraDist = 0;
+ if(Reads.compare(arr[pos + (dist - 1)], arr[pos + dist]) > 0) extraDist = 1;
+ this.grailSwap(arr, pos + (dist - 3), pos + (dist - 1 + extraDist));
+ this.grailSwap(arr, pos + (dist - 2), pos + (dist - extraDist));
+ }
+ if(len % 2 != 0) this.grailSwap(arr, pos + (len - 1), pos + (len - 3));
+ pos -= 2;
+ part = 2;
+ }
+
+ for(; part < buildLen; part *= 2) {
+ int left = 0;
+ int right = len - 2 * part;
+ while(left <= right) {
+ this.grailMergeLeft(arr, pos + left, part, part, 0 - part);
+ left += 2 * part;
+ }
+ int rest = len - left;
+ if(rest > part) {
+ this.grailMergeLeft(arr, pos + left, part, rest - part, 0 - part);
+ } else {
+ this.grailRotate(arr, pos + left - part, part, rest);
+ }
+ pos -= part;
+ }
+ int restToBuild = len % (2 * buildLen);
+ int leftOverPos = len - restToBuild;
+
+ if(restToBuild <= buildLen) this.grailRotate(arr, pos + leftOverPos, restToBuild, buildLen);
+ else this.grailMergeRight(arr, pos + leftOverPos, buildLen, restToBuild - buildLen, buildLen);
+
+ while(leftOverPos > 0) {
+ leftOverPos -= 2 * buildLen;
+ this.grailMergeRight(arr, pos + leftOverPos, buildLen, buildLen, buildLen);
+ }
+ }
+
+ // keys are on the left of arr. Blocks of length buildLen combined. We'll combine them in pairs
+ // buildLen and nkeys are powers of 2. (2 * buildLen / regBlockLen) keys are guaranteed
+ private void grailCombineBlocks(int[] arr, int keyPos, int pos, int len, int buildLen,
+ int regBlockLen, boolean havebuf, int[] buffer, int bufferPos) {
+
+ int combineLen = len / (2 * buildLen);
+ int leftOver = len % (2 * buildLen);
+ if(leftOver <= buildLen) {
+ len -= leftOver;
+ leftOver = 0;
+ }
+
+ if(buffer != null) Writes.arraycopy(arr, pos - regBlockLen, buffer, bufferPos, regBlockLen, 1, true, true);
+
+ for(int i = 0; i <= combineLen; i++) {
+ if(i == combineLen && leftOver == 0) break;
+
+ int blockPos = pos + i * 2 * buildLen;
+ int blockCount = (i == combineLen ? leftOver : 2 * buildLen) / regBlockLen;
+
+ this.grailInsertSort(arr, keyPos, blockCount + (i == combineLen ? 1 : 0));
+
+ int midkey = buildLen / regBlockLen;
+
+ for(int index = 1; index < blockCount; index++) {
+ int leftIndex = index - 1;
+
+ for(int rightIndex = index; rightIndex < blockCount; rightIndex++) {
+ int rightComp = Reads.compare(arr[blockPos + leftIndex * regBlockLen],
+ arr[blockPos + rightIndex * regBlockLen]);
+ if(rightComp > 0 || (rightComp == 0 && Reads.compare(arr[keyPos + leftIndex], arr[keyPos + rightIndex]) > 0)) {
+ leftIndex = rightIndex;
+ }
+ }
+ if(leftIndex != index - 1) {
+ this.grailMultiSwap(arr, blockPos + (index - 1) * regBlockLen, blockPos + leftIndex * regBlockLen, regBlockLen);
+ this.grailSwap(arr, keyPos + (index - 1), keyPos + leftIndex);
+ if(midkey == index - 1 || midkey == leftIndex) {
+ midkey ^= (index - 1) ^ leftIndex;
+ }
+ }
+ }
+
+ int aBlockCount = 0;
+ int lastLen = 0;
+ if(i == combineLen) lastLen = leftOver % regBlockLen;
+
+ if(lastLen != 0) {
+ while(aBlockCount < blockCount && Reads.compare(arr[blockPos + blockCount * regBlockLen],
+ arr[blockPos + (blockCount - aBlockCount - 1) * regBlockLen]) < 0) {
+ aBlockCount++;
+ }
+ }
+
+ if(buffer != null) {
+ this.grailMergeBuffersLeftWithXBuf(arr, keyPos, keyPos + midkey, blockPos,
+ blockCount - aBlockCount, regBlockLen, aBlockCount, lastLen);
+ }
+ else this.grailMergeBuffersLeft(arr, keyPos, keyPos + midkey, blockPos,
+ blockCount - aBlockCount, regBlockLen, havebuf, aBlockCount, lastLen);
+ }
+ if(buffer != null) {
+ for(int i = len; --i >= 0;) Writes.write(arr, pos + i, arr[pos + i - regBlockLen], 1, true, false);
+ Writes.arraycopy(buffer, bufferPos, arr, pos - regBlockLen, regBlockLen, 1, true, false);
+ }
+ else if(havebuf) {
+ while(--len >= 0) {
+ this.grailSwap(arr, pos + len, pos + len - regBlockLen);
+ }
+ }
+ }
+
+ protected void grailLazyStableSort(int[] arr, int pos, int len) {
+ for(int dist = 1; dist < len; dist += 2) {
+ if(Reads.compare(arr[pos + dist - 1], arr[pos + dist]) > 0) {
+ this.grailSwap(arr, pos + (dist - 1), pos + dist);
+ }
+ Highlights.markArray(3, pos + dist - 1);
+ Highlights.markArray(4, pos + dist);
+ }
+ Highlights.clearMark(3);
+ Highlights.clearMark(4);
+
+ for(int part = 2; part < len; part *= 2) {
+ int left = 0;
+ int right = len - 2 * part;
+
+ while(left <= right) {
+ this.grailMergeWithoutBuffer(arr, pos + left, part, part);
+ left += 2 * part;
+ }
+
+ int rest = len - left;
+ if(rest > part) {
+ this.grailMergeWithoutBuffer(arr, pos + left, part, rest - part);
+ }
+ }
+ }
+
+ protected void grailCommonSort(int[] arr, int pos, int len, int[] buffer, int bufferPos, int bufferLen) {
+ this.grailInsertSorter = new SmartGnomeSort(Delays, Highlights, Reads, Writes);
+
+ if(len <= 16) {
+ this.grailInsertSort(arr, pos, len);
+ return;
+ }
+
+ int blockLen = 1;
+ while(blockLen * blockLen < len) blockLen *= 2;
+
+ int numKeys = (len - 1) / blockLen + 1;
+ int keysFound = this.grailFindKeys(arr, pos, len, numKeys + blockLen);
+
+ boolean bufferEnabled = true;
+
+ if(keysFound < numKeys + blockLen) {
+ if(keysFound < 4) {
+ this.grailLazyStableSort(arr, pos, len);
+ return;
+ }
+ numKeys = blockLen;
+ while(numKeys > keysFound) numKeys /= 2;
+ bufferEnabled = false;
+ blockLen = 0;
+ }
+
+ int dist = blockLen + numKeys;
+ int buildLen = bufferEnabled ? blockLen : numKeys;
+
+ if(bufferEnabled) {
+ this.grailBuildBlocks(arr, pos + dist, len - dist, buildLen, buffer, bufferPos, bufferLen);
+ }
+ else {
+ this.grailBuildBlocks(arr, pos + dist, len - dist, buildLen, null, bufferPos, 0);
+ }
+
+ // 2 * buildLen are built
+ while(len - dist > (buildLen *= 2)) {
+ int regBlockLen = blockLen;
+ boolean buildBufEnabled = bufferEnabled;
+
+ if(!bufferEnabled) {
+ if(numKeys > 4 && numKeys / 8 * numKeys >= buildLen) {
+ regBlockLen = numKeys / 2;
+ buildBufEnabled = true;
+ } else {
+ int calcKeys = 1;
+ int i = buildLen * keysFound / 2;
+ while(calcKeys < numKeys && i != 0) {
+ calcKeys *= 2;
+ i /= 8;
+ }
+ regBlockLen = (2 * buildLen) / calcKeys;
+ }
+ }
+ this.grailCombineBlocks(arr, pos, pos + dist, len - dist, buildLen, regBlockLen, buildBufEnabled,
+ buildBufEnabled && regBlockLen <= bufferLen ? buffer : null, bufferPos);
+
+ Highlights.clearMark(2);
+ }
+
+ this.grailInsertSort(arr, pos, dist);
+ this.grailMergeWithoutBuffer(arr, pos, dist, len - dist);
+ }
+
+ private void grailInPlaceMerge(int[] arr, int pos, int len1, int len2) {
+ if(len1 < 3 || len2 < 3) {
+ this.grailMergeWithoutBuffer(arr, pos, len1, len2);
+ return;
+ }
+
+ int midpoint;
+ if(len1 < len2) midpoint = len1 + len2 / 2;
+ else midpoint = len1 / 2;
+
+ //Left binary search
+ int len1Left, len1Right;
+ len1Left = len1Right = this.grailBinSearch(arr, pos, len1, pos + midpoint, true);
+
+ //Right binary search
+ if(len1Right < len1 && Reads.compare(arr[pos + len1Right], arr[pos + midpoint]) == 0) {
+ len1Right = this.grailBinSearch(arr, pos + len1Left, len1 - len1Left, pos + midpoint, false) + len1Left;
+ }
+
+ int len2Left, len2Right;
+ len2Left = len2Right = this.grailBinSearch(arr, pos + len1, len2, pos + midpoint, true);
+
+ if(len2Right < len2 && Reads.compare(arr[pos + len1 + len2Right], arr[pos + midpoint]) == 0) {
+ len2Right = this.grailBinSearch(arr, pos + len1 + len2Left, len2 - len2Left, pos + midpoint, false) + len2Left;
+ }
+
+ if(len1Left == len1Right) this.grailRotate(arr, pos + len1Right, len1 - len1Right, len2Right);
+ else {
+ this.grailRotate(arr, pos + len1Left, len1 - len1Left, len2Left);
+
+ if(len2Right != len2Left) {
+ this.grailRotate(arr, pos + (len1Right + len2Left), len1 - len1Right, len2Right - len2Left);
+ }
+ }
+
+ this.grailInPlaceMerge(arr, pos + (len1Right + len2Right), len1 - len1Right, len2 - len2Right);
+ this.grailInPlaceMerge(arr, pos, len1Left, len2Left);
+ }
+ protected void grailInPlaceMergeSort(int[] arr, int start, int len) {
+ for(int dist = start + 1; dist < len; dist += 2) {
+ if(Reads.compare(arr[dist - 1], arr[dist]) > 0) this.grailSwap(arr, dist - 1, dist);
+ }
+ for(int part = 2; part < len; part *= 2) {
+ int left = start, right = len - 2 * part;
+
+ while(left <= right) {
+ this.grailInPlaceMerge(arr, left, part, part);
+ left += 2 * part;
+ }
+
+ int rest = len - left;
+ if(rest > part) this.grailInPlaceMerge(arr, left, part, rest - part);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/templates/HeapSorting.java b/src/templates/HeapSorting.java
new file mode 100644
index 00000000..c0f2dfa5
--- /dev/null
+++ b/src/templates/HeapSorting.java
@@ -0,0 +1,68 @@
+package templates;
+
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+Copyright (c) rosettacode.org.
+Permission is granted to copy, distribute and/or modify this document
+under the terms of the GNU Free Documentation License, Version 1.2
+or any later version published by the Free Software Foundation;
+with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
+Texts. A copy of the license is included in the section entitled "GNU
+Free Documentation License".
+ *
+ */
+
+public abstract class HeapSorting extends Sort {
+ protected HeapSorting(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+ }
+
+ private void siftDown(int[] array, int root, int dist, int start, double sleep, boolean isMax) {
+ int compareVal = 0;
+
+ if(isMax) compareVal = -1;
+ else compareVal = 1;
+
+ while (root <= dist / 2) {
+ int leaf = 2 * root;
+ if (leaf < dist && Reads.compare(array[start + leaf - 1], array[start + leaf]) == compareVal) {
+ leaf++;
+ }
+ Highlights.markArray(1, start + root - 1);
+ Highlights.markArray(2, start + leaf - 1);
+ Delays.sleep(sleep);
+ if (Reads.compare(array[start + root - 1], array[start + leaf - 1]) == compareVal) {
+ Writes.swap(array, start + root - 1, start + leaf - 1, 0, true, false);
+ root = leaf;
+ }
+ else break;
+ }
+ }
+
+ private void heapify(int[] arr, int low, int high, double sleep, boolean isMax) {
+ int length = high - low;
+ for (int i = length / 2; i >= 1; i--) {
+ siftDown(arr, i, length, low, sleep, isMax);
+ }
+ }
+
+ // This version of heap sort works for max and min variants, alongside sorting
+ // partial ranges of an array.
+ protected void heapSort(int[] arr, int start, int length, double sleep, boolean isMax) {
+ heapify(arr, start, length, sleep, isMax);
+
+ for (int i = length - start; i > 1; i--) {
+ Writes.swap(arr, start, start + i - 1, sleep, true, false);
+ siftDown(arr, 1, i - 1, start, sleep, isMax);
+ }
+
+ if(!isMax) {
+ Writes.reversal(arr, start, start + length - 1, 1, true, false);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/templates/HolyGrailSorting.java b/src/templates/HolyGrailSorting.java
new file mode 100644
index 00000000..b0678998
--- /dev/null
+++ b/src/templates/HolyGrailSorting.java
@@ -0,0 +1,926 @@
+package templates;
+
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+The MIT License (MIT)
+
+Copyright (c) 2013 Andrey Astrelin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/********* Holy Grail Sorting ****************************/
+/* */
+/* (c) 2013 by Andrey Astrelin */
+/* (c) 2019 modified by John Reynolds (MusicTheorist) */
+/* */
+/* Holy Grail Sort is a variant of Grail Sort, an */
+/* implementation of Block Merge Sort. */
+/* */
+/* Grail Sort is a stable sort that works in O(n log n) */
+/* worst time and uses O(1) extra memory. It splits */
+/* sorted ranges of an array into "blocks" of length */
+/* sqrt(n), combines pairs of ranges via swapping */
+/* blocks, and locally merges blocks together using */
+/* an internal buffer. It maintains stability by */
+/* collecting unique values throughout the array called */
+/* "keys", and tagging blocks with these keys to track */
+/* their movements (think of "key-value pairs"). */
+/* */
+/* Holy Grail Sort improves on these ideas by using */
+/* Binary Insertion Sort instead of Insertion Sort, */
+/* reducing the number of write operations in select */
+/* cases of Grail Sort's "Rotate" method, and */
+/* implementing a bidirectional internal buffer. These */
+/* optimizations result in an algorithm that is at least */
+/* 30% faster than Grail Sort. */
+/* */
+/* Define int / SortComparator */
+/* and then call HolyGrailSort() function */
+/* */
+/* For sorting w/ fixed external buffer (512 items) */
+/* use HolyGrailSortWithBuffer() */
+/* */
+/* For sorting w/ dynamic external buffer (sqrt(length)) */
+/* use HolyGrailSortWithDynBuffer() */
+/* */
+/*********************************************************/
+
+//TODO: Implement grailCheckPairs method to detect presorted array
+
+final class HolyGrailPair {
+ private int leftOverLen;
+ private int leftOverFrag;
+
+ protected HolyGrailPair(int len, int frag) {
+ this.leftOverLen = len;
+ this.leftOverFrag = frag;
+ }
+
+ protected int getLeftOverLen() {
+ return leftOverLen;
+ }
+
+ protected int getLeftOverFrag() {
+ return leftOverFrag;
+ }
+}
+
+public abstract class HolyGrailSorting extends Sort {
+ final private int grailStaticBufferLen = 32; //Buffer length changed due to less numbers in this program being sorted than what Mr. Astrelin used for testing.
+
+ protected HolyGrailSorting(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+ }
+
+ protected int getStaticBuffer() {
+ return this.grailStaticBufferLen;
+ }
+
+ private void grailSwap(int[] arr, int a, int b) {
+ Writes.swap(arr, a, b, 1, true, false);
+ }
+
+ private void grailMultiSwap(int[] arr, int a, int b, int swapsLeft) {
+ while(swapsLeft != 0) {
+ this.grailSwap(arr, a++, b++);
+ swapsLeft--;
+ }
+ }
+
+ // Copy element at pos, shift elements from pos + 1 to pos + len to the left by 1,
+ // and paste copied element at pos + len.
+ private void grailForwardShift(int[] array, int index, int shiftsLeft) {
+ int temp = array[index];
+
+ while(shiftsLeft > 0) {
+ Writes.write(array, index, array[index + 1], 1, true, false);
+ index++;
+ shiftsLeft--;
+ }
+ Writes.write(array, index, temp, 1, true, false);
+ }
+
+ // Copy element at pos + len, shift elements from pos to pos + len - 1 to the right by 1,
+ // and paste copied element at pos.
+ private void grailBackwardShift(int[] array, int index, int shiftsLeft) {
+ int temp = array[index + shiftsLeft];
+
+ while(shiftsLeft > 0) {
+ Writes.write(array, index + shiftsLeft, array[(index - 1) + shiftsLeft], 1, true, false);
+ shiftsLeft--;
+ }
+ Writes.write(array, index, temp, 1, true, false);
+ }
+
+ private void grailRotate(int[] array, int leftIndex, int rightIndex, int target) {
+ while(rightIndex != 0 && target != 0) {
+ if(rightIndex <= target) {
+ if(rightIndex != 1) {
+ this.grailMultiSwap(array, leftIndex, leftIndex + rightIndex, rightIndex);
+ leftIndex += rightIndex;
+ target -= rightIndex;
+ }
+ else {
+ Highlights.clearMark(2);
+ this.grailForwardShift(array, leftIndex, target);
+ target = 0;
+ }
+ }
+ else {
+ if(target != 1) {
+ this.grailMultiSwap(array, leftIndex + (rightIndex - target), leftIndex + rightIndex, target);
+ rightIndex -= target;
+ }
+ else {
+ Highlights.clearMark(2);
+ this.grailBackwardShift(array, leftIndex, rightIndex);
+ rightIndex = 0;
+ }
+ }
+ }
+ }
+
+ // Thanks to https://jeffreystedfast.blogspot.com/2007/02/binary-insertion-sort.html for
+ // a great reference on InsertSort optimizations!!
+
+ //TODO: Fairly certain the use of "pos" is inconsistent between Insert and Bin Search
+ @SuppressWarnings("fallthrough")
+ private void grailInsertSort(int[] arr, int pos, int len) {
+ for(int i = 1; i < len; i++) {
+ int insertPos = grailBinSearch(arr, pos, pos + i, pos + i, false);
+ Highlights.markArray(3, insertPos);
+
+ if(insertPos < i) {
+ int item = arr[pos + i];
+
+ // Used TimSort's Binary Insert as a reference here.
+ int shifts = (pos + i) - insertPos;
+ switch(shifts) {
+ case 2: Writes.write(arr, insertPos + 2, arr[insertPos + 1], 1, true, false);
+ case 1: Writes.write(arr, insertPos + 1, arr[insertPos], 1, true, false);
+ break;
+ default: Writes.reversearraycopy(arr, insertPos, arr, insertPos + 1, shifts, 1, true, false);
+ }
+ Writes.write(arr, insertPos, item, 1, true, false);
+ }
+
+ Highlights.clearMark(3);
+ }
+ }
+
+ // Binary Search is not general purpose, FIX IT
+
+ //boolean argument determines direction
+ private int grailBinSearch(int[] arr, int pos, int len, int keyPos, boolean isLeft) {
+ int left = -1, right = len;
+ while(left < right - 1) {
+ int mid = left + ((right - left) >> 1);
+ if(isLeft) {
+ if(Reads.compare(arr[pos + mid], arr[keyPos]) >= 0) {
+ right = mid;
+ } else left = mid;
+ } else {
+ if(Reads.compare(arr[pos + mid], arr[keyPos]) > 0) {
+ right = mid;
+ } else left = mid;
+ }
+ Highlights.markArray(1, pos + mid);
+ }
+ return right;
+ }
+
+ //TODO: *Somehow* make this more efficient.
+ // cost: 2 * len + numKeys^2 / 2
+ private int grailFindKeys(int[] arr, int pos, int len, int numKeys) {
+ int dist = 1, foundKeys = 1, firstKey = 0; // first key is always here
+
+ while(dist < len && foundKeys < numKeys) {
+ //Binary Search left
+ int loc = this.grailBinSearch(arr, pos + firstKey, foundKeys, pos + dist, true);
+ if(loc == foundKeys || Reads.compare(arr[pos + dist], arr[pos + (firstKey + loc)]) != 0) {
+ this.grailRotate(arr, pos + firstKey, foundKeys, dist - (firstKey + foundKeys));
+ firstKey = dist - foundKeys;
+ this.grailRotate(arr, pos + (firstKey + loc), foundKeys - loc, 1);
+ foundKeys++;
+ }
+ dist++;
+ }
+ this.grailRotate(arr, pos, firstKey, foundKeys);
+
+ Highlights.clearMark(2);
+
+ return foundKeys;
+ }
+
+ private boolean grailCheckOddPairs(int[] arr, int pos, int len) {
+ for(int dist = 2; dist < (len - 2); dist += 2) {
+ Highlights.markArray(1, pos + dist - 1);
+ Highlights.markArray(2, pos + dist);
+ Delays.sleep(1);
+
+ if(Reads.compare(arr[pos + (dist - 1)], arr[pos + dist]) > 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // cost: min(len1, len2)^2 + max(len1, len2)
+ private void grailMergeWithoutBuffer(int[] arr, int pos, int len1, int len2) {
+ if(len1 < len2) {
+ while(len1 != 0) {
+ //Binary Search left
+ int loc = this.grailBinSearch(arr, pos + len1, len2, pos, true);
+ if(loc != 0) {
+ this.grailRotate(arr, pos, len1, loc);
+ pos += loc;
+ len2 -= loc;
+ }
+ if(len2 == 0) break;
+ do {
+ pos++;
+ len1--;
+ } while(len1 != 0 && Reads.compare(arr[pos], arr[pos + len1]) <= 0);
+ }
+ } else {
+ while(len2 != 0) {
+ //Binary Search right
+ int loc = this.grailBinSearch(arr, pos, len1, pos + (len1 + len2 - 1), false);
+ if(loc != len1) {
+ this.grailRotate(arr, pos + loc, len1 - loc, len2);
+ len1 = loc;
+ }
+ if(len1 == 0) break;
+ do {
+ len2--;
+ } while(len2 != 0 && Reads.compare(arr[pos + len1 - 1], arr[pos + len1 + len2 - 1]) <= 0);
+ }
+ }
+ }
+
+ // arr - starting array. arr[0 - regBlockLen..-1] - buffer (if havebuf).
+ // regBlockLen - length of regular blocks. First blockCount blocks are stable sorted by 1st elements and key-coded
+ // keysPos - arrays of keys, in same order as blocks. keysPos < midkey means stream A
+ // aBlockCount are regular blocks from stream A.
+ // lastLen is length of last (irregular) block from stream B, that should go before nblock2 blocks.
+ // lastLen = 0 requires aBlockCount = 0 (no irregular blocks). lastLen > 0, aBlockCount = 0 is possible.
+ private void grailMergeBuffersLeft(int[] arr, int keysPos, int midkey, int pos,
+ int blockCount, int blockLen, boolean havebuf, int aBlockCount,
+ int lastLen) {
+
+ if(blockCount == 0) {
+ int aBlocksLen = aBlockCount * blockLen;
+ if(havebuf) this.grailMergeLeft(arr, pos, aBlocksLen, lastLen, 0 - blockLen);
+ else this.grailMergeWithoutBuffer(arr, pos, aBlocksLen, lastLen);
+ return;
+ }
+
+ int leftOverLen = blockLen;
+ int leftOverFrag = Reads.compare(arr[keysPos], arr[midkey]) < 0 ? 0 : 1;
+ int processIndex = blockLen;
+ int restToProcess;
+
+ for(int keyIndex = 1; keyIndex < blockCount; keyIndex++, processIndex += blockLen) {
+ restToProcess = processIndex - leftOverLen;
+ int nextFrag = Reads.compare(arr[keysPos + keyIndex], arr[midkey]) < 0 ? 0 : 1;
+
+ if(nextFrag == leftOverFrag) {
+ if(havebuf) this.grailMultiSwap(arr, pos + restToProcess - blockLen, pos + restToProcess, leftOverLen);
+ restToProcess = processIndex;
+ leftOverLen = blockLen;
+ } else {
+ if(havebuf) {
+ GrailPair results = this.grailSmartMergeWithBuffer(arr, pos + restToProcess, leftOverLen, leftOverFrag, blockLen);
+ leftOverLen = results.getLeftOverLen();
+ leftOverFrag = results.getLeftOverFrag();
+ } else {
+ GrailPair results = this.grailSmartMergeWithoutBuffer(arr, pos + restToProcess, leftOverLen, leftOverFrag, blockLen);
+ leftOverLen = results.getLeftOverLen();
+ leftOverFrag = results.getLeftOverFrag();
+ }
+ }
+ }
+ restToProcess = processIndex - leftOverLen;
+
+ if(lastLen != 0) {
+ if(leftOverFrag != 0) {
+ if(havebuf) {
+ this.grailMultiSwap(arr, pos + restToProcess - blockLen, pos + restToProcess, leftOverLen);
+ }
+ restToProcess = processIndex;
+ leftOverLen = blockLen * aBlockCount;
+ leftOverFrag = 0;
+ } else {
+ leftOverLen += blockLen * aBlockCount;
+ }
+ if(havebuf) {
+ this.grailMergeLeft(arr, pos + restToProcess, leftOverLen, lastLen, -blockLen);
+ }
+ else {
+ this.grailMergeWithoutBuffer(arr, pos + restToProcess, leftOverLen, lastLen);
+ }
+ } else {
+ if(havebuf) {
+ this.grailMultiSwap(arr, pos + restToProcess, pos + (restToProcess - blockLen), leftOverLen);
+ }
+ }
+ }
+
+ // arr[dist..-1] - buffer, arr[0, leftLen - 1] ++ arr[leftLen, leftLen + rightLen - 1]
+ // -> arr[dist, dist + leftLen + rightLen - 1]
+ private void grailMergeLeft(int[] arr, int pos, int leftLen, int rightLen, int dist) {
+ int left = 0;
+ int right = leftLen;
+
+ rightLen += leftLen;
+
+ while(right < rightLen) {
+ if(left == leftLen || Reads.compare(arr[pos + left], arr[pos + right]) > 0) {
+ this.grailSwap(arr, pos + (dist++), pos + (right++));
+ }
+ else this.grailSwap(arr, pos + (dist++), pos + (left++));
+ Highlights.markArray(3, pos + left);
+ Highlights.markArray(4, pos + right);
+ }
+ Highlights.clearMark(3);
+ Highlights.clearMark(4);
+
+ if(dist != left) this.grailMultiSwap(arr, pos + dist, pos + left, leftLen - left);
+ }
+ private void grailMergeRight(int[] arr, int pos, int leftLen, int rightLen, int dist) {
+ int mergedPos = leftLen + rightLen + dist - 1;
+ int right = leftLen + rightLen - 1;
+ int left = leftLen - 1;
+
+ while(left >= 0) {
+ if(right < leftLen || Reads.compare(arr[pos + left], arr[pos + right]) > 0) {
+ this.grailSwap(arr, pos + (mergedPos--), pos + (left--));
+ }
+ else this.grailSwap(arr, pos + (mergedPos--), pos + (right--));
+ Highlights.markArray(3, pos + left);
+ Highlights.markArray(4, pos + right);
+ }
+ Highlights.clearMark(3);
+ Highlights.clearMark(4);
+
+ if(right != mergedPos) {
+ while(right >= leftLen) this.grailSwap(arr, pos + (mergedPos--), pos + (right--));
+ }
+ }
+
+ //returns the leftover length, then the leftover fragment
+ private GrailPair grailSmartMergeWithoutBuffer(int[] arr, int pos, int leftOverLen, int leftOverFrag, int regBlockLen) {
+ if(regBlockLen == 0) return new GrailPair(leftOverLen, leftOverFrag);
+
+ int len1 = leftOverLen;
+ int len2 = regBlockLen;
+ int typeFrag = 1 - leftOverFrag; //1 if inverted
+
+ if(len1 != 0 && Reads.compare(arr[pos + (len1 - 1)], arr[pos + len1]) - typeFrag >= 0) {
+
+ while(len1 != 0) {
+ int foundLen;
+ if (typeFrag != 0) {
+ //Binary Search left
+ foundLen = this.grailBinSearch(arr, pos + len1, len2, pos, true);
+ } else {
+ //Binary Search right
+ foundLen = this.grailBinSearch(arr, pos + len1, len2, pos, false);
+ }
+ if(foundLen != 0) {
+ this.grailRotate(arr, pos, len1, foundLen);
+ pos += foundLen;
+ len2 -= foundLen;
+ }
+ if(len2 == 0) {
+ return new GrailPair(len1, leftOverFrag);
+ }
+ do {
+ pos++;
+ len1--;
+ } while(len1 != 0 && Reads.compare(arr[pos], arr[pos + len1]) - typeFrag < 0);
+ }
+ }
+ return new GrailPair(len2, typeFrag);
+ }
+
+ //returns the leftover length, then the leftover fragment
+ private GrailPair grailSmartMergeWithBuffer(int[] arr, int pos, int leftOverLen, int leftOverFrag, int blockLen) {
+ int dist = 0 - blockLen, left = 0, right = leftOverLen, leftEnd = right, rightEnd = right + blockLen;
+ int typeFrag = 1 - leftOverFrag; // 1 if inverted
+
+ while(left < leftEnd && right < rightEnd) {
+ if(Reads.compare(arr[pos + left], arr[pos + right]) - typeFrag < 0) {
+ this.grailSwap(arr, pos + (dist++), pos + (left++));
+ }
+ else this.grailSwap(arr, pos + (dist++), pos + (right++));
+ Highlights.markArray(3, pos + left);
+ Highlights.markArray(4, pos + right);
+ }
+ Highlights.clearMark(3);
+ Highlights.clearMark(4);
+
+ int length, fragment = leftOverFrag;
+ if(left < leftEnd) {
+ length = leftEnd - left;
+ while(left < leftEnd) this.grailSwap(arr, pos + (--leftEnd), pos + (--rightEnd));
+ } else {
+ length = rightEnd - right;
+ fragment = typeFrag;
+ }
+ return new GrailPair(length, fragment);
+ }
+
+
+ /***** Sort With Extra Buffer *****/
+
+ //returns the leftover length, then the leftover fragment
+ private GrailPair grailSmartMergeWithXBuf(int[] arr, int pos, int leftOverLen, int leftOverFrag, int blockLen) {
+ int dist = 0 - blockLen, left = 0, right = leftOverLen, leftEnd = right, rightEnd = right + blockLen;
+ int typeFrag = 1 - leftOverFrag; // 1 if inverted
+
+ Highlights.clearMark(2);
+
+ while(left < leftEnd && right < rightEnd) {
+ if(Reads.compare(arr[pos + left], arr[pos + right]) - typeFrag < 0) {
+ Writes.write(arr, pos + dist++, arr[pos + left++], 1, true, false);
+ }
+ else Writes.write(arr, pos + dist++, arr[pos + right++], 1, true, false);
+ Highlights.markArray(2, pos + left);
+ Highlights.markArray(3, pos + right);
+ }
+ Highlights.clearMark(2);
+ Highlights.clearMark(3);
+
+ int length, fragment = leftOverFrag;
+ if(left < leftEnd) {
+ length = leftEnd - left;
+ while(left < leftEnd) Writes.write(arr, pos + --rightEnd, arr[pos + --leftEnd], 1, true, false);
+ } else {
+ length = rightEnd - right;
+ fragment = typeFrag;
+ }
+ return new GrailPair(length, fragment);
+ }
+
+ // arr[dist..-1] - free, arr[0, leftEnd - 1] ++ arr[leftEnd, leftEnd + rightEnd - 1]
+ // -> arr[dist, dist + leftEnd + rightEnd - 1]
+ private void grailMergeLeftWithXBuf(int[] arr, int pos, int leftEnd, int rightEnd, int dist) {
+ int left = 0;
+ int right = leftEnd;
+ rightEnd += leftEnd;
+
+ Highlights.clearMark(2);
+
+ while(right < rightEnd) {
+ if(left == leftEnd || Reads.compare(arr[pos + left], arr[pos + right]) > 0) {
+ Writes.write(arr, pos + dist++, arr[pos + right++], 1, true, false);
+ }
+ else Writes.write(arr, pos + dist++, arr[pos + left++], 1, true, false);
+ Highlights.markArray(2, pos + left);
+ Highlights.markArray(3, pos + right);
+ }
+ Highlights.clearMark(2);
+ Highlights.clearMark(3);
+
+ if(dist != left) {
+ while(left < leftEnd) Writes.write(arr, pos + dist++, arr[pos + left++], 1, true, false);
+ }
+ }
+
+ // arr - starting array. arr[0 - regBlockLen..-1] - buffer (if havebuf).
+ // regBlockLen - length of regular blocks. First blockCount blocks are stable sorted by 1st elements and key-coded
+ // keysPos - where keys are in array, in same order as blocks. keysPos < midkey means stream A
+ // aBlockCount are regular blocks from stream A.
+ // lastLen is length of last (irregular) block from stream B, that should go before aCountBlock blocks.
+ // lastLen = 0 requires aBlockCount = 0 (no irregular blocks). lastLen > 0, aBlockCount = 0 is possible.
+ private void grailMergeBuffersLeftWithXBuf(int[] arr, int keysPos, int midkey, int pos,
+ int blockCount, int regBlockLen, int aBlockCount, int lastLen) {
+
+ Highlights.clearMark(2);
+
+ if(blockCount == 0) {
+ int aBlocksLen = aBlockCount * regBlockLen;
+ this.grailMergeLeftWithXBuf(arr, pos, aBlocksLen, lastLen, 0 - regBlockLen);
+ return;
+ }
+
+ int leftOverLen = regBlockLen;
+ int leftOverFrag = Reads.compare(arr[keysPos], arr[midkey]) < 0 ? 0 : 1;
+ int processIndex = regBlockLen;
+
+ int restToProcess;
+ for(int keyIndex = 1; keyIndex < blockCount; keyIndex++, processIndex += regBlockLen) {
+ restToProcess = processIndex - leftOverLen;
+ int nextFrag = Reads.compare(arr[keysPos + keyIndex], arr[midkey]) < 0 ? 0 : 1;
+
+ if(nextFrag == leftOverFrag) {
+ Writes.arraycopy(arr, pos + restToProcess, arr, pos + restToProcess - regBlockLen, leftOverLen, 1, true, false);
+
+ restToProcess = processIndex;
+ leftOverLen = regBlockLen;
+ } else {
+ GrailPair results = this.grailSmartMergeWithXBuf(arr, pos + restToProcess, leftOverLen, leftOverFrag, regBlockLen);
+ leftOverLen = results.getLeftOverLen();
+ leftOverFrag = results.getLeftOverFrag();
+ }
+ }
+ restToProcess = processIndex - leftOverLen;
+
+ if(lastLen != 0) {
+ if(leftOverFrag != 0) {
+ Writes.arraycopy(arr, pos + restToProcess, arr, pos + restToProcess - regBlockLen, leftOverLen, 1, true, false);
+
+ restToProcess = processIndex;
+ leftOverLen = regBlockLen * aBlockCount;
+ leftOverFrag = 0;
+ } else {
+ leftOverLen += regBlockLen * aBlockCount;
+ }
+ this.grailMergeLeftWithXBuf(arr, pos + restToProcess, leftOverLen, lastLen, 0 - regBlockLen);
+ } else {
+ Writes.arraycopy(arr, pos + restToProcess, arr, pos + restToProcess - regBlockLen, leftOverLen, 1, true, false);
+ }
+ }
+
+ /***** End Sort With Extra Buffer *****/
+
+ // build blocks of length buildLen
+ // input: [-buildLen, -1] elements are buffer
+ // output: first buildLen elements are buffer, blocks 2 * buildLen and last subblock sorted
+
+ // if array is already sorted, return false
+ private boolean grailBuildBlocks(int[] arr, int pos, int len, int buildLen,
+ int[] extbuf, int bufferPos, int extBufLen) {
+
+ int buildBuf = buildLen < extBufLen ? buildLen : extBufLen;
+ while((buildBuf & (buildBuf - 1)) != 0) buildBuf &= buildBuf - 1; // max power or 2 - just in case
+
+ int extraDist, part;
+ if(buildBuf != 0) {
+ Writes.arraycopy(arr, pos - buildBuf, extbuf, bufferPos, buildBuf, 1, true, true);
+
+ for(int dist = 1; dist < len; dist += 2) {
+ extraDist = 0;
+ if(Reads.compare(arr[pos + (dist - 1)], arr[pos + dist]) > 0) extraDist = 1;
+ Writes.write(arr, pos + dist - 3, arr[pos + dist - 1 + extraDist], 1, true, false);
+ Writes.write(arr, pos + dist - 2, arr[pos + dist - extraDist], 1, true, false);
+ }
+ if(len % 2 != 0) Writes.write(arr, pos + len - 3, arr[pos + len - 1], 1, true, false);
+ pos -= 2;
+
+ for(part = 2; part < buildBuf; part *= 2) {
+ int left = 0;
+ int right = len - 2 * part;
+ while(left <= right) {
+ this.grailMergeLeftWithXBuf(arr, pos + left, part, part, 0 - part);
+ left += 2 * part;
+ }
+ int rest = len - left;
+
+ if(rest > part) {
+ this.grailMergeLeftWithXBuf(arr, pos + left, part, rest - part, 0 - part);
+ } else {
+ for(; left < len; left++) Writes.write(arr, pos + left - part, arr[pos + left], 1, true, false);
+ }
+ pos -= part;
+ }
+ Writes.arraycopy(extbuf, bufferPos, arr, pos + len, buildBuf, 1, true, false);
+ }
+ else {
+ boolean evenPairsSorted = true;
+
+ for(int dist = 1; dist < len; dist += 2) {
+ extraDist = 0;
+ if(Reads.compare(arr[pos + (dist - 1)], arr[pos + dist]) > 0) {
+ evenPairsSorted = false;
+ extraDist = 1;
+ }
+ this.grailSwap(arr, pos + (dist - 3), pos + (dist - 1 + extraDist));
+ this.grailSwap(arr, pos + (dist - 2), pos + (dist - extraDist));
+ }
+ if(len % 2 != 0) this.grailSwap(arr, pos + (len - 1), pos + (len - 1) - 2);
+ pos -= 2;
+ part = 2;
+
+ if(evenPairsSorted) {
+ pos += 2;
+ if(this.grailCheckOddPairs(arr, pos, len)) {
+ while((len - 1) - 2 >= 0) {
+ this.grailSwap(arr, pos + (len - 1), pos + ((len--) - 1) - 2);
+ }
+ if(Reads.compare(arr[pos], arr[pos + 1]) > 0) {
+ this.grailSwap(arr, pos, pos + 1);
+ }
+ return false;
+ }
+ pos -= 2;
+ }
+ }
+
+ for(; part < buildLen; part *= 2) {
+ int left = 0;
+ int right = len - 2 * part;
+ while(left <= right) {
+ this.grailMergeLeft(arr, pos + left, part, part, 0 - part);
+ left += 2 * part;
+ }
+ int rest = len - left;
+ if(rest > part) {
+ this.grailMergeLeft(arr, pos + left, part, rest - part, 0 - part);
+ } else {
+ this.grailRotate(arr, pos + left - part, part, rest);
+ }
+ pos -= part;
+ }
+ int restToBuild = len % (2 * buildLen);
+ int leftOverPos = len - restToBuild;
+
+ if(restToBuild <= buildLen) this.grailRotate(arr, pos + leftOverPos, restToBuild, buildLen);
+ else this.grailMergeRight(arr, pos + leftOverPos, buildLen, restToBuild - buildLen, buildLen);
+
+ while(leftOverPos > 0) {
+ leftOverPos -= 2 * buildLen;
+ this.grailMergeRight(arr, pos + leftOverPos, buildLen, buildLen, buildLen);
+ }
+
+ return true;
+ }
+
+ // keys are on the left of arr. Blocks of length buildLen combined. We'll combine them in pairs
+ // buildLen and nkeys are powers of 2. (2 * buildLen / regBlockLen) keys are guaranteed
+ private void grailCombineBlocksForward(int[] arr, int keyPos, int pos, int len, int buildLen,
+ int regBlockLen, boolean havebuf, int[] buffer, int bufferPos) {
+
+ int combineLen = len / (2 * buildLen);
+ int leftOver = len % (2 * buildLen);
+ if(leftOver <= buildLen) {
+ len -= leftOver;
+ leftOver = 0;
+ }
+
+ if(buffer != null) Writes.arraycopy(arr, pos - regBlockLen, buffer, bufferPos, regBlockLen, 1, true, true);
+
+ for(int i = 0; i <= combineLen; i++) {
+ if(i == combineLen && leftOver == 0) break;
+
+ int blockPos = pos + i * 2 * buildLen;
+ int blockCount = (i == combineLen ? leftOver : 2 * buildLen) / regBlockLen;
+
+ this.grailInsertSort(arr, keyPos, blockCount + (i == combineLen ? 1 : 0));
+
+ int midkey = buildLen / regBlockLen;
+
+ for(int index = 1; index < blockCount; index++) {
+ int leftIndex = index - 1;
+
+ for(int rightIndex = index; rightIndex < blockCount; rightIndex++) {
+ int rightComp = Reads.compare(arr[blockPos + leftIndex * regBlockLen],
+ arr[blockPos + rightIndex * regBlockLen]);
+ if(rightComp > 0 || (rightComp == 0 && Reads.compare(arr[keyPos + leftIndex], arr[keyPos + rightIndex]) > 0)) {
+ leftIndex = rightIndex;
+ }
+ }
+ if(leftIndex != index - 1) {
+ this.grailMultiSwap(arr, blockPos + (index - 1) * regBlockLen, blockPos + leftIndex * regBlockLen, regBlockLen);
+ this.grailSwap(arr, keyPos + (index - 1), keyPos + leftIndex);
+ if(midkey == index - 1 || midkey == leftIndex) {
+ midkey ^= (index - 1) ^ leftIndex;
+ }
+ }
+ }
+
+ int aBlockCount = 0;
+ int lastLen = 0;
+ if(i == combineLen) lastLen = leftOver % regBlockLen;
+
+ if(lastLen != 0) {
+ while(aBlockCount < blockCount && Reads.compare(arr[blockPos + blockCount * regBlockLen],
+ arr[blockPos + (blockCount - aBlockCount - 1) * regBlockLen]) < 0) {
+ aBlockCount++;
+ }
+ }
+
+ if(buffer != null) {
+ this.grailMergeBuffersLeftWithXBuf(arr, keyPos, keyPos + midkey, blockPos,
+ blockCount - aBlockCount, regBlockLen, aBlockCount, lastLen);
+ }
+ else this.grailMergeBuffersLeft(arr, keyPos, keyPos + midkey, blockPos,
+ blockCount - aBlockCount, regBlockLen, havebuf, aBlockCount, lastLen);
+ }
+ if(buffer != null) {
+ for(int i = len; --i >= 0;) Writes.write(arr, pos + i, arr[pos + i - regBlockLen], 1, true, false);
+ Writes.arraycopy(buffer, bufferPos, arr, pos - regBlockLen, regBlockLen, 1, true, false);
+ }
+ else if(havebuf) {
+ //if(combineLen != 0) {
+ while(--len >= 0) {
+ int temp = arr[pos + len];
+ arr[pos + len] = arr[pos + len - regBlockLen];
+ arr[pos + len - regBlockLen] = temp;
+ }
+ //}
+ }
+ }
+
+ private void grailCombineBlocksBackward(int[] arr, int keyPos, int pos, int len, int buildLen,
+ int regBlockLen, boolean havebuf, int[] buffer, int bufferPos) {
+
+ int combineLen = len / (2 * buildLen);
+ int leftOver = len % (2 * buildLen);
+ if(leftOver <= buildLen) {
+ len -= leftOver;
+ leftOver = 0;
+ }
+
+ if(buffer != null) Writes.arraycopy(arr, pos - regBlockLen, buffer, bufferPos, regBlockLen, 1, true, true);
+
+ for(int i = combineLen; i >= 0; i--) {
+ if(i == 0 && leftOver == 0) break;
+
+ int blockPos = pos + i * 2 * buildLen;
+ int blockCount = (i == 0 ? leftOver : 2 * buildLen) / regBlockLen;
+
+ this.grailInsertSort(arr, keyPos, blockCount + (i == 0 ? 1 : 0));
+
+ int midkey = buildLen / regBlockLen;
+
+ for(int index = 1; index < blockCount; index++) {
+ int leftIndex = index - 1;
+
+ for(int rightIndex = index; rightIndex < blockCount; rightIndex++) {
+ int rightComp = Reads.compare(arr[blockPos + leftIndex * regBlockLen],
+ arr[blockPos + rightIndex * regBlockLen]);
+ if(rightComp > 0 || (rightComp == 0 && Reads.compare(arr[keyPos + leftIndex], arr[keyPos + rightIndex]) > 0)) {
+ leftIndex = rightIndex;
+ }
+ }
+ if(leftIndex != index - 1) {
+ this.grailMultiSwap(arr, blockPos + (index - 1) * regBlockLen, blockPos + leftIndex * regBlockLen, regBlockLen);
+ this.grailSwap(arr, keyPos + (index - 1), keyPos + leftIndex);
+ if(midkey == index - 1 || midkey == leftIndex) {
+ midkey ^= (index - 1) ^ leftIndex;
+ }
+ }
+ }
+
+ int aBlockCount = 0;
+ int lastLen = 0;
+ if(i == 0) lastLen = leftOver % regBlockLen;
+
+ if(lastLen != 0) {
+ while(aBlockCount < blockCount && Reads.compare(arr[blockPos + blockCount * regBlockLen],
+ arr[blockPos + (blockCount - aBlockCount - 1) * regBlockLen]) < 0) {
+ aBlockCount++;
+ }
+ }
+
+ if(buffer != null) {
+ this.grailMergeBuffersLeftWithXBuf(arr, keyPos, keyPos + midkey, blockPos,
+ blockCount - aBlockCount, regBlockLen, aBlockCount, lastLen);
+ }
+ else this.grailMergeBuffersLeft(arr, keyPos, keyPos + midkey, blockPos,
+ blockCount - aBlockCount, regBlockLen, havebuf, aBlockCount, lastLen);
+ }
+ }
+
+ protected void grailLazyStableSort(int[] arr, int pos, int len) {
+ for(int dist = 1; dist < len; dist += 2) {
+ if(Reads.compare(arr[pos + dist - 1], arr[pos + dist]) > 0) {
+ this.grailSwap(arr, pos + (dist - 1), pos + dist);
+ }
+ Highlights.markArray(3, pos + dist - 1);
+ Highlights.markArray(4, pos + dist);
+ }
+ Highlights.clearMark(3);
+ Highlights.clearMark(4);
+
+ for(int part = 2; part < len; part *= 2) {
+ int left = 0;
+ int right = len - 2 * part;
+
+ while(left <= right) {
+ this.grailMergeWithoutBuffer(arr, pos + left, part, part);
+ left += 2 * part;
+ }
+
+ int rest = len - left;
+ if(rest > part) {
+ this.grailMergeWithoutBuffer(arr, pos + left, part, rest - part);
+ }
+ }
+ }
+
+ protected void grailCommonSort(int[] arr, int pos, int len, int[] buffer, int bufferPos, int bufferLen) {
+ if(len <= 32) {
+ this.grailInsertSort(arr, pos, len);
+ return;
+ }
+
+ int blockLen = 1;
+ while(blockLen * blockLen < len) blockLen *= 2;
+
+ int numKeys = (len - 1) / blockLen + 1;
+ int keysFound = this.grailFindKeys(arr, pos, len, numKeys + blockLen);
+
+ boolean bufferEnabled = true;
+
+ if(keysFound < numKeys + blockLen) {
+ if(keysFound < 4) {
+ this.grailLazyStableSort(arr, pos, len);
+ return;
+ }
+ numKeys = blockLen;
+ while(numKeys > keysFound) numKeys /= 2;
+ bufferEnabled = false;
+ blockLen = 0;
+ }
+
+ int dist = blockLen + numKeys;
+ int buildLen = bufferEnabled ? blockLen : numKeys;
+
+ boolean continueSort = true;
+
+ if(bufferEnabled) {
+ continueSort = this.grailBuildBlocks(arr, pos + dist, len - dist, buildLen, buffer, bufferPos, bufferLen);
+ }
+ else {
+ continueSort = this.grailBuildBlocks(arr, pos + dist, len - dist, buildLen, null, bufferPos, 0);
+ }
+
+ System.out.println(continueSort);
+
+ if(continueSort) {
+ boolean mergeForward = true;
+
+ // 2 * buildLen are built
+ while(len - dist > (buildLen *= 2)) {
+ int regBlockLen = blockLen;
+ boolean buildBufEnabled = bufferEnabled;
+
+ if(!bufferEnabled) {
+ if(numKeys > 4 && numKeys / 8 * numKeys >= buildLen) {
+ regBlockLen = numKeys / 2;
+ buildBufEnabled = true;
+ } else {
+ int calcKeys = 1;
+ int i = buildLen * keysFound / 2;
+ while(calcKeys < numKeys && i != 0) {
+ calcKeys *= 2;
+ i /= 8;
+ }
+ regBlockLen = (2 * buildLen) / calcKeys;
+ }
+ }
+ if(mergeForward) {
+ this.grailCombineBlocksForward(arr, pos, pos + dist, len - dist, buildLen, regBlockLen, buildBufEnabled,
+ buildBufEnabled && regBlockLen <= bufferLen ? buffer : null, bufferPos);
+ }
+ else {
+ this.grailCombineBlocksBackward(arr, pos, pos + dist, len - dist - regBlockLen, buildLen, regBlockLen, buildBufEnabled,
+ buildBufEnabled && regBlockLen <= bufferLen ? buffer : null, bufferPos);
+ }
+
+ //mergeForward = !mergeForward;
+
+ Highlights.clearMark(2);
+ }
+
+ this.grailInsertSort(arr, pos, dist);
+ }
+
+ //TODO: O(n) best-case DOES NOT WORK for key counts LESS THAN (2 * sqrt(n))
+ this.grailMergeWithoutBuffer(arr, pos, dist, len - dist);
+
+ /*
+ this.grailInsertSort(arr, pos, numKeys);
+ this.grailMergeWithoutBuffer(arr, pos, numKeys, len - blockLen);
+
+ BinaryInsertionSort sort = new BinaryInsertionSort(Delays, Highlights, Reads, Writes);
+ sort.customBinaryInsert(arr, pos + len - blockLen, len, 0.1);
+
+ //this.grailInsertSort(arr, pos + len - blockLen, blockLen);
+ this.grailMergeWithoutBuffer(arr, pos, len - blockLen, blockLen);
+ */
+ }
+}
\ No newline at end of file
diff --git a/src/templates/InsertionSorting.java b/src/templates/InsertionSorting.java
new file mode 100644
index 00000000..22897a23
--- /dev/null
+++ b/src/templates/InsertionSorting.java
@@ -0,0 +1,54 @@
+package templates;
+
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+public abstract class InsertionSorting extends Sort {
+ protected InsertionSorting(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+ }
+
+ protected void insertionSort(int[] array, int start, int end, double sleep, boolean auxwrite) {
+ int pos;
+ int current;
+
+ for(int i = start; i < end; i++) {
+ current = array[i];
+ pos = i - 1;
+
+ while(pos >= start && Reads.compare(array[pos], current) > 0){
+ Writes.write(array, pos + 1, array[pos], sleep, true, auxwrite);
+ pos--;
+ }
+ Writes.write(array, pos + 1, current, sleep, true, auxwrite);
+ }
+ }
+}
diff --git a/src/templates/JEnhancedOptionPane.java b/src/templates/JEnhancedOptionPane.java
new file mode 100644
index 00000000..1fddea90
--- /dev/null
+++ b/src/templates/JEnhancedOptionPane.java
@@ -0,0 +1,32 @@
+package templates;
+
+import java.awt.HeadlessException;
+
+import javax.swing.JDialog;
+import javax.swing.JOptionPane;
+import javax.swing.UIManager;
+
+//Many thanks to Freek de Bruijn on StackOverflow for providing a custom JOptionPane.
+//https://stackoverflow.com/questions/14407804/how-to-change-the-default-text-of-buttons-in-joptionpane-showinputdialog?noredirect=1&lq=1
+final public class JEnhancedOptionPane extends JOptionPane {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ public static String showInputDialog(final Object message, final Object[] options) throws HeadlessException {
+ final JOptionPane pane = new JOptionPane(message, QUESTION_MESSAGE,
+ OK_CANCEL_OPTION, null,
+ options, null);
+ pane.setWantsInput(true);
+ pane.setComponentOrientation((getRootFrame()).getComponentOrientation());
+ pane.setMessageType(QUESTION_MESSAGE);
+ pane.selectInitialValue();
+ final String title = UIManager.getString("OptionPane.inputDialogTitle", null);
+ final JDialog dialog = pane.createDialog(null, title);
+ dialog.setVisible(true);
+ dialog.dispose();
+ final Object value = pane.getInputValue();
+ return (value == UNINITIALIZED_VALUE) ? null : (String) value;
+ }
+}
\ No newline at end of file
diff --git a/src/templates/JErrorPane.java b/src/templates/JErrorPane.java
new file mode 100644
index 00000000..5d76f4cd
--- /dev/null
+++ b/src/templates/JErrorPane.java
@@ -0,0 +1,32 @@
+package templates;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import javax.swing.JOptionPane;
+import javax.swing.JTextArea;
+
+final public class JErrorPane extends JOptionPane {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ public volatile static boolean errorMessageActive = false;
+
+ public static void invokeErrorMessage(Exception e) {
+ errorMessageActive = true;
+
+ StringWriter exceptionString = new StringWriter();
+ e.printStackTrace(new PrintWriter(exceptionString));
+ String printException = exceptionString.toString();
+
+ JTextArea error = new JTextArea();
+ error.setText(printException);
+ error.setCaretPosition(0);
+ error.setEditable(false);
+
+ JOptionPane.showMessageDialog(null, error, "Error", JOptionPane.ERROR_MESSAGE);
+ errorMessageActive = false;
+ }
+}
\ No newline at end of file
diff --git a/src/templates/MergeSorting.java b/src/templates/MergeSorting.java
new file mode 100644
index 00000000..38679195
--- /dev/null
+++ b/src/templates/MergeSorting.java
@@ -0,0 +1,103 @@
+package templates;
+
+import sorts.BinaryInsertionSort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+public abstract class MergeSorting extends Sort {
+ private BinaryInsertionSort binaryInserter;
+
+ protected MergeSorting(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+ }
+
+ private void merge(int[] array, int start, int mid, int end, boolean binary) {
+ if(start == mid) return;
+
+ merge(array, start, (mid+start)/2, mid, binary);
+ merge(array, mid, (mid+end)/2, end, binary);
+
+ if(end - start < 32 && binary) {
+ return;
+ }
+ else if(end - start == 32 && binary) {
+ binaryInserter.customBinaryInsert(array, start, end, 0.333);
+ }
+ else {
+ int[] tmp = new int[end - start];
+
+ int low = start;
+ int high = mid;
+
+ for(int nxt = 0; nxt < tmp.length; nxt++){
+ if(low >= mid && high >= end) break;
+
+ Highlights.markArray(1, low);
+ Highlights.markArray(2, high);
+
+ if(low < mid && high >= end){
+ Highlights.clearMark(2);
+ Writes.write(tmp, nxt, array[low++], 1, false, true);
+ }
+ else if(low >= mid && high < end){
+ Highlights.clearMark(1);
+ Writes.write(tmp, nxt, array[high++], 1, false, true);
+ }
+ else if(Reads.compare(array[low], array[high]) <= 0){
+ Writes.write(tmp, nxt, array[low++], 1, false, true);
+ }
+ else{
+ Writes.write(tmp, nxt, array[high++], 1, false, true);
+ }
+ }
+ Highlights.clearMark(2);
+
+ for(int i = 0; i < tmp.length; i++){
+ Writes.write(array, start + i, tmp[i], 1, true, false);
+ }
+ }
+ }
+
+ protected void mergeSort(int[] array, int length, boolean binary) {
+ binaryInserter = new BinaryInsertionSort(this.Delays, this.Highlights, this.Reads, this.Writes);
+
+ if(length < 32 && binary) {
+ binaryInserter.customBinaryInsert(array, 0, length, 0.333);
+ return;
+ }
+
+ int start = 0;
+ int end = length;
+ int mid = start + ((end - start) / 2);
+
+ merge(array, start, mid, end, binary);
+ }
+}
\ No newline at end of file
diff --git a/src/templates/MultipleSortThread.java b/src/templates/MultipleSortThread.java
new file mode 100644
index 00000000..141ac12c
--- /dev/null
+++ b/src/templates/MultipleSortThread.java
@@ -0,0 +1,78 @@
+package templates;
+
+import main.ArrayManager;
+import main.ArrayVisualizer;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Sounds;
+import utils.Timer;
+import utils.Writes;
+
+public abstract class MultipleSortThread {
+ protected ArrayManager ArrayManager;
+ protected ArrayVisualizer ArrayVisualizer;
+ protected Delays Delays;
+ protected Highlights Highlights;
+ protected Reads Reads;
+ protected Writes Writes;
+ protected Sounds Sounds;
+ protected Timer Timer;
+
+ protected volatile int sortCount;
+ protected volatile int sortNumber;
+
+ protected volatile int categoryCount;
+
+ public MultipleSortThread(ArrayVisualizer ArrayVisualizer) {
+ this.ArrayVisualizer = ArrayVisualizer;
+ this.ArrayManager = ArrayVisualizer.getArrayManager();
+ this.Delays = ArrayVisualizer.getDelays();
+ this.Highlights = ArrayVisualizer.getHighlights();
+ this.Reads = ArrayVisualizer.getReads();
+ this.Writes = ArrayVisualizer.getWrites();
+ this.Sounds = ArrayVisualizer.getSounds();
+ this.Timer = ArrayVisualizer.getTimer();
+ }
+
+ protected synchronized void runIndividualSort(Sort sort, int bucketCount, int[] array, int length, double speed) throws InterruptedException {
+ Delays.setSleepRatio(2.5);
+
+ if(length != ArrayVisualizer.getCurrentLength()) {
+ ArrayVisualizer.setCurrentLength(length);
+ }
+
+ ArrayManager.refreshArray(array, ArrayVisualizer.getCurrentLength(), this.ArrayVisualizer);
+
+ ArrayVisualizer.setHeading(sort.getRunAllID() + " (Sort " + this.sortNumber + " of " + this.sortCount + ")");
+ Delays.setSleepRatio(speed);
+
+ Timer.enableRealTimer();
+
+ sort.runSort(array, ArrayVisualizer.getCurrentLength(), bucketCount);
+
+ ArrayVisualizer.endSort();
+ Thread.sleep(1000);
+
+ this.sortNumber++;
+ }
+
+ protected abstract void executeSortList(int[] array) throws Exception;
+ protected abstract void runThread(int[] array, int current, int total, boolean runAllActive) throws Exception;
+
+ public synchronized void reportCategorySorts(int[] array) throws Exception {
+ this.runThread(array, 0, 0, false);
+ }
+
+ public synchronized void reportAllSorts(int[] array, int current, int total) throws Exception {
+ this.runThread(array, current, total, true);
+ }
+
+ public int getSortCount() {
+ return this.sortCount;
+ }
+
+ public int getCategoryCount() {
+ return this.categoryCount;
+ }
+}
\ No newline at end of file
diff --git a/src/templates/PDQSorting.java b/src/templates/PDQSorting.java
new file mode 100644
index 00000000..3083563c
--- /dev/null
+++ b/src/templates/PDQSorting.java
@@ -0,0 +1,526 @@
+package templates;
+
+import sorts.MaxHeapSort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+pdqsort.h - Pattern-defeating quicksort.
+Copyright (c) 2015 Orson Peters
+This software is provided 'as-is', without any express or implied warranty. In no event will the
+authors be held liable for any damages arising from the use of this software.
+Permission is granted to anyone to use this software for any purpose, including commercial
+applications, and to alter it and redistribute it freely, subject to the following restrictions:
+1. The origin of this software must not be misrepresented; you must not claim that you wrote the
+ original software. If you use this software in a product, an acknowledgment in the product
+ documentation would be appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be misrepresented as
+ being the original software.
+3. This notice may not be removed or altered from any source distribution.
+ *
+ */
+
+final class PDQPair {
+ private int pivotPosition;
+ private boolean alreadyPartitioned;
+
+ public PDQPair(int pivotPos, boolean presorted) {
+ this.pivotPosition = pivotPos;
+ this.alreadyPartitioned = presorted;
+ }
+
+ public int getPivotPosition() {
+ return this.pivotPosition;
+ }
+
+ public boolean getPresortBool() {
+ return this.alreadyPartitioned;
+ }
+}
+
+public abstract class PDQSorting extends Sort {
+ private MaxHeapSort heapSorter;
+
+ final private int insertSortThreshold = 24;
+ final private int nintherThreshold = 128;
+ final private int partialInsertSortLimit = 8;
+ final private int blockSize = 64;
+ final private int cachelineSize = 64;
+
+ protected PDQSorting(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+ }
+
+ protected void newHeapSorter(MaxHeapSort heapSort) {
+ heapSorter = heapSort;
+ }
+
+ // Returns floor(log2(n)), assumes n > 0.
+ public static int pdqLog(int n) {
+ int log = 0;
+ while ((n >>= 1) != 0) ++log;
+ return log;
+ }
+
+ // We do not record laps here in order to better estimate Branchless PDQ's running time
+ private int pdqLessThan(int a, int b) {
+ Reads.addComparison();
+ return 1 & (Boolean.hashCode(a < b) >> 1);
+ }
+
+ // Sorts [begin, end) using insertion sort with the given comparison function.
+ private void pdqInsertSort(int[] array, int begin, int end, boolean Branchless) {
+ if (begin == end) return;
+
+ double sleep = 1/3d;
+
+ for (int cur = begin + 1; cur != end; ++cur) {
+ int sift = cur;
+ int siftMinusOne = cur - 1;
+
+ // Compare first so we can avoid 2 moves for an element already positioned correctly.
+ if (Reads.compare(array[sift], array[siftMinusOne]) < 0) {
+ int tmp = array[sift];
+ do {
+ Writes.write(array, sift--, array[siftMinusOne], sleep, true, false);
+ } while (sift != begin && Reads.compare(tmp, array[--siftMinusOne]) < 0);
+
+ Writes.write(array, sift, tmp, sleep, true, false);
+ }
+ }
+ }
+
+ // Sorts [begin, end) using insertion sort with the given comparison function. Assumes
+ // array[begin - 1] is an element smaller than or equal to any element in [begin, end).
+ private void pdqUnguardInsertSort(int[] array, int begin, int end, boolean Branchless) {
+ if (begin == end) return;
+
+ double sleep = 1/3d;
+
+ for (int cur = begin + 1; cur != end; ++cur) {
+ int sift = cur;
+ int siftMinusOne = cur - 1;
+
+ // Compare first so we can avoid 2 moves for an element already positioned correctly.
+ if (Reads.compare(array[sift], array[siftMinusOne]) < 0) {
+ int tmp = array[sift];
+
+ do {
+ Writes.write(array, sift--, array[siftMinusOne], sleep, true, false);
+ } while (Reads.compare(tmp, array[--siftMinusOne]) < 0);
+
+ Writes.write(array, sift, tmp, sleep, true, false);
+ }
+ }
+ }
+
+ // Attempts to use insertion sort on [begin, end). Will return false if more than
+ // partialInsertSortLimit elements were moved, and abort sorting. Otherwise it will
+ // successfully sort and return true.
+ private boolean pdqPartialInsertSort(int[] array, int begin, int end, boolean Branchless) {
+ if (begin == end) return true;
+
+ double sleep = 1/3d;
+
+ int limit = 0;
+ for (int cur = begin + 1; cur != end; ++cur) {
+ if (limit > partialInsertSortLimit) return false;
+
+ int sift = cur;
+ int siftMinusOne = cur - 1;
+
+ // Compare first so we can avoid 2 moves for an element already positioned correctly.
+ if (Reads.compare(array[sift], array[siftMinusOne]) < 0) {
+ int tmp = array[sift];
+
+ do {
+ Writes.write(array, sift--, array[siftMinusOne], sleep, true, false);
+ } while (sift != begin && Reads.compare(tmp, array[--siftMinusOne]) < 0);
+
+ Writes.write(array, sift, tmp, sleep, true, false);
+ limit += cur - sift;
+ }
+ }
+ return true;
+ }
+
+ private void pdqSortTwo(int[] array, int a, int b) {
+ if (Reads.compare(array[b], array[a]) < 0) {
+ Writes.swap(array, a, b, 1, true, false);
+ }
+ Highlights.clearMark(2);
+ }
+
+ // Sorts the elements array[a], array[b] and array[c] using comparison function compare.
+ private void pdqSortThree(int[] array, int a, int b, int c) {
+ this.pdqSortTwo(array, a, b);
+ this.pdqSortTwo(array, b, c);
+ this.pdqSortTwo(array, a, b);
+ }
+
+ // With Branchless PDQSort, in order to better estimate the gains in speed from branchless partioning, we treat the writes to the offset arrays
+ // and specialized less than comparison as negligible, and only record time from elements being swapped into position. By no means is this
+ // exact, yet it is a much closer estimate than what was happening before with recording time for every block being written.
+ private void pdqSwapOffsets(int[] array, int first, int last, int[] leftOffsets, int leftOffsetsPos,
+ int[] rightOffsets, int rightOffsetsPos, int num, boolean useSwaps) {
+ if (useSwaps) {
+ // This case is needed for the descending distribution, where we need
+ // to have proper swapping for pdqsort to remain O(n).
+ for (int i = 0; i < num; ++i) {
+ Writes.swap(array, first + leftOffsets[leftOffsetsPos + i], last - rightOffsets[rightOffsetsPos + i], 1, true, false);
+ }
+ Highlights.clearMark(2);
+ } else if (num > 0) {
+ int left = first + leftOffsets[leftOffsetsPos];
+ int right = last - rightOffsets[rightOffsetsPos];
+ int tmp = array[left];
+ Writes.write(array, left, array[right], 1, true, false);
+ for (int i = 1; i < num; ++i) {
+ left = first + leftOffsets[leftOffsetsPos + i];
+ Writes.write(array, right, array[left], 1, true, false);
+ right = last - rightOffsets[rightOffsetsPos + i];
+ Writes.write(array, left, array[right], 1, true, false);
+ }
+ Writes.write(array, right, tmp, 1, true, false);
+ }
+ }
+
+ // Partitions [begin, end) around pivot array[begin] using comparison function compare. Elements equal
+ // to the pivot are put in the right-hand partition. Returns the position of the pivot after
+ // partitioning and whether the passed sequence already was correctly partitioned. Assumes the
+ // pivot is a median of at least 3 elements and that [begin, end) is at least
+ // insertSortThreshold long. Uses branchless partitioning.
+
+ // We do not record laps throughout the vast majority of this method in order to better estimate Branchless PDQ's running time
+ private PDQPair pdqPartRightBranchless(int[] array, int begin, int end) {
+ // Move pivot into local for speed.
+ int pivot = array[begin];
+ int first = begin;
+ int last = end;
+
+ // Find the first element greater than or equal than the pivot (the median of 3 guarantees
+ // this exists).
+ while (this.pdqLessThan(array[++first], pivot) == 1);
+
+ // Find the first element strictly smaller than the pivot. We have to guard this search if
+ // there was no element before *first.
+ if (first - 1 == begin) while (first < last && this.pdqLessThan(array[--last], pivot) == 0);
+ else while ( this.pdqLessThan(array[--last], pivot) == 0);
+
+ // If the first pair of elements that should be swapped to partition are the same element,
+ // the passed in sequence already was correctly partitioned.
+ boolean alreadyParted = first >= last;
+ if (!alreadyParted) {
+ Writes.swap(array, first, last, 1, true, false);
+ ++first;
+ Highlights.clearMark(2);
+ }
+
+ // The following branchless partitioning is derived from "BlockQuicksort: How Branch
+ // Mispredictions don’t affect Quicksort" by Stefan Edelkamp and Armin Weiss.
+ int[] leftOffsets = new int[blockSize + cachelineSize];
+ int[] rightOffsets = new int [blockSize + cachelineSize];
+ int leftNum, rightNum, leftStart, rightStart;
+ leftNum = rightNum = leftStart = rightStart = 0;
+
+ while (last - first > 2 * blockSize) {
+ // Fill up offset blocks with elements that are on the wrong side.
+ if (leftNum == 0) {
+ leftStart = 0;
+ int it = first;
+ Highlights.clearMark(1);
+ for (int i = 0; i < blockSize;) {
+ leftOffsets[leftNum] = i++; leftNum += Math.abs(this.pdqLessThan(array[it++], pivot) - 1);
+ Writes.changeTempWrites(1); Highlights.markArray(2, it); Delays.sleep(0.5);
+ leftOffsets[leftNum] = i++; leftNum += Math.abs(this.pdqLessThan(array[it++], pivot) - 1);
+ Writes.changeTempWrites(1); Highlights.markArray(2, it); Delays.sleep(0.5);
+ leftOffsets[leftNum] = i++; leftNum += Math.abs(this.pdqLessThan(array[it++], pivot) - 1);
+ Writes.changeTempWrites(1); Highlights.markArray(2, it); Delays.sleep(0.5);
+ leftOffsets[leftNum] = i++; leftNum += Math.abs(this.pdqLessThan(array[it++], pivot) - 1);
+ Writes.changeTempWrites(1); Highlights.markArray(2, it); Delays.sleep(0.5);
+ leftOffsets[leftNum] = i++; leftNum += Math.abs(this.pdqLessThan(array[it++], pivot) - 1);
+ Writes.changeTempWrites(1); Highlights.markArray(2, it); Delays.sleep(0.5);
+ leftOffsets[leftNum] = i++; leftNum += Math.abs(this.pdqLessThan(array[it++], pivot) - 1);
+ Writes.changeTempWrites(1); Highlights.markArray(2, it); Delays.sleep(0.5);
+ leftOffsets[leftNum] = i++; leftNum += Math.abs(this.pdqLessThan(array[it++], pivot) - 1);
+ Writes.changeTempWrites(1); Highlights.markArray(2, it); Delays.sleep(0.5);
+ leftOffsets[leftNum] = i++; leftNum += Math.abs(this.pdqLessThan(array[it++], pivot) - 1);
+ Writes.changeTempWrites(1); Highlights.markArray(2, it); Delays.sleep(0.5);
+ }
+ Highlights.clearMark(2);
+ }
+ if (rightNum == 0) {
+ rightStart = 0;
+ int it = last;
+ Highlights.clearMark(1);
+ for (int i = 0; i < blockSize;) {
+ rightOffsets[rightNum] = ++i; rightNum += this.pdqLessThan(array[--it], pivot);
+ Writes.changeTempWrites(1); Highlights.markArray(2, it); Delays.sleep(0.5);
+ rightOffsets[rightNum] = ++i; rightNum += this.pdqLessThan(array[--it], pivot);
+ Writes.changeTempWrites(1); Highlights.markArray(2, it); Delays.sleep(0.5);
+ rightOffsets[rightNum] = ++i; rightNum += this.pdqLessThan(array[--it], pivot);
+ Writes.changeTempWrites(1); Highlights.markArray(2, it); Delays.sleep(0.5);
+ rightOffsets[rightNum] = ++i; rightNum += this.pdqLessThan(array[--it], pivot);
+ Writes.changeTempWrites(1); Highlights.markArray(2, it); Delays.sleep(0.5);
+ rightOffsets[rightNum] = ++i; rightNum += this.pdqLessThan(array[--it], pivot);
+ Writes.changeTempWrites(1); Highlights.markArray(2, it); Delays.sleep(0.5);
+ rightOffsets[rightNum] = ++i; rightNum += this.pdqLessThan(array[--it], pivot);
+ Writes.changeTempWrites(1); Highlights.markArray(2, it); Delays.sleep(0.5);
+ rightOffsets[rightNum] = ++i; rightNum += this.pdqLessThan(array[--it], pivot);
+ Writes.changeTempWrites(1); Highlights.markArray(2, it); Delays.sleep(0.5);
+ rightOffsets[rightNum] = ++i; rightNum += this.pdqLessThan(array[--it], pivot);
+ Writes.changeTempWrites(1); Highlights.markArray(2, it); Delays.sleep(0.5);
+ }
+ Highlights.clearMark(2);
+ }
+
+ // Swap elements and update block sizes and first/last boundaries.
+ int num = Math.min(leftNum, rightNum);
+ this.pdqSwapOffsets(array, first, last, leftOffsets, leftStart, rightOffsets, rightStart, num, leftNum == rightNum);
+ leftNum -= num; rightNum -= num;
+ leftStart += num; rightStart += num;
+ if (leftNum == 0) first += blockSize;
+ if (rightNum == 0) last -= blockSize;
+ }
+
+ int leftSize = 0, rightSize = 0;
+ int unknownLeft = (last - first) - ((rightNum != 0 || leftNum != 0) ? blockSize : 0);
+ if (rightNum != 0) {
+ // Handle leftover block by assigning the unknown elements to the other block.
+ leftSize = unknownLeft;
+ rightSize = blockSize;
+ } else if (leftNum != 0) {
+ leftSize = blockSize;
+ rightSize = unknownLeft;
+ } else {
+ // No leftover block, split the unknown elements in two blocks.
+ leftSize = unknownLeft / 2;
+ rightSize = unknownLeft - leftSize;
+ }
+
+ // Fill offset buffers if needed.
+ if (unknownLeft != 0 && leftNum == 0) {
+ leftStart = 0;
+ int it = first;
+ Highlights.clearMark(1);
+ for (int i = 0; i < leftSize;) {
+ leftOffsets[leftNum] = i++; leftNum += Math.abs(this.pdqLessThan(array[it++], pivot) - 1);
+ Writes.changeTempWrites(1); Highlights.markArray(2, it); Delays.sleep(0.5);
+ }
+ Highlights.clearMark(2);
+ }
+ if (unknownLeft != 0 && rightNum == 0) {
+ rightStart = 0;
+ int it = last;
+ Highlights.clearMark(1);
+ for (int i = 0; i < rightSize;) {
+ rightOffsets[rightNum] = ++i; rightNum += this.pdqLessThan(array[--it], pivot);
+ Writes.changeTempWrites(1); Highlights.markArray(2, it); Delays.sleep(0.5);
+ }
+ Highlights.clearMark(2);
+ }
+
+ int num = Math.min(leftNum, rightNum);
+ this.pdqSwapOffsets(array, first, last, leftOffsets, leftStart, rightOffsets, rightStart, num, leftNum == rightNum);
+ leftNum -= num; rightNum -= num;
+ leftStart += num; rightStart += num;
+ if (leftNum == 0) first += leftSize;
+ if (rightNum == 0) last -= rightSize;
+
+ int leftOffsetsPos = 0;
+ int rightOffsetsPos = 0;
+
+ // We have now fully identified [first, last)'s proper position. Swap the last elements.
+ if (leftNum != 0) {
+ leftOffsetsPos += leftStart;
+ while (leftNum-- != 0) Writes.swap(array, first + leftOffsets[leftOffsetsPos + leftNum], --last, 1, true, false);
+ Highlights.clearMark(2);
+ first = last;
+ }
+ if (rightNum != 0) {
+ rightOffsetsPos += rightStart;
+ while (rightNum-- != 0) Writes.swap(array, last - rightOffsets[rightOffsetsPos + rightNum], first++, 1, true, false);
+ Highlights.clearMark(2);
+ last = first;
+ }
+
+ // Put the pivot in the right place.
+ int pivotPos = first - 1;
+ Writes.write(array, begin, array[pivotPos], 1, true, false);
+ Writes.write(array, pivotPos, pivot, 1, true, false);
+
+ return new PDQPair(pivotPos, alreadyParted);
+ }
+
+ // Partitions [begin, end) around pivot array[begin] using comparison function compare. Elements equal
+ // to the pivot are put in the right-hand partition. Returns the position of the pivot after
+ // partitioning and whether the passed sequence already was correctly partitioned. Assumes the
+ // pivot is a median of at least 3 elements and that [begin, end) is at least
+ // insertSortThreshold long.
+
+ private PDQPair pdqPartRight(int[] array, int begin, int end) {
+ // Move pivot into local for speed.
+ int pivot = array[begin];
+ int first = begin;
+ int last = end;
+
+ // Find the first element greater than or equal than the pivot (the median of 3 guarantees
+ // this exists).
+ while (Reads.compare(array[++first], pivot) < 0);
+
+ // Find the first element strictly smaller than the pivot. We have to guard this search if
+ // there was no element before *first.
+ if (first - 1 == begin) while (first < last && !(Reads.compare(array[--last], pivot) < 0));
+ else while ( !(Reads.compare(array[--last], pivot) < 0));
+
+ // If the first pair of elements that should be swapped to partition are the same element,
+ // the passed in sequence already was correctly partitioned.
+ boolean alreadyParted = first >= last;
+
+ // Keep swapping pairs of elements that are on the wrong side of the pivot. Previously
+ // swapped pairs guard the searches, which is why the first iteration is special-cased
+ // above.
+ while (first < last) {
+ Writes.swap(array, first, last, 1, true, false);
+ while (Reads.compare(array[++first], pivot) < 0);
+ while (!(Reads.compare(array[--last], pivot) < 0));
+ }
+ Highlights.clearMark(2);
+
+ // Put the pivot in the right place.
+ int pivotPos = first - 1;
+ Writes.write(array, begin, array[pivotPos], 1, true, false);
+ Writes.write(array, pivotPos, pivot, 1, true, false);
+
+ return new PDQPair(pivotPos, alreadyParted);
+ }
+
+ // Similar function to the one above, except elements equal to the pivot are put to the left of
+ // the pivot and it doesn't check or return if the passed sequence already was partitioned.
+ // Since this is rarely used (the many equal case), and in that case pdqsort already has O(n)
+ // performance, no block quicksort is applied here for simplicity.
+
+ private int pdqPartLeft(int[] array, int begin, int end) {
+ // Move pivot into local for speed.
+ int pivot = array[begin];
+ int first = begin;
+ int last = end;
+
+ while (Reads.compare(pivot, array[--last]) < 0);
+
+ if (last + 1 == end) while (first < last && !(Reads.compare(pivot, array[++first]) < 0));
+ else while ( !(Reads.compare(pivot, array[++first]) < 0));
+
+ while (first < last) {
+ Writes.swap(array, first, last, 1, true, false);
+ while (Reads.compare(pivot, array[--last]) < 0);
+ while (!(Reads.compare(pivot, array[++first]) < 0));
+ }
+ Highlights.clearMark(2);
+
+ int pivotPos = last;
+ Writes.write(array, begin, array[pivotPos], 1, true, false);
+ Writes.write(array, pivotPos, pivot, 1, true, false);
+
+ return pivotPos;
+ }
+
+ protected void pdqLoop(int[] array, int begin, int end, boolean Branchless, int badAllowed) {
+ boolean leftmost = true;
+
+ // Use a while loop for tail recursion elimination.
+ while (true) {
+ int size = end - begin;
+
+ // Insertion sort is faster for small arrays.
+ if (size < insertSortThreshold) {
+ if (leftmost) this.pdqInsertSort(array, begin, end, Branchless);
+ else this.pdqUnguardInsertSort(array, begin, end, Branchless);
+ return;
+ }
+
+ // Choose pivot as median of 3 or pseudomedian of 9.
+ int halfSize = size / 2;
+ if (size > nintherThreshold) {
+ this.pdqSortThree(array, begin, begin + halfSize, end - 1);
+ this.pdqSortThree(array, begin + 1, begin + (halfSize - 1), end - 2);
+ this.pdqSortThree(array, begin + 2, begin + (halfSize + 1), end - 3);
+ this.pdqSortThree(array, begin + (halfSize - 1), begin + halfSize, begin + (halfSize + 1));
+ Writes.swap(array, begin, begin + halfSize, 1, true, false);
+ Highlights.clearMark(2);
+ } else this.pdqSortThree(array, begin + halfSize, begin, end - 1);
+
+ // If array[begin - 1] is the end of the right partition of a previous partition operation
+ // there is no element in [begin, end) that is smaller than array[begin - 1]. Then if our
+ // pivot compares equal to array[begin - 1] we change strategy, putting equal elements in
+ // the left partition, greater elements in the right partition. We do not have to
+ // recurse on the left partition, since it's sorted (all equal).
+ if (!leftmost && !(Reads.compare(array[begin - 1], array[begin]) < 0)) {
+ begin = this.pdqPartLeft(array, begin, end) + 1;
+ continue;
+ }
+
+ // Partition and get results.
+ PDQPair partResult =
+ Branchless ? this.pdqPartRightBranchless(array, begin, end)
+ : this.pdqPartRight(array, begin, end);
+
+ int pivotPos = partResult.getPivotPosition();
+ boolean alreadyParted = partResult.getPresortBool();
+
+ // Check for a highly unbalanced partition.
+ int leftSize = pivotPos - begin;
+ int rightSize = end - (pivotPos + 1);
+ boolean highUnbalance = leftSize < size / 8 || rightSize < size / 8;
+
+ // If we got a highly unbalanced partition, we shuffle elements to break many patterns.
+ if (highUnbalance) {
+ // If we had too many bad partitions, switch to heapsort to guarantee O(n log n).
+ if (--badAllowed == 0) {
+ heapSorter.customHeapSort(array, begin, end, 1);
+ return;
+ }
+
+ if (leftSize >= insertSortThreshold) {
+ Writes.swap(array, begin, begin + leftSize / 4, 1, true, false);
+ Writes.swap(array, pivotPos-1, pivotPos - leftSize / 4, 1, true, false);
+
+ if (leftSize > nintherThreshold) {
+ Writes.swap(array, begin+1, begin + (leftSize / 4 + 1), 1, true, false);
+ Writes.swap(array, begin+2, begin + (leftSize / 4 + 2), 1, true, false);
+ Writes.swap(array, pivotPos-2, pivotPos - (leftSize / 4 + 1), 1, true, false);
+ Writes.swap(array, pivotPos-3, pivotPos - (leftSize / 4 + 2), 1, true, false);
+ }
+ }
+
+ if (rightSize >= insertSortThreshold) {
+ Writes.swap(array, pivotPos+1, pivotPos + (1 + rightSize / 4), 1, true, false);
+ Writes.swap(array, end-1, end - rightSize / 4, 1, true, false);
+
+ if (rightSize > nintherThreshold) {
+ Writes.swap(array, pivotPos+2, pivotPos + (2 + rightSize / 4), 1, true, false);
+ Writes.swap(array, pivotPos+3, pivotPos + (3 + rightSize / 4), 1, true, false);
+ Writes.swap(array, end-2, end - (1 + rightSize / 4), 1, true, false);
+ Writes.swap(array, end-3, end - (2 + rightSize / 4), 1, true, false);
+ }
+ }
+ Highlights.clearMark(2);
+ } else {
+ // If we were decently balanced and we tried to sort an already partitioned
+ // sequence, try to use insertion sort.
+ if (alreadyParted && pdqPartialInsertSort(array, begin, pivotPos, Branchless)
+ && pdqPartialInsertSort(array, pivotPos + 1, end, Branchless))
+ return;
+ }
+
+ // Sort the left partition first using recursion and do tail recursion elimination for
+ // the right-hand partition.
+ this.pdqLoop(array, begin, pivotPos, Branchless, badAllowed);
+ begin = pivotPos + 1;
+ leftmost = false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/templates/ShatterSorting.java b/src/templates/ShatterSorting.java
new file mode 100644
index 00000000..e40115f6
--- /dev/null
+++ b/src/templates/ShatterSorting.java
@@ -0,0 +1,100 @@
+package templates;
+
+import java.util.ArrayList;
+
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+public abstract class ShatterSorting extends Sort {
+ protected ShatterSorting(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+ }
+
+ private void shatterPartition(int[] array, int length, int num) {
+ int shatters = (int) Math.ceil(length / (double) num);
+
+ @SuppressWarnings("unchecked")
+ ArrayList[] registers = new ArrayList[shatters];
+
+ for(int i = 0; i < shatters; i++) {
+ registers[i] = new ArrayList<>();
+ }
+
+ for(int i = 0; i < length; i++){
+ registers[array[i] / num].add(array[i]);
+ Highlights.markArray(1, i);
+
+ Writes.mockWrite(length, array[i] / num, array[i], 0.5);
+ }
+
+ Writes.transcribe(array, registers, 0, true, false);
+ }
+
+ protected void shatterSort(int[] array, int length, int num) {
+ int shatters = (int) Math.ceil(length / (double) num);
+
+ shatterPartition(array, length, num);
+
+ int[] tmp = new int[num];
+ for(int i = 0; i < shatters; i++) {
+ for(int j = 0; j < num; j++) {
+ if(i * num + j >= length)
+ Writes.write(tmp, j, -1, 0.5, false, true);
+ else
+ Writes.write(tmp, j, array[i * num + j], 0.5, false, true);
+
+ Highlights.markArray(2, i * num + j);
+ }
+
+ Highlights.clearMark(2);
+
+ for(int j = 0; j < tmp.length; j++) {
+ int tmpj = tmp[j];
+
+ if(i * num + (tmpj % num) >= length || tmpj == -1) {
+ break;
+ }
+
+ Writes.write(array, i * num + (tmpj % num), tmpj, 1, false, false);
+ Highlights.markArray(1, i * num + (tmpj % num));
+ }
+
+ Highlights.clearMark(1);
+ }
+ }
+
+ protected void simpleShatterSort(int[] array, int length, int num, int rate) {
+ for(int i = num; i > 1; i = i / rate) {
+ shatterPartition(array, length, i);
+ }
+ shatterPartition(array, length, 1);
+ }
+}
\ No newline at end of file
diff --git a/src/templates/ShellSorting.java b/src/templates/ShellSorting.java
new file mode 100644
index 00000000..a253620b
--- /dev/null
+++ b/src/templates/ShellSorting.java
@@ -0,0 +1,127 @@
+package templates;
+
+import main.ArrayVisualizer;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+// Shell sort variant retrieved from:
+// https://www.cs.princeton.edu/~rs/talks/shellsort.ps
+
+public abstract class ShellSorting extends Sort {
+ protected ArrayVisualizer ArrayVisualizer; // Instance of ArrayVisualizer used to print
+ // Shellsort's current gap to the statistics
+
+ final protected int[] OriginalGaps = {2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1};
+ final protected int[] PowTwoPlusOneGaps = {2049, 1025, 513, 257, 129, 65, 33, 17, 9, 5, 3, 1};
+ final protected int[] PowTwoMinusOneGaps = {4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1};
+ final protected int[] ThreeSmoothGaps = {3888, 3456, 3072, 2916, 2592, 2304, 2187, 2048, 1944, 1728,
+ 1536, 1458, 1296, 1152, 1024, 972, 864, 768, 729, 648, 576,
+ 512, 486, 432, 384, 324, 288, 256, 243, 216, 192, 162, 144,
+ 128, 108, 96, 81, 72, 64, 54, 48, 36, 32, 27, 24, 18, 16, 12,
+ 9, 8, 6, 4, 3, 2, 1};
+ final protected int[] PowersOfThreeGaps = {3280, 1093, 364, 121, 40, 13, 4, 1};
+ final protected int[] SedgewickIncerpiGaps = {1968, 861, 336, 112, 48, 21, 7, 3, 1};
+ final protected int[] SedgewickGaps = {1073, 281, 77, 23, 8, 1};
+ final protected int[] OddEvenSedgewickGaps = {3905, 2161, 929, 505, 209, 109, 41, 19, 5, 1};
+ final protected int[] GonnetBaezaYatesGaps = {1861, 846, 384, 174, 79, 36, 16, 7, 3, 1};
+ final protected int[] TokudaGaps = {2660, 1182, 525, 233, 103, 46, 20, 9, 4, 1};
+ final protected int[] CiuraGaps = {1750, 701, 301, 132, 57, 23, 10, 4, 1};
+ final protected int[] ExtendedCiuraGaps = {511000, 227111, 100938, 44861, 19938, 8861, 3938, 1750,
+ 701, 301, 132, 57, 23, 10, 4, 1};
+
+ protected ShellSorting(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ super(delayOps, markOps, readOps, writeOps);
+ }
+
+ public void setArrayVisualizer(ArrayVisualizer av) {
+ this.ArrayVisualizer = av;
+ }
+
+ protected void shellSort(ArrayVisualizer ArrayVisualizer, int[] array, int length) {
+ int incs[] = ExtendedCiuraGaps;
+
+ for (int k = 0; k < incs.length; k++) {
+ if(incs == PowersOfThreeGaps) {
+ if(incs[k] < length/3) {
+ for (int h = incs[k], i = h; i < length; i++) {
+ //ArrayVisualizer.setCurrentGap(incs[k]);
+
+ int v = array[i];
+ int j = i;
+
+ Highlights.markArray(1, j);
+ Highlights.markArray(2, j - h);
+
+ while (j >= h && Reads.compare(array[j - h], v) == 1)
+ {
+ Writes.write(array, j, array[j - h], 1, false, false);
+ j -= h;
+
+ Highlights.markArray(1, j);
+
+ if(j - h >= 0) {
+ Highlights.markArray(2, j - h);
+ }
+ else {
+ Highlights.clearMark(2);
+ }
+ }
+ Writes.write(array, j, v, 1, true, false);
+ }
+ }
+ }
+ else {
+ if(incs[k] < length) {
+ for (int h = incs[k], i = h; i < length; i++) {
+ //ArrayVisualizer.setCurrentGap(incs[k]);
+
+ int v = array[i];
+ int j = i;
+
+ Highlights.markArray(1, j);
+ Highlights.markArray(2, j - h);
+
+ while (j >= h && Reads.compare(array[j - h], v) == 1)
+ {
+ Writes.write(array, j, array[j - h], 1, false, false);
+ j -= h;
+
+ Highlights.markArray(1, j);
+
+ if(j - h >= 0) {
+ Highlights.markArray(2, j - h);
+ }
+ else {
+ Highlights.clearMark(2);
+ }
+ }
+ Writes.write(array, j, v, 1, true, false);
+ }
+ }
+ }
+ }
+ }
+
+ protected void quickShellSort(int[] array, int lo, int hi) {
+ int incs[] = {48, 21, 7, 3, 1};
+
+ for (int k = 0; k < incs.length; k++) {
+ for (int h = incs[k], i = h + lo; i < hi; i++)
+ {
+ int v = array[i];
+ int j = i;
+
+ while (j >= h && Reads.compare(array[j-h], v) == 1)
+ {
+ Highlights.markArray(1, j);
+
+ Writes.write(array, j, array[j - h], 1, true, false);
+ j -= h;
+ }
+ Writes.write(array, j, v, 0.5, true, false);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/templates/Sort.java b/src/templates/Sort.java
new file mode 100644
index 00000000..d491c36d
--- /dev/null
+++ b/src/templates/Sort.java
@@ -0,0 +1,108 @@
+package templates;
+
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+public abstract class Sort {
+ private String sortPromptID;
+ private String runAllID;
+ private String reportSortID;
+
+ private String category;
+
+ private boolean comparisonBased;
+ private boolean bucketSort;
+ private boolean radixSort;
+ private boolean unreasonablySlow;
+ private boolean bogoSort;
+ private int unreasonableLimit;
+
+ protected Delays Delays;
+ protected Highlights Highlights;
+ protected Reads Reads;
+ protected Writes Writes;
+
+ protected Sort(Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ this.setSortPromptID("");
+ this.setRunAllID("");
+ this.setReportSortID("");
+ this.setCategory("");
+ this.isComparisonBased(true);
+ this.isBucketSort(false);
+ this.isRadixSort(false); //Used to slightly change base dialogue and to verify that a radix sort is also labeled a bucket sort.
+ this.isUnreasonablySlow(false); //This boolean is true when a sort is incredibly slow even when the user clicks the "Skip Sort" button.
+ this.setUnreasonableLimit(0); //The length of the array such that the user will be warned if this sort is picked and it is marked as unreasonably slow.
+ this.isBogoSort(false); //Used to slightly change warning dialogue and to verify that a Bogo-based sort is marked as unreasonably slow.
+
+ this.Delays = delayOps;
+ this.Highlights = markOps;
+ this.Reads = readOps;
+ this.Writes = writeOps;
+ }
+
+ public String getSortPromptID() {
+ return this.sortPromptID;
+ }
+ public String getRunAllID() {
+ return this.runAllID;
+ }
+ public String getReportSortID() {
+ return this.reportSortID;
+ }
+ public String getCategory() {
+ return this.category;
+ }
+ public boolean comparisonBased() {
+ return this.comparisonBased;
+ }
+ public boolean usesBuckets() {
+ return this.bucketSort;
+ }
+ public boolean radixSort() {
+ return this.radixSort;
+ }
+ public boolean getUnreasonablySlow() {
+ return this.unreasonablySlow;
+ }
+ public int getUnreasonableLimit() {
+ return this.unreasonableLimit;
+ }
+ public boolean bogoSort() {
+ return this.bogoSort;
+ }
+
+ protected void setSortPromptID(String ID) {
+ this.sortPromptID = ID;
+ }
+ protected void setRunAllID(String ID) {
+ this.runAllID = ID;
+ }
+ protected void setReportSortID(String ID) {
+ this.reportSortID = ID;
+ }
+ protected void setCategory(String ID) {
+ this.category = ID;
+ }
+ protected void isComparisonBased(boolean Bool) {
+ this.comparisonBased = Bool;
+ }
+ public void isBucketSort(boolean Bool) {
+ this.bucketSort = Bool;
+ }
+ protected void isRadixSort(boolean Bool) {
+ this.radixSort = Bool;
+ }
+ protected void isUnreasonablySlow(boolean Bool) {
+ this.unreasonablySlow = Bool;
+ }
+ public void setUnreasonableLimit(int number) {
+ this.unreasonableLimit = number;
+ }
+ protected void isBogoSort(boolean Bool) {
+ this.bogoSort = Bool;
+ }
+
+ public abstract void runSort(int[] array, int currentLength, int bucketCount); //bucketCount will be zero for comparison-based sorts
+}
\ No newline at end of file
diff --git a/src/templates/TimSorting.java b/src/templates/TimSorting.java
new file mode 100644
index 00000000..c68a98cf
--- /dev/null
+++ b/src/templates/TimSorting.java
@@ -0,0 +1,933 @@
+package templates;
+
+import javax.swing.text.Highlighter.Highlight;
+
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * A stable, adaptive, iterative mergesort that requires far fewer than
+ * n lg(n) comparisons when running on partially sorted arrays, while
+ * offering performance comparable to a traditional mergesort when run
+ * on random arrays. Like all proper mergesorts, this sort is stable and
+ * runs O(n log n) time (worst case). In the worst case, this sort requires
+ * temporary storage space for n/2 object references; in the best case,
+ * it requires only a small constant amount of space.
+ *
+ * This implementation was adapted from Tim Peters's list sort for
+ * Python, which is described in detail here:
+ *
+ * http://svn.python.org/projects/python/trunk/Objects/listsort.txt
+ *
+ * Tim's C code may be found here:
+ *
+ * http://svn.python.org/projects/python/trunk/Objects/listobject.c
+ *
+ * The underlying techniques are described in this paper (and may have
+ * even earlier origins):
+ *
+ * "Optimistic Sorting and Information Theoretic Complexity"
+ * Peter McIlroy
+ * SODA (Fourth Annual ACM-SIAM Symposium on Discrete Algorithms),
+ * pp 467-474, Austin, Texas, 25-27 January 1993.
+ *
+ * While the API to this class consists solely of static methods, it is
+ * (privately) instantiable; a TimSort instance holds the state of an ongoing
+ * sort, assuming the input array is large enough to warrant the full-blown
+ * TimSort. Small arrays are sorted in place, using a binary insertion sort.
+ *
+ * @author Josh Bloch
+ *
+ * Tailored to ArrayVisualizer by MusicTheorist
+ */
+
+final public class TimSorting {
+ private Delays Delays;
+ private Highlights Highlights;
+ private Reads Reads;
+ private Writes Writes;
+
+ /**
+ * This is the minimum sized sequence that will be merged. Shorter
+ * sequences will be lengthened by calling binarySort. If the entire
+ * array is less than this length, no merges will be performed.
+ *
+ * This constant should be a power of two. It was 64 in Tim Peter's C
+ * implementation, but 32 was empirically determined to work better in
+ * this implementation. In the unlikely event that you set this constant
+ * to be a number that's not a power of two, you'll need to change the
+ * {@link #minRunLength} computation.
+ *
+ * If you decrease this constant, you must change the stackLen
+ * computation in the TimSort constructor, or you risk an
+ * ArrayOutOfBounds exception. See listsort.txt for a discussion
+ * of the minimum stack length required as a function of the length
+ * of the array being sorted and the minimum merge sequence length.
+ */
+ private static final int MIN_MERGE = 32;
+ /**
+ * The array being sorted.
+ */
+ private final int[] a;
+ /**
+ * ArrayVisualizer's current length.
+ */
+ private final int len;
+ /**
+ * When we get into galloping mode, we stay there until both runs win less
+ * often than MIN_GALLOP consecutive times.
+ */
+ private static final int MIN_GALLOP = 7;
+ /**
+ * This controls when we get *into* galloping mode. It is initialized
+ * to MIN_GALLOP. The mergeLo and mergeHi methods nudge it higher for
+ * random data, and lower for highly structured data.
+ */
+ private int minGallop = MIN_GALLOP;
+ /**
+ * Maximum initial size of tmp array, which is used for merging. The array
+ * can grow to accommodate demand.
+ *
+ * Unlike Tim's original C version, we do not allocate this much storage
+ * when sorting smaller arrays. This change was required for performance.
+ */
+ private static final int INITIAL_TMP_STORAGE_LENGTH = 256;
+ /**
+ * Temp storage for merges.
+ */
+ private int[] tmp;
+ /**
+ * A stack of pending runs yet to be merged. Run i starts at
+ * address base[i] and extends for len[i] elements. It's always
+ * true (so long as the indices are in bounds) that:
+ *
+ * runBase[i] + runLen[i] == runBase[i + 1]
+ *
+ * so we could cut the storage for this, but it's a minor amount,
+ * and keeping all the info explicit simplifies the code.
+ */
+ private int stackSize = 0; // Number of pending runs on stack
+ private final int[] runBase;
+ private final int[] runLen;
+
+ public static int getMinRun() {
+ return TimSorting.MIN_MERGE / 2;
+ }
+
+ /**
+ * Creates a TimSort instance to maintain the state of an ongoing sort.
+ *
+ * @param a the array to be sorted
+ */
+ public TimSorting(int[] a, int currentLen, Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps) {
+ this.a = a;
+ this.len = currentLen;
+
+ this.Delays = delayOps;
+ this.Highlights = markOps;
+ this.Reads = readOps;
+ this.Writes = writeOps;
+
+ // Allocate temp storage (which may be increased later if necessary)
+ int[] newArray = new int[this.len < 2 * INITIAL_TMP_STORAGE_LENGTH ?
+ this.len >>> 1 : INITIAL_TMP_STORAGE_LENGTH];
+ this.tmp = newArray;
+ /*
+ * Allocate runs-to-be-merged stack (which cannot be expanded). The
+ * stack length requirements are described in listsort.txt. The C
+ * version always uses the same stack length (85), but this was
+ * measured to be too expensive when sorting "mid-sized" arrays (e.g.,
+ * 100 elements) in Java. Therefore, we use smaller (but sufficiently
+ * large) stack lengths for smaller arrays. The "magic numbers" in the
+ * computation below must be changed if MIN_MERGE is decreased. See
+ * the MIN_MERGE declaration above for more information.
+ */
+ int stackLen = (this.len < 120 ? 5 :
+ this.len < 1542 ? 10 :
+ this.len < 119151 ? 19 : 40);
+ this.runBase = new int[stackLen];
+ this.runLen = new int[stackLen];
+ }
+
+ /*
+ * The next two methods (which are static, one being package private) constitute
+ * the entire API of this class. Each of these methods obeys the contract
+ * of the public method with the same signature in java.util.Arrays.
+ */
+ public static void sort(TimSorting timSort, int[] a, int length) {
+ sort(timSort, a, 0, length);
+ }
+ static void sort(TimSorting timSort, int[] a, int lo, int hi) {
+ TimSorting ts = timSort;
+
+ int nRemaining = hi - lo;
+ // If array is small, do a "mini-TimSort" with no merges
+ if (nRemaining < MIN_MERGE) {
+ int initRunLen = countRunAndMakeAscending(ts, a, lo, hi);
+ binarySort(ts, a, lo, hi, lo + initRunLen);
+ return;
+ }
+ /**
+ * March over the array once, left to right, finding natural runs,
+ * extending short natural runs to minRun elements, and merging runs
+ * to maintain stack invariant.
+ */
+ int minRun = minRunLength(nRemaining);
+ do {
+ // Identify next run
+ int runLen = countRunAndMakeAscending(ts, a, lo, hi);
+
+ // If run is short, extend to min(minRun, nRemaining)
+ if (runLen < minRun) {
+ int force = nRemaining <= minRun ? nRemaining : minRun;
+ binarySort(ts, a, lo, lo + force, lo + runLen);
+ runLen = force;
+ }
+ // Push run onto pending-run stack, and maybe merge
+ ts.pushRun(lo, runLen);
+ ts.mergeCollapse();
+
+ // Advance to find next run
+ lo += runLen;
+ nRemaining -= runLen;
+ } while (nRemaining != 0);
+
+ // Merge all remaining runs to complete sort
+ ts.mergeForceCollapse();
+ }
+
+ /**
+ * Sorts the specified portion of the specified array using a binary
+ * insertion sort. This is the best method for sorting small numbers
+ * of elements. It requires O(n log n) compares, but O(n^2) data
+ * movement (worst case).
+ *
+ * If the initial part of the specified range is already sorted,
+ * this method can take advantage of it: the method assumes that the
+ * elements from index {@code lo}, inclusive, to {@code start},
+ * exclusive are already sorted.
+ *
+ * @param a the array in which a range is to be sorted
+ * @param lo the index of the first element in the range to be sorted
+ * @param hi the index after the last element in the range to be sorted
+ * @param start the index of the first element in the range that is
+ * not already known to be sorted (@code lo <= start <= hi}
+ * @param c comparator to used for the sort
+ */
+
+ // Here, we do not use the Binary Insertion Sort included in ArrayVisualizer, as TimSort
+ // outfits it with a start index and uses the arraycopy method
+
+ @SuppressWarnings("fallthrough")
+ private static void binarySort(TimSorting ts, int[] a, int lo, int hi, int start) {
+ if (start == lo)
+ start++;
+
+ for ( ; start < hi; start++) {
+ int pivot = a[start];
+
+ // Set left (and right) to the index where a[start] (pivot) belongs
+ int left = lo;
+ int right = start;
+
+ /*
+ * Invariants:
+ * pivot >= all in [lo, left).
+ * pivot < all in [right, start).
+ */
+ while (left < right) {
+ // Another good way to prevent integer overflow with left + right!
+ int mid = (left + right) >>> 1;
+
+ if (ts.Reads.compare(pivot, a[mid]) < 0)
+ right = mid;
+ else
+ left = mid + 1;
+ }
+
+ /*
+ * The invariants still hold: pivot >= all in [lo, left) and
+ * pivot < all in [left, start), so pivot belongs at left. Note
+ * that if there are elements equal to pivot, left points to the
+ * first slot after them -- that's why this sort is stable.
+ * Slide elements over to make room for pivot.
+ */
+ int n = start - left; // The number of elements to move
+ // Switch is just an optimization for arraycopy in default case
+ switch(n) {
+ case 2: ts.Writes.write(a, left + 2, a[left + 1], 1, true, false);
+ case 1: ts.Writes.write(a, left + 1, a[left], 1, true, false);
+ break;
+ default: ts.Writes.reversearraycopy(a, left, a, left + 1, n, 1, true, false);
+ }
+ ts.Writes.write(a, left, pivot, 1, true, false);
+ }
+ }
+
+ /**
+ * Returns the length of the run beginning at the specified position in
+ * the specified array and reverses the run if it is descending (ensuring
+ * that the run will always be ascending when the method returns).
+ *
+ * A run is the longest ascending sequence with:
+ *
+ * a[lo] <= a[lo + 1] <= a[lo + 2] <= ...
+ *
+ * or the longest descending sequence with:
+ *
+ * a[lo] > a[lo + 1] > a[lo + 2] > ...
+ *
+ * For its intended use in a stable mergesort, the strictness of the
+ * definition of "descending" is needed so that the call can safely
+ * reverse a descending sequence without violating stability.
+ *
+ * @param a the array in which a run is to be counted and possibly reversed
+ * @param lo index of the first element in the run
+ * @param hi index after the last element that may be contained in the run.
+ It is required that @code{lo < hi}.
+ * @param c the comparator to used for the sort
+ * @return the length of the run beginning at the specified position in
+ * the specified array
+ */
+ private static int countRunAndMakeAscending(TimSorting ts, int[] a, int lo, int hi) {
+ int runHi = lo + 1;
+ if (runHi == hi)
+ return 1;
+
+ // Find end of run, and reverse range if descending
+ if (ts.Reads.compare(a[runHi++], a[lo]) < 0) { // Descending
+ while(runHi < hi && ts.Reads.compare(a[runHi], a[runHi - 1]) < 0) {
+ ts.Highlights.markArray(1, runHi);
+ ts.Delays.sleep(1);
+ runHi++;
+ }
+ reverseRange(ts, a, lo, runHi);
+ } else { // Ascending
+ while (runHi < hi && ts.Reads.compare(a[runHi], a[runHi - 1]) >= 0) {
+ ts.Highlights.markArray(1, runHi);
+ ts.Delays.sleep(1);
+ runHi++;
+ }
+ }
+ return runHi - lo;
+ }
+
+ /**
+ * Reverse the specified range of the specified array.
+ *
+ * @param a the array in which a range is to be reversed
+ * @param lo the index of the first element in the range to be reversed
+ * @param hi the index after the last element in the range to be reversed
+ */
+ private static void reverseRange(TimSorting ts, int[] a, int lo, int hi) {
+ ts.Writes.reversal(a, lo, hi - 1, 1, true, false);
+ ts.Highlights.clearMark(2);
+ }
+
+ /**
+ * Returns the minimum acceptable run length for an array of the specified
+ * length. Natural runs shorter than this will be extended with
+ * {@link #binarySort}.
+ *
+ * Roughly speaking, the computation is:
+ *
+ * If n < MIN_MERGE, return n (it's too small to bother with fancy stuff).
+ * Else if n is an exact power of 2, return MIN_MERGE/2.
+ * Else return an int k, MIN_MERGE/2 <= k <= MIN_MERGE, such that n/k
+ * is close to, but strictly less than, an exact power of 2.
+ *
+ * For the rationale, see listsort.txt.
+ *
+ * @param n the length of the array to be sorted
+ * @return the length of the minimum run to be merged
+ */
+ public static int minRunLength(int n) {
+ int r = 0; // Becomes 1 if any 1 bits are shifted off
+ while (n >= MIN_MERGE) {
+ r |= (n & 1);
+ n >>= 1;
+ }
+ return n + r;
+ }
+
+ /**
+ * Pushes the specified run onto the pending-run stack.
+ *
+ * @param runBase index of the first element in the run
+ * @param runLen the number of elements in the run
+ */
+ private void pushRun(int runBase, int runLen) {
+ this.runBase[this.stackSize] = runBase;
+ this.runLen[this.stackSize] = runLen;
+ this.stackSize++;
+ }
+
+ /**
+ * Examines the stack of runs waiting to be merged and merges adjacent runs
+ * until the stack invariants are reestablished:
+ *
+ * 1. runLen[i - 3] > runLen[i - 2] + runLen[i - 1]
+ * 2. runLen[i - 2] > runLen[i - 1]
+ *
+ * This method is called each time a new run is pushed onto the stack,
+ * so the invariants are guaranteed to hold for i < stackSize upon
+ * entry to the method.
+ *
+ * Thanks to Stijn de Gouw, Jurriaan Rot, Frank S. de Boer,
+ * Richard Bubel and Reiner Hahnle, this is fixed with respect to
+ * the analysis in "On the Worst-Case Complexity of TimSort" by
+ * Nicolas Auger, Vincent Jug, Cyril Nicaud, and Carine Pivoteau.
+ */
+ private void mergeCollapse() {
+ while (this.stackSize > 1) {
+ int n = this.stackSize - 2;
+ if ((n >= 1 && this.runLen[n-1] <= this.runLen[n] + this.runLen[n+1]) ||
+ (n >= 2 && this.runLen[n-2] <= this.runLen[n] + this.runLen[n-1])) {
+ if (this.runLen[n - 1] < this.runLen[n + 1])
+ n--;
+ } else if (this.runLen[n] > this.runLen[n + 1]) {
+ break; // Invariant is established
+ }
+ mergeAt(n);
+ }
+ }
+
+ /**
+ * Merges all runs on the stack until only one remains. This method is
+ * called once, to complete the sort.
+ */
+ private void mergeForceCollapse() {
+ while (this.stackSize > 1) {
+ int n = this.stackSize - 2;
+ if (n > 0 && this.runLen[n - 1] < this.runLen[n + 1])
+ n--;
+ mergeAt(n);
+ }
+ }
+
+ /**
+ * Merges the two runs at stack indices i and i+1. Run i must be
+ * the penultimate or antepenultimate run on the stack. In other words,
+ * i must be equal to stackSize-2 or stackSize-3.
+ *
+ * @param i stack index of the first of the two runs to merge
+ */
+ private void mergeAt(int i) {
+ this.Highlights.clearMark(1);
+ this.Highlights.clearMark(2);
+
+ int base1 = this.runBase[i];
+ int len1 = this.runLen[i];
+ int base2 = this.runBase[i + 1];
+ int len2 = this.runLen[i + 1];
+
+ /*
+ * Record the length of the combined runs; if i is the 3rd-last
+ * run now, also slide over the last run (which isn't involved
+ * in this merge). The current run (i+1) goes away in any case.
+ */
+ this.runLen[i] = len1 + len2;
+ if (i == this.stackSize - 3) {
+ this.runBase[i + 1] = this.runBase[i + 2];
+ this.runLen[i + 1] = this.runLen[i + 2];
+ }
+ this.stackSize--;
+
+ /*
+ * Find where the first element of run2 goes in run1. Prior elements
+ * in run1 can be ignored (because they're already in place).
+ */
+ int k = gallopRight(this, this.a[base2], this.a, base1, len1, 0);
+ base1 += k;
+ len1 -= k;
+ if (len1 == 0)
+ return;
+
+ /*
+ * Find where the last element of run1 goes in run2. Subsequent elements
+ * in run2 can be ignored (because they're already in place).
+ */
+ len2 = gallopLeft(this, this.a[base1 + len1 - 1], this.a, base2, len2, len2 - 1);
+ if (len2 == 0)
+ return;
+
+ // Merge remaining runs, using tmp array with min(len1, len2) elements
+ if (len1 <= len2)
+ mergeLo(this, base1, len1, base2, len2);
+ else
+ mergeHi(this, base1, len1, base2, len2);
+
+ this.Highlights.clearMark(1);
+ this.Highlights.clearMark(2);
+ }
+
+ /**
+ * Locates the position at which to insert the specified key into the
+ * specified sorted range; if the range contains an element equal to key,
+ * returns the index of the leftmost equal element.
+ *
+ * @param key the key whose insertion point to search for
+ * @param a the array in which to search
+ * @param base the index of the first element in the range
+ * @param len the length of the range; must be > 0
+ * @param hint the index at which to begin the search, 0 <= hint < n.
+ * The closer hint is to the result, the faster this method will run.
+ * @param c the comparator used to order the range, and to search
+ * @return the int k, 0 <= k <= n such that a[b + k - 1] < key <= a[b + k],
+ * pretending that a[b - 1] is minus infinity and a[b + n] is infinity.
+ * In other words, key belongs at index b + k; or in other words,
+ * the first k elements of a should precede key, and the last n - k
+ * should follow it.
+ */
+ private static int gallopLeft(TimSorting ts, int key, int[] a, int base, int len, int hint) {
+ int lastOfs = 0;
+ int ofs = 1;
+
+ ts.Highlights.markArray(3, base + hint);
+ ts.Delays.sleep(1);
+
+ if (ts.Reads.compare(key, a[base + hint]) > 0) {
+ // Gallop right until a[base+hint+lastOfs] < key <= a[base+hint+ofs]
+ int maxOfs = len - hint;
+
+ ts.Highlights.markArray(3, base + hint + ofs);
+ ts.Delays.sleep(1);
+
+ while (ofs < maxOfs && ts.Reads.compare(key, a[base + hint + ofs]) > 0) {
+ lastOfs = ofs;
+ ofs = (ofs * 2) + 1;
+ if (ofs <= 0) // int overflow
+ ofs = maxOfs;
+
+ ts.Highlights.markArray(3, base + hint + ofs);
+ ts.Delays.sleep(1);
+ }
+ if (ofs > maxOfs)
+ ofs = maxOfs;
+
+ // Make offsets relative to base
+ lastOfs += hint;
+ ofs += hint;
+ } else { // key <= a[base + hint]
+ // Gallop left until a[base+hint-ofs] < key <= a[base+hint-lastOfs]
+ final int maxOfs = hint + 1;
+
+ ts.Highlights.markArray(3, base + hint - ofs);
+ ts.Delays.sleep(1);
+
+ while (ofs < maxOfs && ts.Reads.compare(key, a[base + hint - ofs]) <= 0) {
+ lastOfs = ofs;
+ ofs = (ofs * 2) + 1;
+ if (ofs <= 0) // int overflow
+ ofs = maxOfs;
+
+ ts.Highlights.markArray(3, base + hint - ofs);
+ ts.Delays.sleep(1);
+ }
+ if (ofs > maxOfs)
+ ofs = maxOfs;
+
+ // Make offsets relative to base
+ int tmp = lastOfs;
+ lastOfs = hint - ofs;
+ ofs = hint - tmp;
+ }
+
+ /*
+ * Now a[base+lastOfs] < key <= a[base+ofs], so key belongs somewhere
+ * to the right of lastOfs but no farther right than ofs. Do a binary
+ * search, with invariant a[base + lastOfs - 1] < key <= a[base + ofs].
+ */
+ lastOfs++;
+ while (lastOfs < ofs) {
+ int m = lastOfs + ((ofs - lastOfs) >>> 1);
+
+ ts.Highlights.markArray(3, base + m);
+ ts.Delays.sleep(1);
+
+ if (ts.Reads.compare(key, a[base + m]) > 0)
+ lastOfs = m + 1; // a[base + m] < key
+ else
+ ofs = m; // key <= a[base + m]
+ }
+ ts.Highlights.clearMark(3);
+ return ofs;
+ }
+ /**
+ * Like gallopLeft, except that if the range contains an element equal to
+ * key, gallopRight returns the index after the rightmost equal element.
+ *
+ * @param key the key whose insertion point to search for
+ * @param a the array in which to search
+ * @param base the index of the first element in the range
+ * @param len the length of the range; must be > 0
+ * @param hint the index at which to begin the search, 0 <= hint < n.
+ * The closer hint is to the result, the faster this method will run.
+ * @param c the comparator used to order the range, and to search
+ * @return the int k, 0 <= k <= n such that a[b + k - 1] <= key < a[b + k]
+ */
+ private static int gallopRight(TimSorting ts, int key, int[] a, int base, int len, int hint) {
+ int ofs = 1;
+ int lastOfs = 0;
+
+ ts.Highlights.markArray(3, base + hint);
+ ts.Delays.sleep(1);
+
+ if (ts.Reads.compare(key, a[base + hint]) < 0) {
+ // Gallop left until a[b+hint - ofs] <= key < a[b+hint - lastOfs]
+ int maxOfs = hint + 1;
+
+ ts.Highlights.markArray(3, base + hint - ofs);
+ ts.Delays.sleep(1);
+
+ while (ofs < maxOfs && ts.Reads.compare(key, a[base + hint - ofs]) < 0) {
+ lastOfs = ofs;
+ ofs = (ofs * 2) + 1;
+ if (ofs <= 0) // int overflow
+ ofs = maxOfs;
+
+ ts.Highlights.markArray(3, base + hint - ofs);
+ ts.Delays.sleep(1);
+ }
+ if (ofs > maxOfs)
+ ofs = maxOfs;
+
+ // Make offsets relative to b
+ int tmp = lastOfs;
+ lastOfs = hint - ofs;
+ ofs = hint - tmp;
+ } else { // a[b + hint] <= key
+ // Gallop right until a[b+hint + lastOfs] <= key < a[b+hint + ofs]
+ int maxOfs = len - hint;
+
+ ts.Highlights.markArray(3, base + hint + ofs);
+ ts.Delays.sleep(1);
+
+ while (ofs < maxOfs && ts.Reads.compare(key, a[base + hint + ofs]) >= 0) {
+ lastOfs = ofs;
+ ofs = (ofs * 2) + 1;
+ if (ofs <= 0) // int overflow
+ ofs = maxOfs;
+
+ ts.Highlights.markArray(3, base + hint + ofs);
+ ts.Delays.sleep(1);
+ }
+ if (ofs > maxOfs)
+ ofs = maxOfs;
+
+ // Make offsets relative to b
+ lastOfs += hint;
+ ofs += hint;
+ }
+
+ /*
+ * Now a[b + lastOfs] <= key < a[b + ofs], so key belongs somewhere to
+ * the right of lastOfs but no farther right than ofs. Do a binary
+ * search, with invariant a[b + lastOfs - 1] <= key < a[b + ofs].
+ */
+ lastOfs++;
+ while (lastOfs < ofs) {
+ int m = lastOfs + ((ofs - lastOfs) >>> 1);
+
+ ts.Highlights.markArray(3, base + m);
+ ts.Delays.sleep(1);
+
+ if (ts.Reads.compare(key, a[base + m]) < 0)
+ ofs = m; // key < a[b + m]
+ else
+ lastOfs = m + 1; // a[b + m] <= key
+ }
+ ts.Highlights.clearMark(3);
+ return ofs;
+ }
+ /**
+ * Merges two adjacent runs in place, in a stable fashion. The first
+ * element of the first run must be greater than the first element of the
+ * second run (a[base1] > a[base2]), and the last element of the first run
+ * (a[base1 + len1-1]) must be greater than all elements of the second run.
+ *
+ * For performance, this method should be called only when len1 <= len2;
+ * its twin, mergeHi should be called if len1 >= len2. (Either method
+ * may be called if len1 == len2.)
+ *
+ * @param base1 index of first element in first run to be merged
+ * @param len1 length of first run to be merged (must be > 0)
+ * @param base2 index of first element in second run to be merged
+ * (must be aBase + aLen)
+ * @param len2 length of second run to be merged (must be > 0)
+ */
+ private void mergeLo(TimSorting ts, int base1, int len1, int base2, int len2) {
+ // Copy first run into temp array
+ int[] a = this.a; // For performance
+ int[] tmp = ensureCapacity(len1);
+ ts.Writes.arraycopy(a, base1, tmp, 0, len1, 1, true, true);
+
+ int cursor1 = 0; // Indexes into tmp array
+ int cursor2 = base2; // Indexes int a
+ int dest = base1; // Indexes int a
+
+ // Move first element of second run and deal with degenerate cases
+ this.Writes.write(a, dest++, a[cursor2++], 1, false, false);
+ this.Highlights.markArray(1, dest);
+ this.Highlights.markArray(2, cursor2);
+ if (--len2 == 0) {
+ ts.Writes.arraycopy(tmp, cursor1, a, dest, len1, 1, true, false);
+ return;
+ }
+ if (len1 == 1) {
+ ts.Writes.arraycopy(a, cursor2, a, dest, len2, 1, true, false);
+ this.Writes.write(a, dest + len2, tmp[cursor1], 1, false, false); // Last elt of run 1 to end of merge
+ this.Highlights.markArray(1, dest + len2);
+ return;
+ }
+
+ int minGallop = this.minGallop; // " " " " "
+ outer:
+ while (true) {
+ int count1 = 0; // Number of times in a row that first run won
+ int count2 = 0; // Number of times in a row that second run won
+ /*
+ * Do the straightforward thing until (if ever) one run starts
+ * winning consistently.
+ */
+ do {
+ if (this.Reads.compare(a[cursor2], tmp[cursor1]) < 0) {
+ this.Writes.write(a, dest++, a[cursor2++], 1, false, false);
+ this.Highlights.markArray(1, dest);
+ this.Highlights.markArray(2, cursor2);
+ count2++;
+ count1 = 0;
+ if (--len2 == 0)
+ break outer;
+ } else {
+ this.Writes.write(a, dest++, tmp[cursor1++], 1, false, false);
+ this.Highlights.markArray(1, dest);
+ count1++;
+ count2 = 0;
+ if (--len1 == 1)
+ break outer;
+ }
+ } while ((count1 | count2) < minGallop);
+
+ /*
+ * One run is winning so consistently that galloping may be a
+ * huge win. So try that, and continue galloping until (if ever)
+ * neither run appears to be winning consistently anymore.
+ */
+ do {
+ count1 = gallopRight(ts, a[cursor2], tmp, cursor1, len1, 0);
+ if (count1 != 0) {
+ ts.Writes.arraycopy(tmp, cursor1, a, dest, count1, 1, true, false);
+ dest += count1;
+ cursor1 += count1;
+ len1 -= count1;
+ if (len1 <= 1) // len1 == 1 || len1 == 0
+ break outer;
+ }
+ this.Writes.write(a, dest++, a[cursor2++], 1, false, false);
+ this.Highlights.markArray(1, dest);
+ this.Highlights.markArray(2, cursor2);
+ if (--len2 == 0)
+ break outer;
+
+ count2 = gallopLeft(ts, tmp[cursor1], a, cursor2, len2, 0);
+ if (count2 != 0) {
+ ts.Writes.arraycopy(a, cursor2, a, dest, count2, 1, true, false);
+ dest += count2;
+ cursor2 += count2;
+ len2 -= count2;
+ if (len2 == 0)
+ break outer;
+ }
+ this.Writes.write(a, dest++, tmp[cursor1++], 1, false, false);
+ this.Highlights.markArray(1, dest);
+ if (--len1 == 1)
+ break outer;
+ minGallop--;
+ } while (count1 >= MIN_GALLOP | count2 >= MIN_GALLOP);
+ if (minGallop < 0)
+ minGallop = 0;
+ minGallop += 2; // Penalize for leaving gallop mode
+ } // End of "outer" loop
+ this.minGallop = minGallop < 1 ? 1 : minGallop; // Write back to field
+
+ if (len1 == 1) {
+ ts.Writes.arraycopy(a, cursor2, a, dest, len2, 1, true, false);
+ this.Writes.write(a, dest + len2, tmp[cursor1], 1, false, false); // Last elt of run 1 to end of merge
+ this.Highlights.markArray(1, dest + len2);
+ } else if (len1 == 0) {
+ throw new IllegalArgumentException(
+ "Comparison method violates its general contract!");
+ } else {
+ ts.Writes.arraycopy(tmp, cursor1, a, dest, len1, 1, true, false);
+ }
+ }
+
+ /**
+ * Like mergeLo, except that this method should be called only if
+ * len1 >= len2; mergeLo should be called if len1 <= len2. (Either method
+ * may be called if len1 == len2.)
+ *
+ * @param base1 index of first element in first run to be merged
+ * @param len1 length of first run to be merged (must be > 0)
+ * @param base2 index of first element in second run to be merged
+ * (must be aBase + aLen)
+ * @param len2 length of second run to be merged (must be > 0)
+ */
+ private void mergeHi(TimSorting ts, int base1, int len1, int base2, int len2) {
+ // Copy second run into temp array
+ int[] a = this.a; // For performance
+ int[] tmp = ensureCapacity(len2);
+ ts.Writes.arraycopy(a, base2, tmp, 0, len2, 1, true, true);
+
+ int cursor1 = base1 + len1 - 1; // Indexes into a
+ int cursor2 = len2 - 1; // Indexes into tmp array
+ int dest = base2 + len2 - 1; // Indexes into a
+
+ // Move last element of first run and deal with degenerate cases
+ this.Writes.write(a, dest--, a[cursor1--], 1, false, false);
+ this.Highlights.markArray(1, dest);
+ this.Highlights.markArray(2, cursor1);
+ if (--len1 == 0) {
+ ts.Writes.reversearraycopy(tmp, 0, a, dest - (len2 - 1), len2, 1, true, false);
+ return;
+ }
+ if (len2 == 1) {
+ dest -= len1;
+ cursor1 -= len1;
+ ts.Writes.reversearraycopy(a, cursor1 + 1, a, dest + 1, len1, 1, true, false);
+ this.Writes.write(a, dest, tmp[cursor2], 1, false, false);
+ this.Highlights.markArray(1, dest);
+ return;
+ }
+
+ int minGallop = this.minGallop; // " " " " "
+ outer:
+ while (true) {
+ int count1 = 0; // Number of times in a row that first run won
+ int count2 = 0; // Number of times in a row that second run won
+
+ /*
+ * Do the straightforward thing until (if ever) one run
+ * appears to win consistently.
+ */
+ do {
+ if (this.Reads.compare(tmp[cursor2], a[cursor1]) < 0) {
+ this.Writes.write(a, dest--, a[cursor1--], 1, false, false);
+ this.Highlights.markArray(1, dest);
+ this.Highlights.markArray(2, cursor1);
+ count1++;
+ count2 = 0;
+ if (--len1 == 0)
+ break outer;
+ } else {
+ this.Writes.write(a, dest--, tmp[cursor2--], 1, false, false);
+ this.Highlights.markArray(1, dest);
+ count2++;
+ count1 = 0;
+ if (--len2 == 1)
+ break outer;
+ }
+ } while ((count1 | count2) < minGallop);
+
+ /*
+ * One run is winning so consistently that galloping may be a
+ * huge win. So try that, and continue galloping until (if ever)
+ * neither run appears to be winning consistently anymore.
+ */
+ do {
+ count1 = len1 - gallopRight(ts, tmp[cursor2], a, base1, len1, len1 - 1);
+ if (count1 != 0) {
+ dest -= count1;
+ cursor1 -= count1;
+ len1 -= count1;
+ ts.Writes.reversearraycopy(a, cursor1 + 1, a, dest + 1, count1, 1, true, false);
+ if (len1 == 0)
+ break outer;
+ }
+ this.Writes.write(a, dest--, tmp[cursor2--], 1, false, false);
+ this.Highlights.markArray(1, dest);
+ if (--len2 == 1)
+ break outer;
+
+ count2 = len2 - gallopLeft(ts, a[cursor1], tmp, 0, len2, len2 - 1);
+ if (count2 != 0) {
+ dest -= count2;
+ cursor2 -= count2;
+ len2 -= count2;
+ ts.Writes.reversearraycopy(tmp, cursor2 + 1, a, dest + 1, count2, 1, true, false);
+ if (len2 <= 1) // len2 == 1 || len2 == 0
+ break outer;
+ }
+ this.Writes.write(a, dest--, a[cursor1--], 1, false, false);
+ this.Highlights.markArray(1, dest);
+ this.Highlights.markArray(2, cursor1);
+ if (--len1 == 0)
+ break outer;
+ minGallop--;
+ } while (count1 >= MIN_GALLOP | count2 >= MIN_GALLOP);
+ if (minGallop < 0)
+ minGallop = 0;
+ minGallop += 2; // Penalize for leaving gallop mode
+ } // End of "outer" loop
+ this.minGallop = minGallop < 1 ? 1 : minGallop; // Write back to field
+
+ if (len2 == 1) {
+ dest -= len1;
+ cursor1 -= len1;
+ ts.Writes.reversearraycopy(a, cursor1 + 1, a, dest + 1, len1, 1, true, false);
+ this.Writes.write(a, dest, tmp[cursor2], 1, false, false); // Move first elt of run2 to front of merge
+ this.Highlights.markArray(1, dest);
+ } else if (len2 == 0) {
+ throw new IllegalArgumentException(
+ "Comparison method violates its general contract!");
+ } else {
+ ts.Writes.reversearraycopy(tmp, 0, a, dest - (len2 - 1), len2, 1, true, false);
+ }
+ }
+ /**
+ * Ensures that the external array tmp has at least the specified
+ * number of elements, increasing its size if necessary. The size
+ * increases exponentially to ensure amortized linear time complexity.
+ *
+ * @param minCapacity the minimum required capacity of the tmp array
+ * @return tmp, whether or not it grew
+ */
+ private int[] ensureCapacity(int minCapacity) {
+ if (this.tmp.length < minCapacity) {
+ // Compute smallest power of 2 > minCapacity
+ int newSize = minCapacity;
+ newSize |= newSize >> 1;
+ newSize |= newSize >> 2;
+ newSize |= newSize >> 4;
+ newSize |= newSize >> 8;
+ newSize |= newSize >> 16;
+ newSize++;
+ if (newSize < 0) // Not bloody likely!
+ newSize = minCapacity;
+ else
+ newSize = Math.min(newSize, this.len >>> 1);
+ int[] newArray = new int[newSize];
+ this.tmp = newArray;
+ }
+ return this.tmp;
+ }
+}
\ No newline at end of file
diff --git a/src/templates/Visual.java b/src/templates/Visual.java
new file mode 100644
index 00000000..a8e35fd6
--- /dev/null
+++ b/src/templates/Visual.java
@@ -0,0 +1,128 @@
+package templates;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics2D;
+
+import main.ArrayVisualizer;
+import utils.Highlights;
+import utils.Renderer;
+
+public abstract class Visual {
+ protected Graphics2D mainRender;
+ protected Graphics2D extraRender;
+
+ public Visual(ArrayVisualizer ArrayVisualizer) {
+ this.updateRender(ArrayVisualizer);
+ }
+
+ public void updateRender(ArrayVisualizer ArrayVisualizer) {
+ this.mainRender = ArrayVisualizer.getMainRender();
+ this.extraRender = ArrayVisualizer.getExtraRender();
+ }
+
+ public static Color getIntColor(int i, int length) {
+ return Color.getHSBColor(((float) i / length), 1.0F, 0.8F);
+ }
+
+ public static void markBar(Graphics2D bar, boolean color, boolean rainbow, boolean analysis) {
+ if(color || rainbow) {
+ /*
+ if(analysis) bar.setColor(Color.WHITE);
+ else bar.setColor(Color.BLACK);
+ */
+ if(analysis) bar.setColor(Color.LIGHT_GRAY);
+ else bar.setColor(Color.WHITE);
+ }
+ else if(analysis) bar.setColor(Color.BLUE);
+ else bar.setColor(Color.RED);
+ }
+ private static void markBarFancy(Graphics2D bar, boolean color, boolean rainbow) {
+ if(!color && !rainbow) bar.setColor(Color.RED);
+ else bar.setColor(Color.BLACK);
+ }
+
+ public static void lineMark(Graphics2D line, double width, boolean color, boolean analysis) {
+ line.setStroke(new BasicStroke((float) (9f * (width / 1280f))));
+ if(color) line.setColor(Color.BLACK);
+ else if(analysis) line.setColor(Color.BLUE);
+ else line.setColor(Color.RED);
+ }
+ //TODO: Change name to markLineFancy
+ public static void lineFancy(Graphics2D line, double width) {
+ line.setColor(Color.GREEN);
+ line.setStroke(new BasicStroke((float) (9f * (width / 1280f))));
+ }
+ //TODO: Change name to clearLine
+ public static void lineClear(Graphics2D line, boolean color, int[] array, int i, int length, double width) {
+ if(color) line.setColor(getIntColor(array[i], length));
+ else line.setColor(Color.WHITE);
+ line.setStroke(new BasicStroke((float) (3f * (width / 1280f))));
+ }
+
+ public static void setRectColor(Graphics2D rect, boolean color, boolean analysis) {
+ if(color) rect.setColor(Color.WHITE);
+ else if(analysis) rect.setColor(Color.BLUE);
+ else rect.setColor(Color.RED);
+ }
+
+ @SuppressWarnings("fallthrough")
+ //The longer the array length, the more bars marked. Makes the visual easier to see when bars are thinner.
+ public static void colorMarkedBars(int logOfLen, int index, Highlights Highlights, Graphics2D mainRender, boolean colorEnabled, boolean rainbowEnabled, boolean analysis) {
+ switch(logOfLen) {
+ case 14: if(Highlights.containsPosition(index - 10)
+ || Highlights.containsPosition(index - 9)
+ || Highlights.containsPosition(index - 8)) markBar(mainRender, colorEnabled, rainbowEnabled, analysis);
+ case 13: if(Highlights.containsPosition(index - 7)
+ || Highlights.containsPosition(index - 6)
+ || Highlights.containsPosition(index - 5)) markBar(mainRender, colorEnabled, rainbowEnabled, analysis);
+ case 12: if(Highlights.containsPosition(index - 4)
+ || Highlights.containsPosition(index - 3)) markBar(mainRender, colorEnabled, rainbowEnabled, analysis);
+ case 11: if(Highlights.containsPosition(index - 2)) markBar(mainRender, colorEnabled, rainbowEnabled, analysis);
+ case 10: if(Highlights.containsPosition(index - 1)) markBar(mainRender, colorEnabled, rainbowEnabled, analysis);
+ default: if(Highlights.containsPosition(index)) markBar(mainRender, colorEnabled, rainbowEnabled, analysis);
+ }
+ }
+
+ @SuppressWarnings("fallthrough")
+ public static void drawFancyFinish(int logOfLen, int index, int position, Graphics2D mainRender, boolean colorEnabled, boolean rainbowEnabled) {
+ switch(logOfLen) {
+ case 14: if(index == position - 13) markBarFancy(mainRender, colorEnabled, rainbowEnabled);
+ case 13: if(index == position - 12) markBarFancy(mainRender, colorEnabled, rainbowEnabled);
+ case 12: if(index == position - 11) markBarFancy(mainRender, colorEnabled, rainbowEnabled);
+ case 11: if(index == position - 10) markBarFancy(mainRender, colorEnabled, rainbowEnabled);
+ case 10: if(index == position - 9) markBarFancy(mainRender, colorEnabled, rainbowEnabled);
+ case 9: if(index == position - 8) markBarFancy(mainRender, colorEnabled, rainbowEnabled);
+ case 8: if(index == position - 7) markBarFancy(mainRender, colorEnabled, rainbowEnabled);
+ case 7: if(index == position - 6) markBarFancy(mainRender, colorEnabled, rainbowEnabled);
+ case 6: if(index == position - 5) markBarFancy(mainRender, colorEnabled, rainbowEnabled);
+ case 5: if(index == position - 4) markBarFancy(mainRender, colorEnabled, rainbowEnabled);
+ case 4: if(index == position - 3) markBarFancy(mainRender, colorEnabled, rainbowEnabled);
+ case 3: if(index == position - 2) markBarFancy(mainRender, colorEnabled, rainbowEnabled);
+ case 2: if(index == position - 1) markBarFancy(mainRender, colorEnabled, rainbowEnabled);
+ default: if(index == position) markBarFancy(mainRender, colorEnabled, rainbowEnabled);
+ }
+ }
+
+ @SuppressWarnings("fallthrough")
+ public static void drawFancyFinishLine(int logOfLen, int index, int position, Graphics2D mainRender, double width, boolean colorEnabled) {
+ switch(logOfLen) {
+ case 14: if(index == position - 13) lineMark(mainRender, width, colorEnabled, false);
+ case 13: if(index == position - 12) lineMark(mainRender, width, colorEnabled, false);
+ case 12: if(index == position - 11) lineMark(mainRender, width, colorEnabled, false);
+ case 11: if(index == position - 10) lineMark(mainRender, width, colorEnabled, false);
+ case 10: if(index == position - 9) lineMark(mainRender, width, colorEnabled, false);
+ case 9: if(index == position - 8) lineMark(mainRender, width, colorEnabled, false);
+ case 8: if(index == position - 7) lineMark(mainRender, width, colorEnabled, false);
+ case 7: if(index == position - 6) lineMark(mainRender, width, colorEnabled, false);
+ case 6: if(index == position - 5) lineMark(mainRender, width, colorEnabled, false);
+ case 5: if(index == position - 4) lineMark(mainRender, width, colorEnabled, false);
+ case 4: if(index == position - 3) lineMark(mainRender, width, colorEnabled, false);
+ case 3: if(index == position - 2) lineMark(mainRender, width, colorEnabled, false);
+ case 2: if(index == position - 1) lineMark(mainRender, width, colorEnabled, false);
+ default: if(index == position) lineMark(mainRender, width, colorEnabled, false);
+ }
+ }
+
+ public abstract void drawVisual(int[] array, ArrayVisualizer ArrayVisualizer, Renderer Renderer, Highlights Highlights);
+}
\ No newline at end of file
diff --git a/src/templates/WikiSorting.java b/src/templates/WikiSorting.java
new file mode 100644
index 00000000..3cedaf41
--- /dev/null
+++ b/src/templates/WikiSorting.java
@@ -0,0 +1,1048 @@
+package templates;
+
+import sorts.InsertionSort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Writes;
+
+/*
+ *
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to
+ *
+ */
+
+// structure to represent ranges within the array
+final class Range {
+ public int start;
+ public int end;
+
+ public Range(int start1, int end1) {
+ this.start = start1;
+ this.end = end1;
+ }
+
+ public Range() {
+ this.start = 0;
+ this.end = 0;
+ }
+
+ void set(int start1, int end1) {
+ this.start = start1;
+ this.end = end1;
+ }
+
+ int length() {
+ return this.end - this.start;
+ }
+}
+
+final class Pull {
+ public int from, to, count;
+ public Range range;
+
+ public Pull() {
+ this.range = new Range(0, 0);
+ }
+
+ void reset() {
+ this.range.set(0, 0);
+ this.from = 0;
+ this.to = 0;
+ this.count = 0;
+ }
+}
+
+// calculate how to scale the index value to the range within the array
+// the bottom-up merge sort only operates on values that are powers of two,
+// so scale down to that power of two, then use a fraction to scale back again
+final class Iterator {
+ public int size, power_of_two;
+ public int numerator, decimal;
+ public int denominator, decimal_step, numerator_step;
+
+ // 63 -> 32, 64 -> 64, etc.
+ // this comes from Hacker's Delight
+ static int FloorPowerOfTwo(int value) {
+ int x = value;
+ x = x | (x >> 1);
+ x = x | (x >> 2);
+ x = x | (x >> 4);
+ x = x | (x >> 8);
+ x = x | (x >> 16);
+ return x - (x >> 1);
+ }
+
+ Iterator(int size2, int min_level) {
+ this.size = size2;
+ this.power_of_two = FloorPowerOfTwo(this.size);
+ this.denominator = this.power_of_two / min_level;
+ this.numerator_step = this.size % this.denominator;
+ this.decimal_step = this.size / this.denominator;
+ begin();
+ }
+
+ void begin() {
+ this.numerator = this.decimal = 0;
+ }
+
+ Range nextRange() {
+ int start = this.decimal;
+
+ this.decimal += this.decimal_step;
+ this.numerator += this.numerator_step;
+ if (this.numerator >= this.denominator) {
+ this.numerator -= this.denominator;
+ this.decimal++;
+ }
+
+ return new Range(start, this.decimal);
+ }
+
+ boolean finished() {
+ return (this.decimal >= this.size);
+ }
+
+ boolean nextLevel() {
+ this.decimal_step += this.decimal_step;
+ this.numerator_step += this.numerator_step;
+ if (this.numerator_step >= this.denominator) {
+ this.numerator_step -= this.denominator;
+ this.decimal_step++;
+ }
+
+ return (this.decimal_step < this.size);
+ }
+
+ int length() {
+ return this.decimal_step;
+ }
+}
+
+final public class WikiSorting {
+ // use a small cache to speed up some of the operations
+ // since the cache size is fixed, it's still O(1) memory!
+ // just keep in mind that making it too small ruins the point (nothing will fit into it),
+ // and making it too large also ruins the point (so much for "low memory"!)
+ private InsertionSort InsertSort;
+
+ private Delays Delays;
+ private Highlights Highlights;
+ private Reads Reads;
+ private Writes Writes;
+
+ private static int cache_size = 0;
+ private int[] cache;
+
+ // note that you can easily modify the above to allocate a dynamically sized cache
+ // good choices for the cache size are:
+
+ // (size + 1)/2 – turns into a full-speed standard merge sort since everything fits into the cache
+ // sqrt((size + 1)/2) + 1 – this will be the size of the A blocks at the largest level of merges,
+ // so a buffer of this size would allow it to skip using internal or in-place merges for anything
+
+ // Original static buffer = 512 – chosen from careful testing as a good balance between fixed-size memory use and run time
+ // ArrayVisualizer static buffer = 32, as the numbers of items we use for visual purposes is relatively small
+
+ // 0 – if the system simply cannot allocate any extra memory whatsoever, no memory works just fine
+
+
+ public WikiSorting(InsertionSort insertionSort, Delays delayOps, Highlights markOps, Reads readOps, Writes writeOps, int cacheChoice) {
+ this.InsertSort = insertionSort;
+
+ this.Delays = delayOps;
+ this.Highlights = markOps;
+ this.Reads = readOps;
+ this.Writes = writeOps;
+
+ cache_size = cacheChoice;
+
+ if(cache_size != 0) this.cache = new int[cache_size];
+ else this.cache = null;
+ }
+
+ public static void sort(WikiSorting WikiSort, int[] array, int currentLen) {
+ WikiSort.Sort(array, currentLen);
+ }
+
+ // toolbox functions used by the sorter
+
+ // find the index of the first value within the range that is equal to array[index]
+ int BinaryFirst(int[] array, int value, Range range) {
+ int start = range.start, end = range.end - 1;
+ while (start < end) {
+ int mid = start + (end - start)/2;
+ if (Reads.compare(array[mid], value) < 0)
+ start = mid + 1;
+ else
+ end = mid;
+ }
+ if (start == range.end - 1 && Reads.compare(array[start], value) < 0) start++;
+ return start;
+ }
+
+ // find the index of the last value within the range that is equal to array[index], plus 1
+ int BinaryLast(int[] array, int value, Range range) {
+ int start = range.start, end = range.end - 1;
+ while (start < end) {
+ int mid = start + (end - start)/2;
+ if (Reads.compare(value, array[mid]) >= 0)
+ start = mid + 1;
+ else
+ end = mid;
+ }
+ if (start == range.end - 1 && Reads.compare(value, array[start]) >= 0) start++;
+ return start;
+ }
+
+ // combine a linear search with a binary search to reduce the number of comparisons in situations
+ // where have some idea as to how many unique values there are and where the next value might be
+ int FindFirstForward(int[] array, int value, Range range, int unique) {
+ if (range.length() == 0) return range.start;
+ int index, skip = Math.max(range.length()/unique, 1);
+
+ for (index = range.start + skip; Reads.compare(array[index - 1], value) < 0; index += skip)
+ if (index >= range.end - skip)
+ return BinaryFirst(array, value, new Range(index, range.end));
+
+ return BinaryFirst(array, value, new Range(index - skip, index));
+ }
+
+ int FindLastForward(int[] array, int value, Range range, int unique) {
+ if (range.length() == 0) return range.start;
+ int index, skip = Math.max(range.length()/unique, 1);
+
+ for (index = range.start + skip; Reads.compare(value, array[index - 1]) >= 0; index += skip)
+ if (index >= range.end - skip)
+ return BinaryLast(array, value, new Range(index, range.end));
+
+ return BinaryLast(array, value, new Range(index - skip, index));
+ }
+
+ int FindFirstBackward(int[] array, int value, Range range, int unique) {
+ if (range.length() == 0) return range.start;
+ int index, skip = Math.max(range.length()/unique, 1);
+
+ for (index = range.end - skip; index > range.start && Reads.compare(array[index - 1], value) >= 0; index -= skip)
+ if (index < range.start + skip)
+ return BinaryFirst(array, value, new Range(range.start, index));
+
+ return BinaryFirst(array, value, new Range(index, index + skip));
+ }
+
+ int FindLastBackward(int[] array, int value, Range range, int unique) {
+ if (range.length() == 0) return range.start;
+ int index, skip = Math.max(range.length()/unique, 1);
+
+ for (index = range.end - skip; index > range.start && Reads.compare(value, array[index - 1]) < 0; index -= skip)
+ if (index < range.start + skip)
+ return BinaryLast(array, value, new Range(range.start, index));
+
+ return BinaryLast(array, value, new Range(index, index + skip));
+ }
+
+ // n^2 sorting algorithm used to sort tiny chunks of the full array
+ void InsertionSort(int[] array, Range range) {
+ InsertSort.customInsertSort(array, range.start, range.end, 0.5, false);
+ }
+
+ // reverse a range of values within the array
+ void Reverse(int[] array, Range range) {
+ Writes.reversal(array, range.start, range.end - 1, 1, true, false);
+ }
+
+ // swap a series of values in the array
+ void BlockSwap(int[] array, int start1, int start2, int block_size) {
+ for (int index = 0; index < block_size; index++) {
+ Writes.swap(array, start1 + index, start2 + index, 1, true, false);
+ }
+ }
+
+ // rotate the values in an array ([0 1 2 3] becomes [1 2 3 0] if we rotate by 1)
+ // this assumes that 0 <= amount <= range.length()
+ void Rotate(int[] array, int amount, Range range, boolean use_cache) {
+ if (range.length() == 0) return;
+
+ int split;
+ if (amount >= 0)
+ split = range.start + amount;
+ else
+ split = range.end + amount;
+
+ Range range1 = new Range(range.start, split);
+ Range range2 = new Range(split, range.end);
+
+ if (use_cache) {
+ // if the smaller of the two ranges fits into the cache, it's *slightly* faster copying it there and shifting the elements over
+ if (range1.length() <= range2.length()) {
+ if (range1.length() <= cache_size) {
+ if (cache != null) {
+ Writes.arraycopy(array, range1.start, cache, 0, range1.length(), 1, true, true);
+ Writes.arraycopy(array, range2.start, array, range1.start, range2.length(), 1, true, false);
+ Writes.arraycopy(cache, 0, array, range1.start + range2.length(), range1.length(), 1, true, false);
+ }
+ return;
+ }
+ } else {
+ if (range2.length() <= cache_size) {
+ if (cache != null) {
+ Writes.reversearraycopy(array, range2.start, cache, 0, range2.length(), 1, true, true);
+ Writes.reversearraycopy(array, range1.start, array, range2.end - range1.length(), range1.length(), 1, true, false);
+ Writes.reversearraycopy(cache, 0, array, range1.start, range2.length(), 1, true, false);
+ }
+ return;
+ }
+ }
+ }
+
+ Reverse(array, range1);
+ Reverse(array, range2);
+ Reverse(array, range);
+ }
+
+ // merge two ranges from one array and save the results into a different array
+ void MergeInto(int[] from, Range A, Range B, int[] into, int at_index, boolean tempwrite) {
+ int A_index = A.start;
+ int B_index = B.start;
+ int insert_index = at_index;
+ int A_last = A.end;
+ int B_last = B.end;
+
+ while (true) {
+ if (Reads.compare(from[B_index], from[A_index]) >= 0) {
+ Writes.write(into, insert_index, from[A_index], 1, false, tempwrite);
+
+ if(tempwrite) Highlights.markArray(1, A_index);
+ else Highlights.markArray(1, insert_index);
+
+ A_index++;
+ insert_index++;
+ if (A_index == A_last) {
+ // copy the remainder of B into the final array
+ Writes.arraycopy(from, B_index, into, insert_index, B_last - B_index, 1, true, tempwrite);
+ break;
+ }
+ } else {
+ Writes.write(into, insert_index, from[B_index], 1, false, tempwrite);
+
+ if(tempwrite) Highlights.markArray(1, B_index);
+ else Highlights.markArray(1, insert_index);
+
+ B_index++;
+ insert_index++;
+ if (B_index == B_last) {
+ // copy the remainder of A into the final array
+ Writes.arraycopy(from, A_index, into, insert_index, A_last - A_index, 1, true, tempwrite);
+ break;
+ }
+ }
+ }
+ }
+
+ // merge operation using an external buffer,
+ void MergeExternal(int[] array, Range A, Range B) {
+ // A fits into the cache, so use that instead of the internal buffer
+ int A_index = 0;
+ int B_index = B.start;
+ int insert_index = A.start;
+ int A_last = A.length();
+ int B_last = B.end;
+
+ if (B.length() > 0 && A.length() > 0) {
+ while (true) {
+ Highlights.markArray(3, A_index);
+ Highlights.markArray(4, B_index);
+ if (Reads.compare(array[B_index], cache[A_index]) >= 0) {
+ Writes.write(array, insert_index, cache[A_index], 1, true, false);
+ A_index++;
+ insert_index++;
+ if (A_index == A_last) break;
+ } else {
+ Writes.write(array, insert_index, array[B_index], 1, true, false);
+ B_index++;
+ insert_index++;
+ if (B_index == B_last) break;
+ }
+ }
+ }
+ Highlights.clearMark(3);
+ Highlights.clearMark(4);
+
+ // copy the remainder of A into the final array
+ if (cache != null) {
+ Writes.arraycopy(cache, A_index, array, insert_index, A_last - A_index, 1, true, false);
+ }
+ }
+
+ // merge operation using an internal buffer
+ void MergeInternal(int[] array, Range A, Range B, Range buffer) {
+ // whenever we find a value to add to the final array, swap it with the value that's already in that spot
+ // when this algorithm is finished, 'buffer' will contain its original contents, but in a different order
+ int A_count = 0, B_count = 0, insert = 0;
+
+ if (B.length() > 0 && A.length() > 0) {
+ while (true) {
+ if (Reads.compare(array[B.start + B_count], array[buffer.start + A_count]) >= 0) {
+ Highlights.markArray(3, buffer.start + A_count);
+ Delays.sleep(1);
+ Writes.swap(array, A.start + insert, buffer.start + A_count, 0, true, false);
+ A_count++;
+ insert++;
+ if (A_count >= A.length()) break;
+ } else {
+ Highlights.markArray(3, B.start + B_count);
+ Delays.sleep(1);
+ Writes.swap(array, A.start + insert, B.start + B_count, 0, true, false);
+ B_count++;
+ insert++;
+ if (B_count >= B.length()) break;
+ }
+ }
+ }
+ Highlights.clearMark(3);
+
+ // swap the remainder of A into the final array
+ BlockSwap(array, buffer.start + A_count, A.start + insert, A.length() - A_count);
+ }
+
+ // merge operation without a buffer
+ void MergeInPlace(int[] array, Range A, Range B) {
+ if (A.length() == 0 || B.length() == 0) return;
+
+ /*
+ this just repeatedly binary searches into B and rotates A into position.
+ the paper suggests using the 'rotation-based Hwang and Lin algorithm' here,
+ but I decided to stick with this because it had better situational performance
+
+ (Hwang and Lin is designed for merging subarrays of very different sizes,
+ but WikiSort almost always uses subarrays that are roughly the same size)
+
+ normally this is incredibly suboptimal, but this function is only called
+ when none of the A or B blocks in any subarray contained 2√A unique values,
+ which places a hard limit on the number of times this will ACTUALLY need
+ to binary search and rotate.
+
+ according to my analysis the worst case is √A rotations performed on √A items
+ once the constant factors are removed, which ends up being O(n)
+
+ again, this is NOT a general-purpose solution – it only works well in this case!
+ kind of like how the O(n^2) insertion sort is used in some places
+ */
+
+ A = new Range(A.start, A.end);
+ B = new Range(B.start, B.end);
+
+ while (true) {
+ // find the first place in B where the first item in A needs to be inserted
+ int mid = BinaryFirst(array, array[A.start], B);
+
+ // rotate A into place
+ int amount = mid - A.end;
+ Rotate(array, -amount, new Range(A.start, mid), true);
+ if (B.end == mid) break;
+
+ // calculate the new A and B ranges
+ B.start = mid;
+ A.set(A.start + amount, B.start);
+ A.start = BinaryLast(array, array[A.start], A);
+ if (A.length() == 0) break;
+ }
+ }
+
+ void NetSwap(int[] array, int[] order, Range range, int x, int y) {
+ int compare = Reads.compare(array[range.start + x], array[range.start + y]);
+ if (compare > 0 || (Reads.compare(order[x], order[y]) == 1 && compare == 0)) {
+ Writes.swap(array, range.start + x, range.start + y, 1, true, false);
+ Writes.swap(order, x, y, 0, false, false);
+ }
+ }
+
+ // bottom-up merge sort combined with an in-place merge algorithm for O(1) memory use
+ void Sort(int[] array, int len) {
+ int size = len;
+
+ // if the array is of size 0, 1, 2, or 3, just sort them like so:
+ if (size < 4) {
+ if (size == 3) {
+ // hard-coded insertion sort
+ if (Reads.compare(array[1], array[0]) < 0) {
+ Writes.swap(array, 0, 1, 1, true, false);
+ }
+ if (Reads.compare(array[2], array[1]) < 0) {
+ Writes.swap(array, 1, 2, 1, true, false);
+ if (Reads.compare(array[1], array[0]) < 0) {
+ Writes.swap(array, 0, 1, 1, true, false);
+ }
+ }
+ } else if (size == 2) {
+ // swap the items if they're out of order
+ if (Reads.compare(array[1], array[0]) < 0) {
+ Writes.swap(array, 0, 1, 1, true, false);
+ }
+ }
+ return;
+ }
+
+ // sort groups of 4-8 items at a time using an unstable sorting network,
+ // but keep track of the original item orders to force it to be stable
+ // http://pages.ripco.net/~jgamble/nw.html
+ Iterator iterator = new Iterator(size, 4);
+ while (!iterator.finished()) {
+ int order[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
+ Range range = iterator.nextRange();
+
+ if (range.length() == 8) {
+ NetSwap(array, order, range, 0, 1); NetSwap(array, order, range, 2, 3);
+ NetSwap(array, order, range, 4, 5); NetSwap(array, order, range, 6, 7);
+ NetSwap(array, order, range, 0, 2); NetSwap(array, order, range, 1, 3);
+ NetSwap(array, order, range, 4, 6); NetSwap(array, order, range, 5, 7);
+ NetSwap(array, order, range, 1, 2); NetSwap(array, order, range, 5, 6);
+ NetSwap(array, order, range, 0, 4); NetSwap(array, order, range, 3, 7);
+ NetSwap(array, order, range, 1, 5); NetSwap(array, order, range, 2, 6);
+ NetSwap(array, order, range, 1, 4); NetSwap(array, order, range, 3, 6);
+ NetSwap(array, order, range, 2, 4); NetSwap(array, order, range, 3, 5);
+ NetSwap(array, order, range, 3, 4);
+
+ } else if (range.length() == 7) {
+ NetSwap(array, order, range, 1, 2); NetSwap(array, order, range, 3, 4); NetSwap(array, order, range, 5, 6);
+ NetSwap(array, order, range, 0, 2); NetSwap(array, order, range, 3, 5); NetSwap(array, order, range, 4, 6);
+ NetSwap(array, order, range, 0, 1); NetSwap(array, order, range, 4, 5); NetSwap(array, order, range, 2, 6);
+ NetSwap(array, order, range, 0, 4); NetSwap(array, order, range, 1, 5);
+ NetSwap(array, order, range, 0, 3); NetSwap(array, order, range, 2, 5);
+ NetSwap(array, order, range, 1, 3); NetSwap(array, order, range, 2, 4);
+ NetSwap(array, order, range, 2, 3);
+
+ } else if (range.length() == 6) {
+ NetSwap(array, order, range, 1, 2); NetSwap(array, order, range, 4, 5);
+ NetSwap(array, order, range, 0, 2); NetSwap(array, order, range, 3, 5);
+ NetSwap(array, order, range, 0, 1); NetSwap(array, order, range, 3, 4); NetSwap(array, order, range, 2, 5);
+ NetSwap(array, order, range, 0, 3); NetSwap(array, order, range, 1, 4);
+ NetSwap(array, order, range, 2, 4); NetSwap(array, order, range, 1, 3);
+ NetSwap(array, order, range, 2, 3);
+
+ } else if (range.length() == 5) {
+ NetSwap(array, order, range, 0, 1); NetSwap(array, order, range, 3, 4);
+ NetSwap(array, order, range, 2, 4);
+ NetSwap(array, order, range, 2, 3); NetSwap(array, order, range, 1, 4);
+ NetSwap(array, order, range, 0, 3);
+ NetSwap(array, order, range, 0, 2); NetSwap(array, order, range, 1, 3);
+ NetSwap(array, order, range, 1, 2);
+
+ } else if (range.length() == 4) {
+ NetSwap(array, order, range, 0, 1); NetSwap(array, order, range, 2, 3);
+ NetSwap(array, order, range, 0, 2); NetSwap(array, order, range, 1, 3);
+ NetSwap(array, order, range, 1, 2);
+ }
+ }
+ if (size < 8) return;
+
+ Highlights.clearMark(2);
+
+ // we need to keep track of a lot of ranges during this sort!
+ Range buffer1 = new Range(), buffer2 = new Range();
+ Range blockA = new Range(), blockB = new Range();
+ Range lastA = new Range(), lastB = new Range();
+ Range firstA = new Range();
+ Range A = new Range(), B = new Range();
+
+ Pull[] pull = new Pull[2];
+ pull[0] = new Pull();
+ pull[1] = new Pull();
+
+ // then merge sort the higher levels, which can be 8-15, 16-31, 32-63, 64-127, etc.
+ while (true) {
+
+ // if every A and B block will fit into the cache, use a special branch specifically for merging with the cache
+ // (we use < rather than <= since the block size might be one more than iterator.length())
+ if (iterator.length() < cache_size) {
+
+ // if four subarrays fit into the cache, it's faster to merge both pairs of subarrays into the cache,
+ // then merge the two merged subarrays from the cache back into the original array
+ if ((iterator.length() + 1) * 4 <= cache_size && iterator.length() * 4 <= size) {
+ iterator.begin();
+ while (!iterator.finished()) {
+ // merge A1 and B1 into the cache
+ Range A1 = iterator.nextRange();
+ Range B1 = iterator.nextRange();
+ Range A2 = iterator.nextRange();
+ Range B2 = iterator.nextRange();
+
+ if (Reads.compare(array[B1.end - 1], array[A1.start]) < 0) {
+ // the two ranges are in reverse order, so copy them in reverse order into the cache
+ Writes.arraycopy(array, A1.start, cache, B1.length(), A1.length(), 1, true, true);
+ Writes.arraycopy(array, B1.start, cache, 0, B1.length(), 1, true, true);
+ } else if (Reads.compare(array[B1.start], array[A1.end - 1]) < 0) {
+ // these two ranges weren't already in order, so merge them into the cache
+ MergeInto(array, A1, B1, cache, 0, true);
+ } else {
+ // if A1, B1, A2, and B2 are all in order, skip doing anything else
+ if (Reads.compare(array[B2.start], array[A2.end - 1]) >= 0 && Reads.compare(array[A2.start], array[B1.end - 1]) >= 0) continue;
+
+ // copy A1 and B1 into the cache in the same order
+ Writes.arraycopy(array, A1.start, cache, 0, A1.length(), 1, true, true);
+ Writes.arraycopy(array, B1.start, cache, A1.length(), B1.length(), 1, true, true);
+ }
+ A1.set(A1.start, B1.end);
+
+ // merge A2 and B2 into the cache
+ if (Reads.compare(array[B2.end - 1], array[A2.start]) < 0) {
+ // the two ranges are in reverse order, so copy them in reverse order into the cache
+ Writes.arraycopy(array, A2.start, cache, A1.length() + B2.length(), A2.length(), 1, true, true);
+ Writes.arraycopy(array, B2.start, cache, A1.length(), B2.length(), 1, true, true);
+ } else if (Reads.compare(array[B2.start], array[A2.end - 1]) < 0) {
+ // these two ranges weren't already in order, so merge them into the cache
+ MergeInto(array, A2, B2, cache, A1.length(), true);
+ } else {
+ // copy A2 and B2 into the cache in the same order
+ Writes.arraycopy(array, A2.start, cache, A1.length(), A2.length(), 1, true, true);
+ Writes.arraycopy(array, B2.start, cache, A1.length() + A2.length(), B2.length(), 1, true, true);
+ }
+ A2.set(A2.start, B2.end);
+
+ // merge A1 and A2 from the cache into the array
+ Range A3 = new Range(0, A1.length());
+ Range B3 = new Range(A1.length(), A1.length() + A2.length());
+
+ if (Reads.compare(cache[B3.end - 1], cache[A3.start]) < 0) {
+ // the two ranges are in reverse order, so copy them in reverse order into the cache
+ Writes.arraycopy(cache, A3.start, array, A1.start + A2.length(), A3.length(), 1, true, false);
+ Writes.arraycopy(cache, B3.start, array, A1.start, B3.length(), 1, true, false);
+ } else if (Reads.compare(cache[B3.start], cache[A3.end - 1]) < 0) {
+ // these two ranges weren't already in order, so merge them back into the array
+ MergeInto(cache, A3, B3, array, A1.start, false);
+ } else {
+ // copy A3 and B3 into the array in the same order
+ Writes.arraycopy(cache, A3.start, array, A1.start, A3.length(), 1, true, false);
+ Writes.arraycopy(cache, B3.start, array, A1.start + A1.length(), B3.length(), 1, true, false);
+ }
+ }
+
+ // we merged two levels at the same time, so we're done with this level already
+ // (iterator.nextLevel() is called again at the bottom of this outer merge loop)
+ iterator.nextLevel();
+
+ } else {
+ iterator.begin();
+ while (!iterator.finished()) {
+ A = iterator.nextRange();
+ B = iterator.nextRange();
+
+ if (Reads.compare(array[B.end - 1], array[A.start]) < 0) {
+ // the two ranges are in reverse order, so a simple rotation should fix it
+ Rotate(array, A.length(), new Range(A.start, B.end), true);
+ } else if (Reads.compare(array[B.start], array[A.end - 1]) < 0) {
+ // these two ranges weren't already in order, so we'll need to merge them!
+ Writes.arraycopy(array, A.start, cache, 0, A.length(), 1, true, true);
+ MergeExternal(array, A, B);
+ }
+ }
+ }
+ } else {
+ // this is where the in-place merge logic starts!
+ // 1. pull out two internal buffers each containing √A unique values
+ // 1a. adjust block_size and buffer_size if we couldn't find enough unique values
+ // 2. loop over the A and B subarrays within this level of the merge sort
+ // 3. break A and B into blocks of size 'block_size'
+ // 4. "tag" each of the A blocks with values from the first internal buffer
+ // 5. roll the A blocks through the B blocks and drop/rotate them where they belong
+ // 6. merge each A block with any B values that follow, using the cache or the second internal buffer
+ // 7. sort the second internal buffer if it exists
+ // 8. redistribute the two internal buffers back into the array
+
+ int block_size = (int)Math.sqrt(iterator.length());
+ int buffer_size = iterator.length()/block_size + 1;
+
+ // as an optimization, we really only need to pull out the internal buffers once for each level of merges
+ // after that we can reuse the same buffers over and over, then redistribute it when we're finished with this level
+ int index, last, count, pull_index = 0;
+ buffer1.set(0, 0);
+ buffer2.set(0, 0);
+
+ pull[0].reset();
+ pull[1].reset();
+
+ // find two internal buffers of size 'buffer_size' each
+ int find = buffer_size + buffer_size;
+ boolean find_separately = false;
+
+ if (block_size <= cache_size) {
+ // if every A block fits into the cache then we won't need the second internal buffer,
+ // so we really only need to find 'buffer_size' unique values
+ find = buffer_size;
+ } else if (find > iterator.length()) {
+ // we can't fit both buffers into the same A or B subarray, so find two buffers separately
+ find = buffer_size;
+ find_separately = true;
+ }
+
+ // we need to find either a single contiguous space containing 2√A unique values (which will be split up into two buffers of size √A each),
+ // or we need to find one buffer of < 2√A unique values, and a second buffer of √A unique values,
+ // OR if we couldn't find that many unique values, we need the largest possible buffer we can get
+
+ // in the case where it couldn't find a single buffer of at least √A unique values,
+ // all of the Merge steps must be replaced by a different merge algorithm (MergeInPlace)
+
+ iterator.begin();
+ while (!iterator.finished()) {
+ A = iterator.nextRange();
+ B = iterator.nextRange();
+
+ // check A for the number of unique values we need to fill an internal buffer
+ // these values will be pulled out to the start of A
+ for (last = A.start, count = 1; count < find; last = index, count++) {
+ index = FindLastForward(array, array[last], new Range(last + 1, A.end), find - count);
+ if (index == A.end) break;
+ }
+ index = last;
+
+ if (count >= buffer_size) {
+ // keep track of the range within the array where we'll need to "pull out" these values to create the internal buffer
+ pull[pull_index].range.set(A.start, B.end);
+ pull[pull_index].count = count;
+ pull[pull_index].from = index;
+ pull[pull_index].to = A.start;
+ pull_index = 1;
+
+ if (count == buffer_size + buffer_size) {
+ // we were able to find a single contiguous section containing 2√A unique values,
+ // so this section can be used to contain both of the internal buffers we'll need
+ buffer1.set(A.start, A.start + buffer_size);
+ buffer2.set(A.start + buffer_size, A.start + count);
+ break;
+ } else if (find == buffer_size + buffer_size) {
+ // we found a buffer that contains at least √A unique values, but did not contain the full 2√A unique values,
+ // so we still need to find a second separate buffer of at least √A unique values
+ buffer1.set(A.start, A.start + count);
+ find = buffer_size;
+ } else if (block_size <= cache_size) {
+ // we found the first and only internal buffer that we need, so we're done!
+ buffer1.set(A.start, A.start + count);
+ break;
+ } else if (find_separately) {
+ // found one buffer, but now find the other one
+ buffer1 = new Range(A.start, A.start + count);
+ find_separately = false;
+ } else {
+ // we found a second buffer in an 'A' subarray containing √A unique values, so we're done!
+ buffer2.set(A.start, A.start + count);
+ break;
+ }
+ } else if (pull_index == 0 && count > buffer1.length()) {
+ // keep track of the largest buffer we were able to find
+ buffer1.set(A.start, A.start + count);
+
+ pull[pull_index].range.set(A.start, B.end);
+ pull[pull_index].count = count;
+ pull[pull_index].from = index;
+ pull[pull_index].to = A.start;
+ }
+
+ // check B for the number of unique values we need to fill an internal buffer
+ // these values will be pulled out to the end of B
+ for (last = B.end - 1, count = 1; count < find; last = index - 1, count++) {
+ index = FindFirstBackward(array, array[last], new Range(B.start, last), find - count);
+ if (index == B.start) break;
+ }
+ index = last;
+
+ if (count >= buffer_size) {
+ // keep track of the range within the array where we'll need to "pull out" these values to create the internal buffer
+ pull[pull_index].range.set(A.start, B.end);
+ pull[pull_index].count = count;
+ pull[pull_index].from = index;
+ pull[pull_index].to = B.end;
+ pull_index = 1;
+
+ if (count == buffer_size + buffer_size) {
+ // we were able to find a single contiguous section containing 2√A unique values,
+ // so this section can be used to contain both of the internal buffers we'll need
+ buffer1.set(B.end - count, B.end - buffer_size);
+ buffer2.set(B.end - buffer_size, B.end);
+ break;
+ } else if (find == buffer_size + buffer_size) {
+ // we found a buffer that contains at least √A unique values, but did not contain the full 2√A unique values,
+ // so we still need to find a second separate buffer of at least √A unique values
+ buffer1.set(B.end - count, B.end);
+ find = buffer_size;
+ } else if (block_size <= cache_size) {
+ // we found the first and only internal buffer that we need, so we're done!
+ buffer1.set(B.end - count, B.end);
+ break;
+ } else if (find_separately) {
+ // found one buffer, but now find the other one
+ buffer1 = new Range(B.end - count, B.end);
+ find_separately = false;
+ } else {
+ // buffer2 will be pulled out from a 'B' subarray, so if the first buffer was pulled out from the corresponding 'A' subarray,
+ // we need to adjust the end point for that A subarray so it knows to stop redistributing its values before reaching buffer2
+ if (pull[0].range.start == A.start) pull[0].range.end -= pull[1].count;
+
+ // we found a second buffer in an 'B' subarray containing √A unique values, so we're done!
+ buffer2.set(B.end - count, B.end);
+ break;
+ }
+ } else if (pull_index == 0 && count > buffer1.length()) {
+ // keep track of the largest buffer we were able to find
+ buffer1.set(B.end - count, B.end);
+
+ pull[pull_index].range.set(A.start, B.end);
+ pull[pull_index].count = count;
+ pull[pull_index].from = index;
+ pull[pull_index].to = B.end;
+ }
+ }
+
+ // pull out the two ranges so we can use them as internal buffers
+ for (pull_index = 0; pull_index < 2; pull_index++) {
+ int length = pull[pull_index].count;
+
+ if (pull[pull_index].to < pull[pull_index].from) {
+ // we're pulling the values out to the left, which means the start of an A subarray
+ index = pull[pull_index].from;
+ for (count = 1; count < length; count++) {
+ index = FindFirstBackward(array, array[index - 1], new Range(pull[pull_index].to, pull[pull_index].from - (count - 1)), length - count);
+ Range range = new Range(index + 1, pull[pull_index].from + 1);
+ Rotate(array, range.length() - count, range, true);
+ pull[pull_index].from = index + count;
+ }
+ } else if (pull[pull_index].to > pull[pull_index].from) {
+ // we're pulling values out to the right, which means the end of a B subarray
+ index = pull[pull_index].from + 1;
+ for (count = 1; count < length; count++) {
+ index = FindLastForward(array, array[index], new Range(index, pull[pull_index].to), length - count);
+ Range range = new Range(pull[pull_index].from, index - 1);
+ Rotate(array, count, range, true);
+ pull[pull_index].from = index - 1 - count;
+ }
+ }
+ }
+
+ // adjust block_size and buffer_size based on the values we were able to pull out
+ buffer_size = buffer1.length();
+ block_size = iterator.length()/buffer_size + 1;
+
+ // the first buffer NEEDS to be large enough to tag each of the evenly sized A blocks,
+ // so this was originally here to test the math for adjusting block_size above
+ //if ((iterator.length() + 1)/block_size > buffer_size) throw new RuntimeException();
+
+ // now that the two internal buffers have been created, it's time to merge each A+B combination at this level of the merge sort!
+ iterator.begin();
+ while (!iterator.finished()) {
+ A = iterator.nextRange();
+ B = iterator.nextRange();
+
+ // remove any parts of A or B that are being used by the internal buffers
+ int start = A.start;
+ if (start == pull[0].range.start) {
+ if (pull[0].from > pull[0].to) {
+ A.start += pull[0].count;
+
+ // if the internal buffer takes up the entire A or B subarray, then there's nothing to merge
+ // this only happens for very small subarrays, like √4 = 2, 2 * (2 internal buffers) = 4,
+ // which also only happens when cache_size is small or 0 since it'd otherwise use MergeExternal
+ if (A.length() == 0) continue;
+ } else if (pull[0].from < pull[0].to) {
+ B.end -= pull[0].count;
+ if (B.length() == 0) continue;
+ }
+ }
+ if (start == pull[1].range.start) {
+ if (pull[1].from > pull[1].to) {
+ A.start += pull[1].count;
+ if (A.length() == 0) continue;
+ } else if (pull[1].from < pull[1].to) {
+ B.end -= pull[1].count;
+ if (B.length() == 0) continue;
+ }
+ }
+
+ if (Reads.compare(array[B.end - 1], array[A.start]) < 0) {
+ // the two ranges are in reverse order, so a simple rotation should fix it
+ Rotate(array, A.length(), new Range(A.start, B.end), true);
+ } else if (Reads.compare(array[A.end], array[A.end - 1]) < 0) {
+ // these two ranges weren't already in order, so we'll need to merge them!
+
+ // break the remainder of A into blocks. firstA is the uneven-sized first A block
+ blockA.set(A.start, A.end);
+ firstA.set(A.start, A.start + blockA.length() % block_size);
+
+ // swap the first value of each A block with the value in buffer1
+ int indexA = buffer1.start;
+ for (index = firstA.end; index < blockA.end; index += block_size) {
+ Writes.swap(array, indexA, index, 1, true, false);
+ indexA++;
+ }
+
+ // start rolling the A blocks through the B blocks!
+ // whenever we leave an A block behind, we'll need to merge the previous A block with any B blocks that follow it, so track that information as well
+ lastA.set(firstA.start, firstA.end);
+ lastB.set(0, 0);
+ blockB.set(B.start, B.start + Math.min(block_size, B.length()));
+ blockA.start += firstA.length();
+ indexA = buffer1.start;
+
+ // if the first unevenly sized A block fits into the cache, copy it there for when we go to Merge it
+ // otherwise, if the second buffer is available, block swap the contents into that
+ if (lastA.length() <= cache_size && cache != null) {
+ Writes.arraycopy(array, lastA.start, cache, 0, lastA.length(), 1, true, true);
+ }
+ else if (buffer2.length() > 0)
+ BlockSwap(array, lastA.start, buffer2.start, lastA.length());
+
+ if (blockA.length() > 0) {
+ while (true) {
+ // if there's a previous B block and the first value of the minimum A block is <= the last value of the previous B block,
+ // then drop that minimum A block behind. or if there are no B blocks left then keep dropping the remaining A blocks.
+ if ((lastB.length() > 0 && Reads.compare(array[lastB.end - 1], array[indexA]) >= 0) || blockB.length() == 0) {
+ // figure out where to split the previous B block, and rotate it at the split
+ int B_split = BinaryFirst(array, array[indexA], lastB);
+ int B_remaining = lastB.end - B_split;
+
+ // swap the minimum A block to the beginning of the rolling A blocks
+ int minA = blockA.start;
+ for (int findA = minA + block_size; findA < blockA.end; findA += block_size)
+ if (Reads.compare(array[findA], array[minA]) < 0)
+ minA = findA;
+ BlockSwap(array, blockA.start, minA, block_size);
+
+ // swap the first item of the previous A block back with its original value, which is stored in buffer1
+ Writes.swap(array, blockA.start, indexA, 1, true, false);
+ indexA++;
+
+ // locally merge the previous A block with the B values that follow it
+ // if lastA fits into the external cache we'll use that (with MergeExternal),
+ // or if the second internal buffer exists we'll use that (with MergeInternal),
+ // or failing that we'll use a strictly in-place merge algorithm (MergeInPlace)
+ if (lastA.length() <= cache_size)
+ MergeExternal(array, lastA, new Range(lastA.end, B_split));
+ else if (buffer2.length() > 0)
+ MergeInternal(array, lastA, new Range(lastA.end, B_split), buffer2);
+ else
+ MergeInPlace(array, lastA, new Range(lastA.end, B_split));
+
+ if (buffer2.length() > 0 || block_size <= cache_size) {
+ // copy the previous A block into the cache or buffer2, since that's where we need it to be when we go to merge it anyway
+ if (block_size <= cache_size) {
+ Writes.arraycopy(array, blockA.start, cache, 0, block_size, 1, true, true);
+ }
+ else
+ BlockSwap(array, blockA.start, buffer2.start, block_size);
+
+ // this is equivalent to rotating, but faster
+ // the area normally taken up by the A block is either the contents of buffer2, or data we don't need anymore since we memcopied it
+ // either way, we don't need to retain the order of those items, so instead of rotating we can just block swap B to where it belongs
+ BlockSwap(array, B_split, blockA.start + block_size - B_remaining, B_remaining);
+ } else {
+ // we are unable to use the 'buffer2' trick to speed up the rotation operation since buffer2 doesn't exist, so perform a normal rotation
+ Rotate(array, blockA.start - B_split, new Range(B_split, blockA.start + block_size), true);
+ }
+
+ // update the range for the remaining A blocks, and the range remaining from the B block after it was split
+ lastA.set(blockA.start - B_remaining, blockA.start - B_remaining + block_size);
+ lastB.set(lastA.end, lastA.end + B_remaining);
+
+ // if there are no more A blocks remaining, this step is finished!
+ blockA.start += block_size;
+ if (blockA.length() == 0)
+ break;
+
+ } else if (blockB.length() < block_size) {
+ // move the last B block, which is unevenly sized, to before the remaining A blocks, by using a rotation
+ // the cache is disabled here since it might contain the contents of the previous A block
+ Rotate(array, -blockB.length(), new Range(blockA.start, blockB.end), false);
+
+ lastB.set(blockA.start, blockA.start + blockB.length());
+ blockA.start += blockB.length();
+ blockA.end += blockB.length();
+ blockB.end = blockB.start;
+ } else {
+ // roll the leftmost A block to the end by swapping it with the next B block
+ BlockSwap(array, blockA.start, blockB.start, block_size);
+ lastB.set(blockA.start, blockA.start + block_size);
+
+ blockA.start += block_size;
+ blockA.end += block_size;
+ blockB.start += block_size;
+ blockB.end += block_size;
+
+ if (blockB.end > B.end)
+ blockB.end = B.end;
+ }
+ }
+ }
+
+ // merge the last A block with the remaining B values
+ if (lastA.length() <= cache_size)
+ MergeExternal(array, lastA, new Range(lastA.end, B.end));
+ else if (buffer2.length() > 0)
+ MergeInternal(array, lastA, new Range(lastA.end, B.end), buffer2);
+ else
+ MergeInPlace(array, lastA, new Range(lastA.end, B.end));
+ }
+ }
+
+ Highlights.clearMark(2);
+
+ // when we're finished with this merge step we should have the one or two internal buffers left over, where the second buffer is all jumbled up
+ // insertion sort the second buffer, then redistribute the buffers back into the array using the opposite process used for creating the buffer
+
+ // while an unstable sort like quick sort could be applied here, in benchmarks it was consistently slightly slower than a simple insertion sort,
+ // even for tens of millions of items. this may be because insertion sort is quite fast when the data is already somewhat sorted, like it is here
+ InsertionSort(array, buffer2);
+
+ for (pull_index = 0; pull_index < 2; pull_index++) {
+ int unique = pull[pull_index].count * 2;
+ if (pull[pull_index].from > pull[pull_index].to) {
+ // the values were pulled out to the left, so redistribute them back to the right
+ Range buffer = new Range(pull[pull_index].range.start, pull[pull_index].range.start + pull[pull_index].count);
+ while (buffer.length() > 0) {
+ index = FindFirstForward(array, array[buffer.start], new Range(buffer.end, pull[pull_index].range.end), unique);
+ int amount = index - buffer.end;
+ Rotate(array, buffer.length(), new Range(buffer.start, index), true);
+ buffer.start += (amount + 1);
+ buffer.end += amount;
+ unique -= 2;
+ }
+ } else if (pull[pull_index].from < pull[pull_index].to) {
+ // the values were pulled out to the right, so redistribute them back to the left
+ Range buffer = new Range(pull[pull_index].range.end - pull[pull_index].count, pull[pull_index].range.end);
+ while (buffer.length() > 0) {
+ index = FindLastBackward(array, array[buffer.end - 1], new Range(pull[pull_index].range.start, buffer.start), unique);
+ int amount = buffer.start - index;
+ Rotate(array, amount, new Range(index, buffer.end), true);
+ buffer.start -= amount;
+ buffer.end -= (amount + 1);
+ unique -= 2;
+ }
+ }
+ }
+ }
+
+ // double the size of each A and B subarray that will be merged in the next level
+ if (!iterator.nextLevel()) break;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/Tester.java b/src/test/Tester.java
new file mode 100644
index 00000000..4236e1e5
--- /dev/null
+++ b/src/test/Tester.java
@@ -0,0 +1,84 @@
+package test;
+
+import java.util.Arrays;
+
+import main.ArrayVisualizer;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Timer;
+import utils.Writes;
+
+public class Tester {
+ private static Delays Delays;
+ private static Highlights Highlights;
+ private static Reads Reads;
+ private static Timer RealTimer;
+ private static Writes Writes;
+
+ // Attempts to use insertion sort on [begin, end). Will return false if more than
+ // partial_insertion_sort_limit elements were moved, and abort sorting. Otherwise it will
+ // successfully sort and return true.
+ private static boolean pdqPartialInsertSort(int[] array, int begin, int end) {
+ if (begin == end) return true;
+
+ int limit = 0;
+ for (int cur = begin + 1; cur != end; ++cur) {
+ if (limit > 8) return false;
+
+ int sift = cur;
+ int siftMinusOne = cur - 1;
+
+ // Compare first so we can avoid 2 moves for an element already positioned correctly.
+ if (Reads.compare(array[sift], array[siftMinusOne]) < 0) {
+ int tmp = array[sift];
+
+ do {
+ Writes.write(array, sift--, array[siftMinusOne], 0, false, false);
+ } while (sift != begin && Reads.compare(tmp, array[--siftMinusOne]) < 0);
+
+ Writes.write(array, sift, tmp, 0, false, false);
+ limit += cur - sift;
+ }
+ }
+ return true;
+ }
+
+ public static void main(String[] args) throws Exception {
+ int[] testArr = new int[32];
+ for(int i = 0; i < testArr.length; i++) {
+ testArr[i] = i;
+ }
+
+ ArrayVisualizer av = new ArrayVisualizer();
+
+ Delays = new Delays(av);
+ Delays.setSleepRatio(Double.MAX_VALUE);
+
+ Highlights = new Highlights(av, testArr.length);
+ Highlights.toggleFancyFinishes(false);
+
+ RealTimer = new Timer();
+ RealTimer.toggleRealTimer(false);
+
+ Reads = new Reads(av);
+ Writes = new Writes(av);
+
+ for(int i = 0; i < testArr.length; i++){
+ Writes.swap(testArr, i, (int)(Math.random()*testArr.length), 0, false, false);
+ }
+
+ System.out.println(Arrays.toString(testArr));
+
+ RealTimer.startLap();
+
+ pdqPartialInsertSort(testArr, 0, testArr.length);
+
+ RealTimer.stopLap();
+
+ RealTimer.toggleRealTimer(true);
+
+ System.out.println(Arrays.toString(testArr));
+ System.out.println(RealTimer.getRealTime());
+ }
+}
\ No newline at end of file
diff --git a/src/threads/RunAllSorts.java b/src/threads/RunAllSorts.java
new file mode 100644
index 00000000..aeda83f0
--- /dev/null
+++ b/src/threads/RunAllSorts.java
@@ -0,0 +1,73 @@
+package threads;
+
+import java.util.ArrayList;
+
+import main.ArrayVisualizer;
+import templates.JErrorPane;
+import templates.MultipleSortThread;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class RunAllSorts {
+ private ArrayVisualizer ArrayVisualizer;
+ private ArrayList allSortThreads;
+
+ public RunAllSorts(ArrayVisualizer ArrayVisualizer) {
+ this.ArrayVisualizer = ArrayVisualizer;
+ this.allSortThreads = new ArrayList<>();
+ this.allSortThreads.add(new RunExchangeSorts(ArrayVisualizer));
+ this.allSortThreads.add(new RunSelectionSorts(ArrayVisualizer));
+ this.allSortThreads.add(new RunInsertionSorts(ArrayVisualizer));
+ this.allSortThreads.add(new RunMergeSorts(ArrayVisualizer));
+ this.allSortThreads.add(new RunDistributionSorts(ArrayVisualizer));
+ this.allSortThreads.add(new RunConcurrentSorts(ArrayVisualizer));
+ this.allSortThreads.add(new RunHybridSorts(ArrayVisualizer));
+ this.allSortThreads.add(new RunMiscellaneousSorts(ArrayVisualizer));
+ this.allSortThreads.add(new RunImpracticalSorts(ArrayVisualizer));
+ }
+
+ public void reportAllSorts(int[] array) throws Exception {
+ int totalSortCount = 0;
+ for(MultipleSortThread category : this.allSortThreads) {
+ totalSortCount += category.getSortCount();
+ }
+
+ try {
+ int currentSort = 1;
+ for(MultipleSortThread thread : this.allSortThreads) {
+ thread.reportAllSorts(array, currentSort, totalSortCount);
+ this.ArrayVisualizer.getSortingThread().join();
+ currentSort += thread.getCategoryCount();
+ }
+ } catch (Exception e) {
+ JErrorPane.invokeErrorMessage(e);
+ }
+
+ this.ArrayVisualizer.setCategory("Run All Sorts");
+ this.ArrayVisualizer.setHeading("Finished!!");
+ }
+}
\ No newline at end of file
diff --git a/src/threads/RunComparisonSort.java b/src/threads/RunComparisonSort.java
new file mode 100644
index 00000000..9b5f0a83
--- /dev/null
+++ b/src/threads/RunComparisonSort.java
@@ -0,0 +1,139 @@
+package threads;
+
+import java.lang.reflect.Constructor;
+
+import javax.swing.JOptionPane;
+
+import main.ArrayManager;
+import main.ArrayVisualizer;
+import templates.JErrorPane;
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Sounds;
+import utils.Timer;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class RunComparisonSort {
+ private ArrayManager ArrayManager;
+ private ArrayVisualizer ArrayVisualizer;
+ private Delays delayOps;
+ private Highlights markOps;
+ private Reads readOps;
+ private Writes writeOps;
+ private Sounds Sounds;
+ private Timer realTimer;
+
+ public RunComparisonSort(ArrayVisualizer ArrayVisualizer) {
+ this.ArrayVisualizer = ArrayVisualizer;
+ this.ArrayManager = ArrayVisualizer.getArrayManager();
+ this.delayOps = ArrayVisualizer.getDelays();
+ this.markOps = ArrayVisualizer.getHighlights();
+ this.readOps = ArrayVisualizer.getReads();
+ this.writeOps = ArrayVisualizer.getWrites();
+ this.Sounds = ArrayVisualizer.getSounds();
+ this.realTimer = ArrayVisualizer.getTimer();
+ }
+
+ public void ReportComparativeSort(int[] array, int selection) {
+ if(ArrayVisualizer.getSortingThread() != null && ArrayVisualizer.getSortingThread().isAlive())
+ return;
+
+ //TODO: This code is bugged! It causes the program to forget the sleep ratio specified by the user!
+ if(delayOps.skipped()) {
+ delayOps.setSleepRatio(1);
+ delayOps.changeSkipped(false);
+ }
+
+ Sounds.toggleSound(true);
+ ArrayVisualizer.setSortingThread(new Thread() {
+ @Override
+ public void run() {
+ try {
+ Class> sortClass = Class.forName(ArrayVisualizer.getComparisonSorts()[0][selection]);
+ Constructor> newSort = sortClass.getConstructor(new Class[] {Delays.class, Highlights.class, Reads.class, Writes.class});
+ Sort sort = (Sort) newSort.newInstance(delayOps, markOps, readOps, writeOps);
+
+ boolean goAhead;
+
+ ArrayManager.toggleMutableLength(false);
+ ArrayManager.refreshArray(array, ArrayVisualizer.getCurrentLength(), ArrayVisualizer);
+
+ if(sort.getUnreasonablySlow() && ArrayVisualizer.getCurrentLength() >= sort.getUnreasonableLimit()) {
+ goAhead = false;
+ Object[] options = { "Let's see how bad " + sort.getReportSortID() + " is!", "Cancel" };
+
+ if(sort.bogoSort()) {
+ int warning = JOptionPane.showOptionDialog(ArrayVisualizer.getMainWindow(), "Even at a high speed, "
+ + sort.getReportSortID() + "ing " + ArrayVisualizer.getCurrentLength()
+ + " numbers will almost certainly not finish in a reasonable amount of time. "
+ + "Are you sure you want to continue?", "Warning!", 2, JOptionPane.WARNING_MESSAGE,
+ null, options, options[1]);
+ if(warning == 0) goAhead = true;
+ else goAhead = false;
+ }
+ else {
+ int warning = JOptionPane.showOptionDialog(ArrayVisualizer.getMainWindow(), "Even at a high speed, "
+ + sort.getReportSortID() + "ing " + ArrayVisualizer.getCurrentLength()
+ + " numbers will not finish in a reasonable amount of time. "
+ + "Are you sure you want to continue?", "Warning!", 2, JOptionPane.WARNING_MESSAGE,
+ null, options, options[1]);
+
+ if(warning == 0) goAhead = true;
+ else goAhead = false;
+ }
+ }
+ else {
+ goAhead = true;
+ }
+
+ if(goAhead) {
+ ArrayVisualizer.setHeading(sort.getReportSortID());
+ ArrayVisualizer.setCategory(sort.getCategory());
+
+ realTimer.enableRealTimer();
+ sort.runSort(array, ArrayVisualizer.getCurrentLength(), 0);
+ }
+ else {
+ ArrayManager.initializeArray(array);
+ }
+ }
+ catch(Exception e) {
+ JErrorPane.invokeErrorMessage(e);
+ }
+ ArrayVisualizer.endSort();
+ ArrayManager.toggleMutableLength(true);
+ Sounds.toggleSound(false);
+ }
+ });
+
+ ArrayVisualizer.runSortingThread();
+ }
+}
\ No newline at end of file
diff --git a/src/threads/RunConcurrentSorts.java b/src/threads/RunConcurrentSorts.java
new file mode 100644
index 00000000..a70ac38d
--- /dev/null
+++ b/src/threads/RunConcurrentSorts.java
@@ -0,0 +1,112 @@
+package threads;
+
+import main.ArrayVisualizer;
+import sorts.IterativeBitonicSort;
+import sorts.IterativeOddEvenMergeSort;
+import sorts.IterativePairwiseSort;
+import sorts.RecursiveBitonicSort;
+import sorts.RecursiveOddEvenMergeSort;
+import sorts.RecursivePairwiseSort;
+import templates.JErrorPane;
+import templates.MultipleSortThread;
+import templates.Sort;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class RunConcurrentSorts extends MultipleSortThread {
+ private Sort RecursiveBitonicSort;
+ private Sort RecursiveOddEvenMergeSort;
+ private Sort RecursivePairwiseSort;
+ private Sort IterativeBitonicSort;
+ private Sort IterativeOddEvenMergeSort;
+ private Sort IterativePairwiseSort;
+
+ public RunConcurrentSorts(ArrayVisualizer ArrayVisualizer) {
+ super(ArrayVisualizer);
+ this.sortCount = 6;
+ this.categoryCount = this.sortCount;
+
+ RecursiveBitonicSort = new RecursiveBitonicSort(Delays, Highlights, Reads, Writes);
+ RecursiveOddEvenMergeSort = new RecursiveOddEvenMergeSort(Delays, Highlights, Reads, Writes);
+ RecursivePairwiseSort = new RecursivePairwiseSort(Delays, Highlights, Reads, Writes);
+ IterativeBitonicSort = new IterativeBitonicSort(Delays, Highlights, Reads, Writes);
+ IterativeOddEvenMergeSort = new IterativeOddEvenMergeSort(Delays, Highlights, Reads, Writes);
+ IterativePairwiseSort = new IterativePairwiseSort(Delays, Highlights, Reads, Writes);
+ }
+
+ @Override
+ protected synchronized void executeSortList(int[] array) throws Exception {
+ RunConcurrentSorts.this.runIndividualSort(RecursiveBitonicSort, 0, array, 1024, 0.6667);
+ RunConcurrentSorts.this.runIndividualSort(RecursiveOddEvenMergeSort, 0, array, 1024, 1);
+ RunConcurrentSorts.this.runIndividualSort(RecursivePairwiseSort, 0, array, 1024, 1);
+ RunConcurrentSorts.this.runIndividualSort(IterativeBitonicSort, 0, array, 1024, 0.3333);
+ RunConcurrentSorts.this.runIndividualSort(IterativeOddEvenMergeSort, 0, array, 1024, 1);
+ RunConcurrentSorts.this.runIndividualSort(IterativePairwiseSort, 0, array, 1024, 0.8);
+ }
+
+ @Override
+ protected synchronized void runThread(int[] array, int current, int total, boolean runAllActive) throws Exception {
+ if(ArrayVisualizer.getSortingThread() != null && ArrayVisualizer.getSortingThread().isAlive())
+ return;
+
+ Sounds.toggleSound(true);
+ ArrayVisualizer.setSortingThread(new Thread() {
+ @Override
+ public void run() {
+ try{
+ if(runAllActive) {
+ RunConcurrentSorts.this.sortNumber = current;
+ RunConcurrentSorts.this.sortCount = total;
+ }
+ else {
+ RunConcurrentSorts.this.sortNumber = 1;
+ }
+
+ ArrayManager.toggleMutableLength(false);
+
+ ArrayVisualizer.setCategory("Concurrent Sorts");
+
+ RunConcurrentSorts.this.executeSortList(array);
+
+ if(!runAllActive) {
+ ArrayVisualizer.setCategory("Run Concurrent Sorts");
+ ArrayVisualizer.setHeading("Done");
+ }
+
+ ArrayManager.toggleMutableLength(true);
+ }
+ catch (Exception e) {
+ JErrorPane.invokeErrorMessage(e);
+ }
+ Sounds.toggleSound(false);
+ ArrayVisualizer.setSortingThread(null);
+ }
+ });
+
+ ArrayVisualizer.runSortingThread();
+ }
+}
\ No newline at end of file
diff --git a/src/threads/RunDistributionSort.java b/src/threads/RunDistributionSort.java
new file mode 100644
index 00000000..93dd790c
--- /dev/null
+++ b/src/threads/RunDistributionSort.java
@@ -0,0 +1,255 @@
+package threads;
+
+import java.lang.reflect.Constructor;
+
+import javax.swing.JOptionPane;
+
+import main.ArrayManager;
+import main.ArrayVisualizer;
+import templates.JEnhancedOptionPane;
+import templates.JErrorPane;
+import templates.Sort;
+import utils.Delays;
+import utils.Highlights;
+import utils.Reads;
+import utils.Sounds;
+import utils.Timer;
+import utils.Writes;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class RunDistributionSort {
+ private ArrayManager ArrayManager;
+ private ArrayVisualizer ArrayVisualizer;
+ private Delays delayOps;
+ private Highlights markOps;
+ private Reads readOps;
+ private Writes writeOps;
+ private Sounds Sounds;
+ private Timer realTimer;
+
+ public RunDistributionSort(ArrayVisualizer ArrayVisualizer) {
+ this.ArrayVisualizer = ArrayVisualizer;
+ this.ArrayManager = ArrayVisualizer.getArrayManager();
+ this.delayOps = ArrayVisualizer.getDelays();
+ this.markOps = ArrayVisualizer.getHighlights();
+ this.readOps = ArrayVisualizer.getReads();
+ this.writeOps = ArrayVisualizer.getWrites();
+ this.Sounds = ArrayVisualizer.getSounds();
+ this.realTimer = ArrayVisualizer.getTimer();
+ }
+
+ private String getTimeSortEstimate(int bucketCount) {
+ String timeString = "";
+ String timeUnit;
+
+ int seconds = Math.max(((ArrayVisualizer.getCurrentLength() * bucketCount) / 1000), 1);
+ int minutes;
+ int hours;
+ long days;
+
+ if(seconds >= 60) {
+ minutes = Math.round(seconds / 60);
+
+ if(minutes >= 60) {
+ hours = Math.round(minutes / 60);
+
+ if(hours >= 24) {
+ days = Math.round(hours / 24);
+
+ if(days < 2) timeUnit = "day";
+ else timeUnit = "days";
+
+ timeString = "" + ArrayVisualizer.getNumberFormat().format(days) + " " + timeUnit + " ";
+ }
+ else {
+ if(hours < 2) timeUnit = "hour";
+ else timeUnit = "hours";
+
+ timeString = "" + hours + " " + timeUnit + " ";
+ }
+ }
+ else {
+ if(minutes < 2) timeUnit = "minute";
+ else timeUnit = "minutes";
+
+ timeString = "" + minutes + " " + timeUnit + " ";
+ }
+ }
+ else {
+ if(seconds < 2) timeUnit = "second";
+ else timeUnit = "seconds";
+
+ timeString = "" + seconds + " " + timeUnit + " ";
+ }
+
+ return timeString;
+ }
+
+ public void ReportDistributiveSort(int[] array, int selection) {
+ if(ArrayVisualizer.getSortingThread() != null && ArrayVisualizer.getSortingThread().isAlive())
+ return;
+
+ //TODO: This code is bugged! It causes the program to forget the sleep ratio specified by the user!
+ if(delayOps.skipped()) {
+ delayOps.setSleepRatio(1);
+ delayOps.changeSkipped(false);
+ }
+
+ double storeVol = Sounds.getVolume();
+
+ ArrayVisualizer.setCategory("Distributive Sorts");
+
+ Sounds.toggleSound(true);
+ ArrayVisualizer.setSortingThread(new Thread() {
+ @SuppressWarnings("unused")
+ @Override
+ public void run(){
+ try {
+ Class> sortClass = Class.forName(ArrayVisualizer.getDistributionSorts()[0][selection]);
+ Constructor> newSort = sortClass.getConstructor(new Class[] {Delays.class, Highlights.class, Reads.class, Writes.class});
+ Sort sort = (Sort) newSort.newInstance(delayOps, markOps, readOps, writeOps);
+
+ int bucketCount;
+
+ if(sort.getReportSortID().equals("Timesort")) {
+ try {
+ bucketCount = (int) Math.ceil(Double.parseDouble(JEnhancedOptionPane.showInputDialog("Enter delay per number in milliseconds:", new Object[]{"Enter", "Use default"})));
+ }
+ catch(Exception e) {
+ bucketCount = 10;
+ }
+ }
+ else {
+ if(sort.usesBuckets()) {
+ if(sort.radixSort()) {
+ try {
+ bucketCount = Integer.parseInt(JEnhancedOptionPane.showInputDialog("Enter the base for this sort:", new Object[]{"Enter", "Use default"}));
+ }
+ catch(Exception e) {
+ bucketCount = 4;
+ }
+ }
+ else if(sort.getReportSortID().equals("Shatter Sort") || sort.getReportSortID().equals("Simple Shatter Sort")) {
+ try {
+ bucketCount = Integer.parseInt(JEnhancedOptionPane.showInputDialog("Enter the size for each partition:", new Object[]{"Enter", "Use default"}));
+ }
+ catch(Exception e) {
+ bucketCount = ArrayVisualizer.getCurrentLength() / 16;
+ }
+ }
+ else {
+ try {
+ bucketCount = Integer.parseInt(JEnhancedOptionPane.showInputDialog("How many buckets will this sort use?", new Object[]{"Enter", "Use default"}));
+ }
+ catch(Exception e) {
+ bucketCount = 16;
+ }
+ }
+
+ if(bucketCount < 2) bucketCount = 2;
+ }
+ else {
+ bucketCount = 0;
+ }
+ }
+
+ ArrayManager.toggleMutableLength(false);
+ ArrayManager.refreshArray(array, ArrayVisualizer.getCurrentLength(), ArrayVisualizer);
+
+ boolean goAhead;
+
+ if(sort.getUnreasonablySlow() && ArrayVisualizer.getCurrentLength() >= sort.getUnreasonableLimit()) {
+ goAhead = false;
+
+ if(sort.getReportSortID().equals("Timesort")) {
+ Object[] options = { "Continue", "Cancel" };
+
+ int warning = JOptionPane.showOptionDialog(ArrayVisualizer.getMainWindow(), "Time Sort will take at least " + getTimeSortEstimate(bucketCount)
+ + "to complete. Once it starts, you cannot skip this sort.", "Warning!", 2, JOptionPane.WARNING_MESSAGE,
+ null, options, options[1]);
+
+ if(warning == 0) goAhead = true;
+ else goAhead = false;
+
+ }
+ else {
+ Object[] options = { "Let's see how bad " + sort.getReportSortID() + " is!", "Cancel" };
+
+ if(sort.bogoSort()) {
+ int warning = JOptionPane.showOptionDialog(ArrayVisualizer.getMainWindow(), "Even at a high speed, "
+ + sort.getReportSortID() + "ing " + ArrayVisualizer.getCurrentLength()
+ + " numbers will almost certainly not finish in a reasonable amount of time. "
+ + "Are you sure you want to continue?", "Warning!", 2, JOptionPane.WARNING_MESSAGE,
+ null, options, options[1]);
+ if(warning == 0) goAhead = true;
+ else goAhead = false;
+ }
+ else {
+ //Currently, no distribution sort calls this message. It's here if you want to include a sort that might use it in the future.
+ int warning = JOptionPane.showOptionDialog(ArrayVisualizer.getMainWindow(), "Even at a high speed, "
+ + sort.getReportSortID() + "ing " + ArrayVisualizer.getCurrentLength()
+ + " numbers will not finish in a reasonable amount of time. "
+ + "Are you sure you want to continue?", "Warning!", 2, JOptionPane.WARNING_MESSAGE,
+ null, options, options[1]);
+
+ if(warning == 0) goAhead = true;
+ else goAhead = false;
+ }
+ }
+ }
+ else {
+ goAhead = true;
+ }
+
+ if(sort.getReportSortID().equals("In-Place LSD Radix")) {
+ Sounds.changeVolume(0.01); // Here to protect your ears :)
+ }
+
+ if(goAhead) {
+ ArrayVisualizer.setHeading(sort.getReportSortID());
+
+ realTimer.enableRealTimer();
+ sort.runSort(array, ArrayVisualizer.getCurrentLength(), bucketCount);
+ }
+ else {
+ ArrayManager.initializeArray(array);
+ }
+ }
+ catch(Exception e) {
+ JErrorPane.invokeErrorMessage(e);
+ }
+ ArrayVisualizer.endSort();
+ ArrayManager.toggleMutableLength(true);
+ Sounds.changeVolume(storeVol);
+ Sounds.toggleSound(false);
+ }
+ });
+
+ ArrayVisualizer.runSortingThread();
+ }
+}
\ No newline at end of file
diff --git a/src/threads/RunDistributionSorts.java b/src/threads/RunDistributionSorts.java
new file mode 100644
index 00000000..c8279716
--- /dev/null
+++ b/src/threads/RunDistributionSorts.java
@@ -0,0 +1,141 @@
+package threads;
+
+import main.ArrayVisualizer;
+import sorts.AmericanFlagSort;
+import sorts.BinaryQuickSort;
+import sorts.CountingSort;
+import sorts.FlashSort;
+import sorts.GravitySort;
+import sorts.InPlaceLSDRadixSort;
+import sorts.LSDRadixSort;
+import sorts.MSDRadixSort;
+import sorts.PigeonholeSort;
+import sorts.RecursiveBinaryQuickSort;
+import sorts.ShatterSort;
+import sorts.SimpleShatterSort;
+import sorts.TimeSort;
+import templates.JErrorPane;
+import templates.MultipleSortThread;
+import templates.Sort;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class RunDistributionSorts extends MultipleSortThread {
+ private Sort CountingSort;
+ private Sort PigeonholeSort;
+ private Sort GravitySort;
+ private Sort AmericanFlagSort;
+ private Sort LSDRadixSort;
+ private Sort InPlaceLSDRadixSort;
+ private Sort MSDRadixSort;
+ private Sort FlashSort;
+ private Sort BinaryQuickSort;
+ private Sort RecursiveBinaryQuickSort;
+ private Sort ShatterSort;
+ private Sort SimpleShatterSort;
+ private Sort TimeSort;
+
+ public RunDistributionSorts(ArrayVisualizer ArrayVisualizer) {
+ super(ArrayVisualizer);
+ this.sortCount = 13;
+ this.categoryCount = this.sortCount;
+
+ CountingSort = new CountingSort(Delays, Highlights, Reads, Writes);
+ PigeonholeSort = new PigeonholeSort(Delays, Highlights, Reads, Writes);
+ GravitySort = new GravitySort(Delays, Highlights, Reads, Writes);
+ AmericanFlagSort = new AmericanFlagSort(Delays, Highlights, Reads, Writes);
+ LSDRadixSort = new LSDRadixSort(Delays, Highlights, Reads, Writes);
+ InPlaceLSDRadixSort = new InPlaceLSDRadixSort(Delays, Highlights, Reads, Writes);
+ MSDRadixSort = new MSDRadixSort(Delays, Highlights, Reads, Writes);
+ FlashSort = new FlashSort(Delays, Highlights, Reads, Writes);
+ BinaryQuickSort = new BinaryQuickSort(Delays, Highlights, Reads, Writes);
+ RecursiveBinaryQuickSort = new RecursiveBinaryQuickSort(Delays, Highlights, Reads, Writes);
+ ShatterSort = new ShatterSort(Delays, Highlights, Reads, Writes);
+ SimpleShatterSort = new SimpleShatterSort(Delays, Highlights, Reads, Writes);
+ TimeSort = new TimeSort(Delays, Highlights, Reads, Writes);
+ }
+
+ @Override
+ protected synchronized void executeSortList(int[] array) throws Exception {
+ RunDistributionSorts.this.runIndividualSort(CountingSort, 0, array, 2048, 1.5);
+ RunDistributionSorts.this.runIndividualSort(PigeonholeSort, 0, array, 2048, 1.5);
+ RunDistributionSorts.this.runIndividualSort(GravitySort, 0, array, 1024, 0.5);
+ RunDistributionSorts.this.runIndividualSort(AmericanFlagSort, 128, array, 2048, 0.75);
+ RunDistributionSorts.this.runIndividualSort(LSDRadixSort, 4, array, 2048, 1.5);
+ Sounds.toggleSofterSounds(true);
+ RunDistributionSorts.this.runIndividualSort(InPlaceLSDRadixSort, 10, array, 2048, 1);
+ Sounds.toggleSofterSounds(false);
+ RunDistributionSorts.this.runIndividualSort(MSDRadixSort, 4, array, 2048, 1.25);
+ RunDistributionSorts.this.runIndividualSort(FlashSort, 0, array, 2048, 1);
+ RunDistributionSorts.this.runIndividualSort(BinaryQuickSort, 0, array, 2048, 1);
+ RunDistributionSorts.this.runIndividualSort(RecursiveBinaryQuickSort, 0, array, 2048, 1);
+ RunDistributionSorts.this.runIndividualSort(ShatterSort, 128, array, 2048, 1);
+ RunDistributionSorts.this.runIndividualSort(SimpleShatterSort, 128, array, 2048, 1);
+ RunDistributionSorts.this.runIndividualSort(TimeSort, 10, array, 2048, 1);
+ }
+
+ @Override
+ protected synchronized void runThread(int[] array, int current, int total, boolean runAllActive) throws Exception {
+ if(ArrayVisualizer.getSortingThread() != null && ArrayVisualizer.getSortingThread().isAlive())
+ return;
+
+ Sounds.toggleSound(true);
+ ArrayVisualizer.setSortingThread(new Thread() {
+ @Override
+ public void run() {
+ try{
+ if(runAllActive) {
+ RunDistributionSorts.this.sortNumber = current;
+ RunDistributionSorts.this.sortCount = total;
+ }
+ else {
+ RunDistributionSorts.this.sortNumber = 1;
+ }
+
+ ArrayManager.toggleMutableLength(false);
+
+ ArrayVisualizer.setCategory("Distribution Sorts");
+
+ RunDistributionSorts.this.executeSortList(array);
+
+ if(!runAllActive) {
+ ArrayVisualizer.setCategory("Run Distribution Sorts");
+ ArrayVisualizer.setHeading("Done");
+ }
+
+ ArrayManager.toggleMutableLength(true);
+ }
+ catch (Exception e) {
+ JErrorPane.invokeErrorMessage(e);
+ }
+ Sounds.toggleSound(false);
+ ArrayVisualizer.setSortingThread(null);
+ }
+ });
+ ArrayVisualizer.runSortingThread();
+ }
+}
\ No newline at end of file
diff --git a/src/threads/RunExchangeSorts.java b/src/threads/RunExchangeSorts.java
new file mode 100644
index 00000000..5926f30c
--- /dev/null
+++ b/src/threads/RunExchangeSorts.java
@@ -0,0 +1,144 @@
+package threads;
+
+import main.ArrayVisualizer;
+import sorts.BinaryGnomeSort;
+import sorts.BubbleSort;
+import sorts.CircleSort;
+import sorts.CocktailShakerSort;
+import sorts.CombSort;
+import sorts.DualPivotQuickSort;
+import sorts.GnomeSort;
+import sorts.LLQuickSort;
+import sorts.LRQuickSort;
+import sorts.OddEvenSort;
+import sorts.SmartBubbleSort;
+import sorts.SmartCocktailSort;
+import sorts.SmartGnomeSort;
+import sorts.StableQuickSort;
+import templates.JErrorPane;
+import templates.MultipleSortThread;
+import templates.Sort;
+import utils.Shuffles;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class RunExchangeSorts extends MultipleSortThread {
+ private Sort BubbleSort;
+ private Sort SmartBubbleSort;
+ private Sort CocktailShakerSort;
+ private Sort SmartCocktailSort;
+ private Sort OddEvenSort;
+ private Sort GnomeSort;
+ private Sort SmartGnomeSort;
+ private Sort BinaryGnomeSort;
+ private Sort CombSort;
+ private Sort CircleSort;
+ private Sort LLQuickSort;
+ private Sort LRQuickSort;
+ private Sort DualPivotQuickSort;
+ private Sort StableQuickSort;
+
+ public RunExchangeSorts(ArrayVisualizer ArrayVisualizer) {
+ super(ArrayVisualizer);
+ this.sortCount = 14;
+ this.categoryCount = this.sortCount;
+
+ BubbleSort = new BubbleSort(Delays, Highlights, Reads, Writes);
+ SmartBubbleSort = new SmartBubbleSort(Delays, Highlights, Reads, Writes);
+ CocktailShakerSort = new CocktailShakerSort(Delays, Highlights, Reads, Writes);
+ SmartCocktailSort = new SmartCocktailSort(Delays, Highlights, Reads, Writes);
+ OddEvenSort = new OddEvenSort(Delays, Highlights, Reads, Writes);
+ GnomeSort = new GnomeSort(Delays, Highlights, Reads, Writes);
+ SmartGnomeSort = new SmartGnomeSort(Delays, Highlights, Reads, Writes);
+ BinaryGnomeSort = new BinaryGnomeSort(Delays, Highlights, Reads, Writes);
+ CombSort = new CombSort(Delays, Highlights, Reads, Writes);
+ CircleSort = new CircleSort(Delays, Highlights, Reads, Writes);
+ LLQuickSort = new LLQuickSort(Delays, Highlights, Reads, Writes);
+ LRQuickSort = new LRQuickSort(Delays, Highlights, Reads, Writes);
+ DualPivotQuickSort = new DualPivotQuickSort(Delays, Highlights, Reads, Writes);
+ StableQuickSort = new StableQuickSort(Delays, Highlights, Reads, Writes);
+ }
+
+ @Override
+ protected synchronized void executeSortList(int[] array) throws Exception {
+ RunExchangeSorts.this.runIndividualSort(BubbleSort, 0, array, 512, 1.5);
+ RunExchangeSorts.this.runIndividualSort(SmartBubbleSort, 0, array, 512, 1.5);
+ RunExchangeSorts.this.runIndividualSort(CocktailShakerSort, 0, array, 512, 1.25);
+ RunExchangeSorts.this.runIndividualSort(SmartCocktailSort, 0, array, 512, 1.25);
+ RunExchangeSorts.this.runIndividualSort(OddEvenSort, 0, array, 512, 1);
+ RunExchangeSorts.this.runIndividualSort(GnomeSort, 0, array, 128, 0.025);
+ RunExchangeSorts.this.runIndividualSort(SmartGnomeSort, 0, array, 128, 0.025);
+ RunExchangeSorts.this.runIndividualSort(BinaryGnomeSort, 0, array, 128, 0.025);
+ RunExchangeSorts.this.runIndividualSort(CombSort, 0, array, 1024, 1);
+ RunExchangeSorts.this.runIndividualSort(CircleSort, 0, array, 1024, 1);
+ RunExchangeSorts.this.runIndividualSort(LLQuickSort, 0, array, 2048, ArrayManager.getShuffle() == Shuffles.RANDOM ? 1.5 : 65);
+ RunExchangeSorts.this.runIndividualSort(LRQuickSort, 0, array, 2048, 1);
+ RunExchangeSorts.this.runIndividualSort(DualPivotQuickSort, 0, array, 2048, ArrayManager.getShuffle() == Shuffles.RANDOM ? 1 : 50);
+ RunExchangeSorts.this.runIndividualSort(StableQuickSort, 0, array, 2048, ArrayManager.getShuffle() == Shuffles.RANDOM ? 1 : 50);
+ }
+
+ @Override
+ protected synchronized void runThread(int[] array, int current, int total, boolean runAllActive) throws Exception {
+ if(ArrayVisualizer.getSortingThread() != null && ArrayVisualizer.getSortingThread().isAlive())
+ return;
+
+ Sounds.toggleSound(true);
+ ArrayVisualizer.setSortingThread(new Thread() {
+ @Override
+ public void run() {
+ try{
+ if(runAllActive) {
+ RunExchangeSorts.this.sortNumber = current;
+ RunExchangeSorts.this.sortCount = total;
+ }
+ else {
+ RunExchangeSorts.this.sortNumber = 1;
+ }
+
+ ArrayManager.toggleMutableLength(false);
+
+ ArrayVisualizer.setCategory("Exchange Sorts");
+
+ RunExchangeSorts.this.executeSortList(array);
+
+ if(!runAllActive) {
+ ArrayVisualizer.setCategory("Run Exchange Sorts");
+ ArrayVisualizer.setHeading("Done");
+ }
+
+ ArrayManager.toggleMutableLength(true);
+ }
+ catch (Exception e) {
+ JErrorPane.invokeErrorMessage(e);
+ }
+ Sounds.toggleSound(false);
+ ArrayVisualizer.setSortingThread(null);
+ }
+ });
+ ArrayVisualizer.runSortingThread();
+ }
+}
\ No newline at end of file
diff --git a/src/threads/RunHybridSorts.java b/src/threads/RunHybridSorts.java
new file mode 100644
index 00000000..3ca2a196
--- /dev/null
+++ b/src/threads/RunHybridSorts.java
@@ -0,0 +1,144 @@
+package threads;
+
+import main.ArrayVisualizer;
+import sorts.BinaryMergeSort;
+import sorts.BottomUpMergeSort;
+import sorts.BranchedPDQSort;
+import sorts.BranchlessPDQSort;
+import sorts.CocktailMergeSort;
+import sorts.GrailSort;
+import sorts.HybridCombSort;
+import sorts.IntroCircleSort;
+import sorts.IntroSort;
+import sorts.OptimizedDualPivotQuickSort;
+import sorts.SqrtSort;
+import sorts.TimSort;
+import sorts.WeaveMergeSort;
+import sorts.WikiSort;
+import templates.JErrorPane;
+import templates.MultipleSortThread;
+import templates.Sort;
+import utils.Shuffles;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class RunHybridSorts extends MultipleSortThread {
+ private Sort HybridCombSort;
+ private Sort IntroCircleSort;
+ private Sort BinaryMergeSort;
+ private Sort WeaveMergeSort;
+ private Sort TimSort;
+ private Sort CocktailMergeSort;
+ private Sort WikiSort;
+ private Sort GrailSort;
+ private Sort SqrtSort;
+ private Sort IntroSort;
+ private Sort BottomUpMergeSort;
+ private Sort OptimizedDualPivotQuickSort;
+ private Sort BranchedPDQSort;
+ private Sort BranchlessPDQSort;
+
+ public RunHybridSorts(ArrayVisualizer ArrayVisualizer) {
+ super(ArrayVisualizer);
+ this.sortCount = 14;
+ this.categoryCount = this.sortCount;
+
+ HybridCombSort = new HybridCombSort(Delays, Highlights, Reads, Writes);
+ IntroCircleSort = new IntroCircleSort(Delays, Highlights, Reads, Writes);
+ BinaryMergeSort = new BinaryMergeSort(Delays, Highlights, Reads, Writes);
+ WeaveMergeSort = new WeaveMergeSort(Delays, Highlights, Reads, Writes);
+ TimSort = new TimSort(Delays, Highlights, Reads, Writes);
+ CocktailMergeSort = new CocktailMergeSort(Delays, Highlights, Reads, Writes);
+ WikiSort = new WikiSort(Delays, Highlights, Reads, Writes);
+ GrailSort = new GrailSort(Delays, Highlights, Reads, Writes);
+ SqrtSort = new SqrtSort(Delays, Highlights, Reads, Writes);
+ IntroSort = new IntroSort(Delays, Highlights, Reads, Writes);
+ BottomUpMergeSort = new BottomUpMergeSort(Delays, Highlights, Reads, Writes);
+ OptimizedDualPivotQuickSort = new OptimizedDualPivotQuickSort(Delays, Highlights, Reads, Writes);
+ BranchedPDQSort = new BranchedPDQSort(Delays, Highlights, Reads, Writes);
+ BranchlessPDQSort = new BranchlessPDQSort(Delays, Highlights, Reads, Writes);
+ }
+
+ @Override
+ protected synchronized void executeSortList(int[] array) throws Exception {
+ RunHybridSorts.this.runIndividualSort(HybridCombSort, 0, array, 1024, 1);
+ RunHybridSorts.this.runIndividualSort(IntroCircleSort, 0, array, 1024, 1);
+ RunHybridSorts.this.runIndividualSort(BinaryMergeSort, 0, array, 2048, 1);
+ RunHybridSorts.this.runIndividualSort(WeaveMergeSort, 0, array, 2048, ArrayManager.getShuffle() == Shuffles.RANDOM ? 1.25 : 6);
+ RunHybridSorts.this.runIndividualSort(TimSort, 0, array, 2048, 1);
+ RunHybridSorts.this.runIndividualSort(CocktailMergeSort, 0, array, 2048, 1);
+ RunHybridSorts.this.runIndividualSort(WikiSort, 0, array, 2048, 1);
+ RunHybridSorts.this.runIndividualSort(GrailSort, 0, array, 2048, 1);
+ RunHybridSorts.this.runIndividualSort(SqrtSort, 0, array, 2048, 1);
+ RunHybridSorts.this.runIndividualSort(IntroSort, 0, array, 2048, 1);
+ RunHybridSorts.this.runIndividualSort(BottomUpMergeSort, 0, array, 2048, 1);
+ RunHybridSorts.this.runIndividualSort(OptimizedDualPivotQuickSort, 0, array, 2048, 0.75);
+ RunHybridSorts.this.runIndividualSort(BranchedPDQSort, 0, array, 2048, 0.75);
+ RunHybridSorts.this.runIndividualSort(BranchlessPDQSort, 0, array, 2048, 0.75);
+ }
+
+ @Override
+ protected synchronized void runThread(int[] array, int current, int total, boolean runAllActive) throws Exception {
+ if(ArrayVisualizer.getSortingThread() != null && ArrayVisualizer.getSortingThread().isAlive())
+ return;
+
+ Sounds.toggleSound(true);
+ ArrayVisualizer.setSortingThread(new Thread() {
+ @Override
+ public void run() {
+ try{
+ if(runAllActive) {
+ RunHybridSorts.this.sortNumber = current;
+ RunHybridSorts.this.sortCount = total;
+ }
+ else {
+ RunHybridSorts.this.sortNumber = 1;
+ }
+
+ ArrayManager.toggleMutableLength(false);
+
+ ArrayVisualizer.setCategory("Hybrid Sorts");
+
+ RunHybridSorts.this.executeSortList(array);
+
+ if(!runAllActive) {
+ ArrayVisualizer.setCategory("Run Hybrid Sorts");
+ ArrayVisualizer.setHeading("Done");
+ }
+
+ ArrayManager.toggleMutableLength(true);
+ }
+ catch (Exception e) {
+ JErrorPane.invokeErrorMessage(e);
+ }
+ Sounds.toggleSound(false);
+ ArrayVisualizer.setSortingThread(null);
+ }
+ });
+ ArrayVisualizer.runSortingThread();
+ }
+}
\ No newline at end of file
diff --git a/src/threads/RunImpracticalSorts.java b/src/threads/RunImpracticalSorts.java
new file mode 100644
index 00000000..69da1159
--- /dev/null
+++ b/src/threads/RunImpracticalSorts.java
@@ -0,0 +1,128 @@
+package threads;
+
+import main.ArrayVisualizer;
+import sorts.BadSort;
+import sorts.BogoSort;
+import sorts.BubbleBogoSort;
+import sorts.CocktailBogoSort;
+import sorts.ExchangeBogoSort;
+import sorts.LessBogoSort;
+import sorts.SillySort;
+import sorts.SlowSort;
+import sorts.StoogeSort;
+import templates.JErrorPane;
+import templates.MultipleSortThread;
+import templates.Sort;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class RunImpracticalSorts extends MultipleSortThread {
+ private Sort BadSort;
+ private Sort StoogeSort;
+ private Sort SillySort;
+ private Sort SlowSort;
+ private Sort ExchangeBogoSort;
+ private Sort BubbleBogoSort;
+ private Sort LessBogoSort;
+ private Sort CocktailBogoSort;
+ private Sort BogoSort;
+
+ public RunImpracticalSorts(ArrayVisualizer ArrayVisualizer) {
+ super(ArrayVisualizer);
+ this.sortCount = 9;
+ this.categoryCount = this.sortCount;
+
+ BadSort = new BadSort(Delays, Highlights, Reads, Writes);
+ StoogeSort = new StoogeSort(Delays, Highlights, Reads, Writes);
+ SillySort = new SillySort(Delays, Highlights, Reads, Writes);
+ SlowSort = new SlowSort(Delays, Highlights, Reads, Writes);
+ ExchangeBogoSort = new ExchangeBogoSort(Delays, Highlights, Reads, Writes);
+ BubbleBogoSort = new BubbleBogoSort(Delays, Highlights, Reads, Writes);
+ LessBogoSort = new LessBogoSort(Delays, Highlights, Reads, Writes);
+ CocktailBogoSort = new CocktailBogoSort(Delays, Highlights, Reads, Writes);
+ BogoSort = new BogoSort(Delays, Highlights, Reads, Writes);
+ }
+
+ @Override
+ protected synchronized void executeSortList(int[] array) throws Exception {
+ RunImpracticalSorts.this.runIndividualSort(BadSort, 0, array, 64, 0.0075);
+ RunImpracticalSorts.this.runIndividualSort(StoogeSort, 0, array, 64, 0.005);
+ RunImpracticalSorts.this.runIndividualSort(SillySort, 0, array, 64, 10);
+ RunImpracticalSorts.this.runIndividualSort(SlowSort, 0, array, 64, 10);
+ Sounds.toggleSofterSounds(true);
+ RunImpracticalSorts.this.runIndividualSort(ExchangeBogoSort, 0, array, 32, 0.01);
+ RunImpracticalSorts.this.runIndividualSort(BubbleBogoSort, 0, array, 32, 0.01);
+ RunImpracticalSorts.this.runIndividualSort(LessBogoSort, 0, array, 16, 0.0025);
+ RunImpracticalSorts.this.runIndividualSort(CocktailBogoSort, 0, array, 16, 0.0025);
+ RunImpracticalSorts.this.runIndividualSort(BogoSort, 0, array, 8, 1);
+ Sounds.toggleSofterSounds(false);
+ }
+
+ @Override
+ protected synchronized void runThread(int[] array, int current, int total, boolean runAllActive) throws Exception {
+ if(ArrayVisualizer.getSortingThread() != null && ArrayVisualizer.getSortingThread().isAlive())
+ return;
+
+ Sounds.toggleSound(true);
+ ArrayVisualizer.setSortingThread(new Thread() {
+ @Override
+ public void run() {
+ try{
+ if(runAllActive) {
+ RunImpracticalSorts.this.sortNumber = current;
+ RunImpracticalSorts.this.sortCount = total;
+ }
+ else {
+ RunImpracticalSorts.this.sortNumber = 1;
+ }
+
+ ArrayManager.toggleMutableLength(false);
+
+ ArrayVisualizer.setCategory("Impractical Sorts");
+
+ RunImpracticalSorts.this.executeSortList(array);
+
+ if(runAllActive) {
+ Thread.sleep(3000);
+ }
+ else {
+ ArrayVisualizer.setCategory("Run Impractical Sorts");
+ ArrayVisualizer.setHeading("Done");
+ }
+
+ ArrayManager.toggleMutableLength(true);
+ }
+ catch (Exception e) {
+ JErrorPane.invokeErrorMessage(e);
+ }
+ Sounds.toggleSound(false);
+ ArrayVisualizer.setSortingThread(null);
+ }
+ });
+ ArrayVisualizer.runSortingThread();
+ }
+}
\ No newline at end of file
diff --git a/src/threads/RunInsertionSorts.java b/src/threads/RunInsertionSorts.java
new file mode 100644
index 00000000..0a850f5e
--- /dev/null
+++ b/src/threads/RunInsertionSorts.java
@@ -0,0 +1,108 @@
+package threads;
+
+import main.ArrayVisualizer;
+import sorts.BinaryInsertionSort;
+import sorts.InsertionSort;
+import sorts.PatienceSort;
+import sorts.ShellSort;
+import templates.JErrorPane;
+import templates.MultipleSortThread;
+import templates.Sort;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class RunInsertionSorts extends MultipleSortThread {
+ private Sort InsertionSort;
+ private Sort BinaryInsertionSort;
+ private Sort ShellSort;
+ private Sort PatienceSort;
+ //private Sort TreeSort;
+
+ public RunInsertionSorts(ArrayVisualizer ArrayVisualizer) {
+ super(ArrayVisualizer);
+ this.sortCount = 4;
+ //5;
+ //TODO: Add Treesort back in when fixed
+ this.categoryCount = this.sortCount;
+
+ InsertionSort = new InsertionSort(Delays, Highlights, Reads, Writes);
+ BinaryInsertionSort = new BinaryInsertionSort(Delays, Highlights, Reads, Writes);
+ ShellSort = new ShellSort(Delays, Highlights, Reads, Writes);
+ PatienceSort = new PatienceSort(Delays, Highlights, Reads, Writes);
+ //Sort TreeSort = new TreeSort(Delays, Highlights, Reads, Writes);
+ }
+
+ @Override
+ protected synchronized void executeSortList(int[] array) throws Exception {
+ RunInsertionSorts.this.runIndividualSort(InsertionSort, 0, array, 128, 0.005);
+ RunInsertionSorts.this.runIndividualSort(BinaryInsertionSort, 0, array, 128, 0.025);
+ RunInsertionSorts.this.runIndividualSort(ShellSort, 0, array, 256, 0.1);
+ RunInsertionSorts.this.runIndividualSort(PatienceSort, 0, array, 2048, 1);
+ //RunInsertionSorts.this.RunIndividualSort(TreeSort, 0, array, 2048, 1);
+ }
+
+ @Override
+ protected synchronized void runThread(int[] array, int current, int total, boolean runAllActive) throws Exception {
+ if(ArrayVisualizer.getSortingThread() != null && ArrayVisualizer.getSortingThread().isAlive())
+ return;
+
+ Sounds.toggleSound(true);
+ ArrayVisualizer.setSortingThread(new Thread() {
+ @Override
+ public void run() {
+ try{
+ if(runAllActive) {
+ RunInsertionSorts.this.sortNumber = current;
+ RunInsertionSorts.this.sortCount = total;
+ }
+ else {
+ RunInsertionSorts.this.sortNumber = 1;
+ }
+
+ ArrayManager.toggleMutableLength(false);
+
+ ArrayVisualizer.setCategory("Insertion Sorts");
+
+ RunInsertionSorts.this.executeSortList(array);
+
+ if(!runAllActive) {
+ ArrayVisualizer.setCategory("Run Insertion Sorts");
+ ArrayVisualizer.setHeading("Done");
+ }
+
+ ArrayManager.toggleMutableLength(true);
+ }
+ catch (Exception e) {
+ JErrorPane.invokeErrorMessage(e);
+ }
+ Sounds.toggleSound(false);
+ ArrayVisualizer.setSortingThread(null);
+ }
+ });
+ ArrayVisualizer.runSortingThread();
+ }
+}
\ No newline at end of file
diff --git a/src/threads/RunMergeSorts.java b/src/threads/RunMergeSorts.java
new file mode 100644
index 00000000..e52a26c9
--- /dev/null
+++ b/src/threads/RunMergeSorts.java
@@ -0,0 +1,103 @@
+package threads;
+
+import main.ArrayVisualizer;
+import sorts.InPlaceMergeSort;
+import sorts.LazyStableSort;
+import sorts.MergeSort;
+import sorts.RotateMergeSort;
+import templates.JErrorPane;
+import templates.MultipleSortThread;
+import templates.Sort;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class RunMergeSorts extends MultipleSortThread {
+ private Sort MergeSort;
+ private Sort InPlaceMergeSort;
+ private Sort LazyStableSort;
+ private Sort RotateMergeSort;
+
+ public RunMergeSorts(ArrayVisualizer ArrayVisualizer) {
+ super(ArrayVisualizer);
+ this.sortCount = 4;
+ this.categoryCount = this.sortCount;
+
+ MergeSort = new MergeSort(Delays, Highlights, Reads, Writes);
+ InPlaceMergeSort = new InPlaceMergeSort(Delays, Highlights, Reads, Writes);
+ LazyStableSort = new LazyStableSort(Delays, Highlights, Reads, Writes);
+ RotateMergeSort = new RotateMergeSort(Delays, Highlights, Reads, Writes);
+ }
+
+ @Override
+ protected synchronized void executeSortList(int[] array) throws Exception {
+ RunMergeSorts.this.runIndividualSort(MergeSort, 0, array, 2048, 1.5);
+ RunMergeSorts.this.runIndividualSort(InPlaceMergeSort, 0, array, 2048, 1.75);
+ RunMergeSorts.this.runIndividualSort(LazyStableSort, 0, array, 256, 0.2);
+ RunMergeSorts.this.runIndividualSort(RotateMergeSort, 0, array, 512, 0.2);
+ }
+
+ @Override
+ protected synchronized void runThread(int[] array, int current, int total, boolean runAllActive) throws Exception {
+ if(ArrayVisualizer.getSortingThread() != null && ArrayVisualizer.getSortingThread().isAlive())
+ return;
+
+ Sounds.toggleSound(true);
+ ArrayVisualizer.setSortingThread(new Thread() {
+ @Override
+ public void run() {
+ try{
+ if(runAllActive) {
+ RunMergeSorts.this.sortNumber = current;
+ RunMergeSorts.this.sortCount = total;
+ }
+ else {
+ RunMergeSorts.this.sortNumber = 1;
+ }
+
+ ArrayManager.toggleMutableLength(false);
+
+ ArrayVisualizer.setCategory("Merge Sorts");
+
+ RunMergeSorts.this.executeSortList(array);
+
+ if(!runAllActive) {
+ ArrayVisualizer.setCategory("Run Merge Sorts");
+ ArrayVisualizer.setHeading("Done");
+ }
+
+ ArrayManager.toggleMutableLength(true);
+ }
+ catch (Exception e) {
+ JErrorPane.invokeErrorMessage(e);
+ }
+ Sounds.toggleSound(false);
+ ArrayVisualizer.setSortingThread(null);
+ }
+ });
+ ArrayVisualizer.runSortingThread();
+ }
+}
\ No newline at end of file
diff --git a/src/threads/RunMiscellaneousSorts.java b/src/threads/RunMiscellaneousSorts.java
new file mode 100644
index 00000000..3f782a90
--- /dev/null
+++ b/src/threads/RunMiscellaneousSorts.java
@@ -0,0 +1,91 @@
+package threads;
+
+import main.ArrayVisualizer;
+import sorts.PancakeSort;
+import templates.JErrorPane;
+import templates.MultipleSortThread;
+import templates.Sort;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class RunMiscellaneousSorts extends MultipleSortThread {
+ private Sort PancakeSort;
+
+ public RunMiscellaneousSorts(ArrayVisualizer ArrayVisualizer) {
+ super(ArrayVisualizer);
+ this.sortCount = 1;
+ this.categoryCount = this.sortCount;
+
+ PancakeSort = new PancakeSort(Delays, Highlights, Reads, Writes);
+ }
+
+ @Override
+ protected synchronized void executeSortList(int[] array) throws Exception {
+ RunMiscellaneousSorts.this.runIndividualSort(PancakeSort, 0, array, 128, 0.015);
+ }
+
+ @Override
+ protected synchronized void runThread(int[] array, int current, int total, boolean runAllActive) throws Exception {
+ if(ArrayVisualizer.getSortingThread() != null && ArrayVisualizer.getSortingThread().isAlive())
+ return;
+
+ Sounds.toggleSound(true);
+ ArrayVisualizer.setSortingThread(new Thread() {
+ @Override
+ public void run() {
+ try{
+ if(runAllActive) {
+ RunMiscellaneousSorts.this.sortNumber = current;
+ RunMiscellaneousSorts.this.sortCount = total;
+ }
+ else {
+ RunMiscellaneousSorts.this.sortNumber = 1;
+ }
+
+ ArrayManager.toggleMutableLength(false);
+
+ ArrayVisualizer.setCategory("Miscellaneous Sorts");
+
+ RunMiscellaneousSorts.this.executeSortList(array);
+
+ if(!runAllActive) {
+ ArrayVisualizer.setCategory("Run Miscellaneous Sorts");
+ ArrayVisualizer.setHeading("Done");
+ }
+
+ ArrayManager.toggleMutableLength(true);
+ }
+ catch (Exception e) {
+ JErrorPane.invokeErrorMessage(e);
+ }
+ Sounds.toggleSound(false);
+ ArrayVisualizer.setSortingThread(null);
+ }
+ });
+ ArrayVisualizer.runSortingThread();
+ }
+}
\ No newline at end of file
diff --git a/src/threads/RunSelectionSorts.java b/src/threads/RunSelectionSorts.java
new file mode 100644
index 00000000..16b76546
--- /dev/null
+++ b/src/threads/RunSelectionSorts.java
@@ -0,0 +1,131 @@
+package threads;
+
+import main.ArrayVisualizer;
+import sorts.CycleSort;
+import sorts.DoubleSelectionSort;
+import sorts.FlippedMinHeapSort;
+import sorts.MaxHeapSort;
+import sorts.MinHeapSort;
+import sorts.PoplarHeapSort;
+import sorts.SelectionSort;
+import sorts.SmoothSort;
+import sorts.TernaryHeapSort;
+import sorts.TournamentSort;
+import sorts.WeakHeapSort;
+import templates.JErrorPane;
+import templates.MultipleSortThread;
+import templates.Sort;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class RunSelectionSorts extends MultipleSortThread {
+ private Sort SelectionSort;
+ private Sort DoubleSelectionSort;
+ private Sort CycleSort;
+ private Sort MaxHeapSort;
+ private Sort MinHeapSort;
+ private Sort FlippedMinHeapSort;
+ private Sort WeakHeapSort;
+ private Sort TernaryHeapSort;
+ private Sort SmoothSort;
+ private Sort PoplarHeapSort;
+ private Sort TournamentSort;
+
+ public RunSelectionSorts(ArrayVisualizer ArrayVisualizer) {
+ super(ArrayVisualizer);
+ this.sortCount = 11;
+ this.categoryCount = this.sortCount;
+
+ SelectionSort = new SelectionSort(Delays, Highlights, Reads, Writes);
+ DoubleSelectionSort = new DoubleSelectionSort(Delays, Highlights, Reads, Writes);
+ CycleSort = new CycleSort(Delays, Highlights, Reads, Writes);
+ MaxHeapSort = new MaxHeapSort(Delays, Highlights, Reads, Writes);
+ MinHeapSort = new MinHeapSort(Delays, Highlights, Reads, Writes);
+ FlippedMinHeapSort = new FlippedMinHeapSort(Delays, Highlights, Reads, Writes);
+ WeakHeapSort = new WeakHeapSort(Delays, Highlights, Reads, Writes);
+ TernaryHeapSort = new TernaryHeapSort(Delays, Highlights, Reads, Writes);
+ SmoothSort = new SmoothSort(Delays, Highlights, Reads, Writes);
+ PoplarHeapSort = new PoplarHeapSort(Delays, Highlights, Reads, Writes);
+ TournamentSort = new TournamentSort(Delays, Highlights, Reads, Writes);
+ }
+
+ @Override
+ protected synchronized void executeSortList(int[] array) throws Exception {
+ RunSelectionSorts.this.runIndividualSort(SelectionSort, 0, array, 128, 0.01);
+ RunSelectionSorts.this.runIndividualSort(DoubleSelectionSort, 0, array, 128, 0.01);
+ RunSelectionSorts.this.runIndividualSort(CycleSort, 0, array, 128, 0.01);
+ RunSelectionSorts.this.runIndividualSort(MaxHeapSort, 0, array, 2048, 1.5);
+ RunSelectionSorts.this.runIndividualSort(MinHeapSort, 0, array, 2048, 1.5);
+ RunSelectionSorts.this.runIndividualSort(FlippedMinHeapSort, 0, array, 2048, 1.5);
+ RunSelectionSorts.this.runIndividualSort(WeakHeapSort, 0, array, 2048, 1);
+ RunSelectionSorts.this.runIndividualSort(TernaryHeapSort, 0, array, 2048, 1);
+ RunSelectionSorts.this.runIndividualSort(SmoothSort, 0, array, 2048, 1.5);
+ RunSelectionSorts.this.runIndividualSort(PoplarHeapSort, 0, array, 2048, 1);
+ RunSelectionSorts.this.runIndividualSort(TournamentSort, 0, array, 2048, 1.5);
+ }
+
+ @Override
+ protected synchronized void runThread(int[] array, int current, int total, boolean runAllActive) throws Exception {
+ if(ArrayVisualizer.getSortingThread() != null && ArrayVisualizer.getSortingThread().isAlive())
+ return;
+
+ Sounds.toggleSound(true);
+ ArrayVisualizer.setSortingThread(new Thread() {
+ @Override
+ public void run() {
+ try{
+ if(runAllActive) {
+ RunSelectionSorts.this.sortNumber = current;
+ RunSelectionSorts.this.sortCount = total;
+ }
+ else {
+ RunSelectionSorts.this.sortNumber = 1;
+ }
+
+ ArrayManager.toggleMutableLength(false);
+
+ ArrayVisualizer.setCategory("Selection Sorts");
+
+ RunSelectionSorts.this.executeSortList(array);
+
+ if(!runAllActive) {
+ ArrayVisualizer.setCategory("Run Selection Sorts");
+ ArrayVisualizer.setHeading("Done");
+ }
+
+ ArrayManager.toggleMutableLength(true);
+ }
+ catch (Exception e) {
+ JErrorPane.invokeErrorMessage(e);
+ }
+ Sounds.toggleSound(false);
+ ArrayVisualizer.setSortingThread(null);
+ }
+ });
+ ArrayVisualizer.runSortingThread();
+ }
+}
\ No newline at end of file
diff --git a/src/utils/Delays.java b/src/utils/Delays.java
new file mode 100644
index 00000000..1d224c37
--- /dev/null
+++ b/src/utils/Delays.java
@@ -0,0 +1,142 @@
+package utils;
+
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.NumberFormat;
+import java.util.Locale;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import main.ArrayVisualizer;
+import templates.JErrorPane;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class Delays {
+ private volatile double SLEEPRATIO;
+ private volatile boolean SKIPPED;
+
+ private double addamt;
+ private double delay;
+ private double nanos;
+
+ private volatile double currentDelay;
+
+ private DecimalFormat formatter;
+ private DecimalFormatSymbols symbols;
+
+ private Sounds Sounds;
+
+ public Delays(ArrayVisualizer ArrayVisualizer) {
+ this.SLEEPRATIO = 1.0;
+ this.SKIPPED = false;
+ this.addamt = 0;
+
+ this.formatter = (DecimalFormat) NumberFormat.getInstance(Locale.US);
+ this.symbols = this.formatter.getDecimalFormatSymbols();
+
+ this.symbols.setGroupingSeparator(',');
+ this.formatter.setDecimalFormatSymbols(this.symbols);
+
+ this.Sounds = ArrayVisualizer.getSounds();
+ }
+
+ public String displayCurrentDelay() {
+ String currDelay = "";
+
+ if(this.currentDelay == 0 || this.SKIPPED) {
+ currDelay = "0";
+ }
+ else if(this.currentDelay < 0.001) {
+ currDelay = "< 0.001";
+ }
+ else {
+ currDelay = formatter.format(this.currentDelay);
+ }
+
+ return currDelay;
+ }
+ public double getCurrentDelay() {
+ return this.currentDelay;
+ }
+ public void setCurrentDelay(double value) {
+ this.delay = value;
+ }
+ public void updateCurrentDelay(double oldRatio, double newRatio) {
+ this.delay = (this.delay * oldRatio) / newRatio;
+ this.currentDelay = this.delay;
+ this.Sounds.changeNoteDelayAndFilter((int) this.currentDelay);
+ this.addamt = 0;
+
+ if(this.currentDelay < 0) {
+ this.delay = this.currentDelay = 0;
+ }
+ }
+
+ public double getSleepRatio() {
+ return this.SLEEPRATIO;
+ }
+ public void setSleepRatio(double sleepRatio) {
+ this.SLEEPRATIO = sleepRatio;
+ }
+
+ public boolean skipped() {
+ return this.SKIPPED;
+ }
+ public void changeSkipped(boolean Bool) {
+ this.SKIPPED = Bool;
+ if(this.SKIPPED) this.Sounds.changeNoteDelayAndFilter(1);
+ }
+
+ public void sleep(double millis){
+ if(millis <= 0) {
+ return;
+ }
+
+ this.delay += (millis * (1 / this.SLEEPRATIO));
+ this.currentDelay = (millis * (1 / this.SLEEPRATIO));
+
+ this.Sounds.changeNoteDelayAndFilter((int) this.currentDelay);
+
+ try {
+ // With this for loop, you can change the speed of sorts without waiting for the current delay to finish.
+ if(!this.SKIPPED) {
+ while(this.delay >= 1) {
+ Thread.sleep(1);
+ this.delay--;
+ }
+ }
+ else {
+ this.delay = 0;
+ }
+ } catch(Exception ex) {
+ JErrorPane.invokeErrorMessage(ex);
+ }
+
+ this.currentDelay = 0;
+ }
+}
\ No newline at end of file
diff --git a/src/utils/Highlights.java b/src/utils/Highlights.java
new file mode 100644
index 00000000..0b16c65c
--- /dev/null
+++ b/src/utils/Highlights.java
@@ -0,0 +1,163 @@
+package utils;
+
+import java.util.Arrays;
+
+import main.ArrayVisualizer;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class Highlights {
+ private volatile int[] Highlights;
+
+ private volatile int maxIndexMarked; // The Highlights array is huge and slows down the visualizer if all its indices are read.
+ // In an attempt to speed up the containsPosition() method while also giving anyone room
+ // to use the full array, this variable keeps track of the farthest index an array position
+ // has been highlighted at. The containsPosition() method only scans the Highlights array
+ // up to index maxIndexMarked.
+
+ // If an index is used in markArray() that is higher than maxPossibleMarked, the variable
+ // is updated. If the highest index used in Highlights is removed with clearMark(), the
+ // next biggest index (less than what was removed) is found and updates maxIndexMarked.
+
+ // Trivially, clearAllMarks() resets maxIndexMarked to zero. This variable also serves
+ // as a subtle design hint for anyone who wants to add an algorithm to the app to highlight
+ // array positions at low indices which are close together.
+
+ // This way, the program runs more efficiently, and looks pretty. :)
+
+ private volatile int markCount;
+
+ private boolean FANCYFINISH;
+ private volatile boolean fancyFinish;
+ private volatile int trackFinish;
+
+ private ArrayVisualizer ArrayVisualizer;
+
+ public Highlights(ArrayVisualizer ArrayVisualizer, int maximumLength) {
+ this.ArrayVisualizer = ArrayVisualizer;
+
+ this.Highlights = new int[maximumLength];
+ this.FANCYFINISH = true;
+ this.maxIndexMarked = 0;
+ this.markCount = 0;
+
+ Arrays.fill(Highlights, -1);
+ }
+
+ public boolean fancyFinishEnabled() {
+ return this.FANCYFINISH;
+ }
+ public void toggleFancyFinishes(boolean Bool) {
+ this.FANCYFINISH = Bool;
+ }
+
+ public boolean fancyFinishActive() {
+ return this.fancyFinish;
+ }
+ public void toggleFancyFinish(boolean Bool) {
+ this.fancyFinish = Bool;
+ }
+
+ public int getFancyFinishPosition() {
+ return this.trackFinish;
+ }
+ public void incrementFancyFinishPosition() {
+ this.trackFinish++;
+ }
+ public void resetFancyFinish() {
+ this.trackFinish = -1; // Magic number that clears the green sweep animation
+ }
+
+ //TODO: Move Analysis to Highlights
+ public void toggleAnalysis(boolean Bool) {
+ this.ArrayVisualizer.toggleAnalysis(Bool);
+ }
+
+ public int getMaxIndex() {
+ return this.maxIndexMarked;
+ }
+ public int getMarkCount() {
+ return this.markCount;
+ }
+
+ //Consider revising highlightList().
+ public int[] highlightList() {
+ return this.Highlights;
+ }
+ public boolean containsPosition(int arrayPosition) {
+ for(int i = 0; i <= this.maxIndexMarked; i++) {
+ if(Highlights[i] == -1) {
+ continue;
+ }
+ else if(Highlights[i] == arrayPosition) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ public 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 {
+ Highlights[marker] = markPosition;
+ this.markCount++;
+
+ if(marker > this.maxIndexMarked) {
+ this.maxIndexMarked = marker;
+ }
+ }
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ public void clearMark(int marker) {
+ Highlights[marker] = -1; // -1 is used as the magic number to unmark a position in the main array
+ this.markCount--;
+
+ if(marker == this.maxIndexMarked) {
+ this.maxIndexMarked = 0;
+
+ for(int i = marker - 1; i >= 0; i--) {
+ if(Highlights[i] != -1) {
+ this.maxIndexMarked = i;
+ break;
+ }
+ }
+ }
+ }
+ public void clearAllMarks() {
+ Arrays.fill(this.Highlights, -1);
+ this.maxIndexMarked = 0;
+ this.markCount = 0;
+ }
+}
\ No newline at end of file
diff --git a/src/utils/Reads.java b/src/utils/Reads.java
new file mode 100644
index 00000000..a2fa1c13
--- /dev/null
+++ b/src/utils/Reads.java
@@ -0,0 +1,189 @@
+package utils;
+
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.NumberFormat;
+import java.util.Locale;
+
+import main.ArrayVisualizer;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class Reads {
+ private volatile long comparisons;
+
+ private ArrayVisualizer ArrayVisualizer;
+
+ private DecimalFormat formatter;
+ private DecimalFormatSymbols symbols;
+
+ private Delays Delays;
+ private Highlights Highlights;
+ private Timer Timer;
+
+ public Reads(ArrayVisualizer arrayVisualizer) {
+ this.ArrayVisualizer = arrayVisualizer;
+
+ this.comparisons = 0;
+
+ this.Delays = ArrayVisualizer.getDelays();
+ this.Highlights = ArrayVisualizer.getHighlights();
+ this.Timer = ArrayVisualizer.getTimer();
+
+ this.formatter = (DecimalFormat) NumberFormat.getInstance(Locale.US);
+ this.symbols = this.formatter.getDecimalFormatSymbols();
+
+ this.symbols.setGroupingSeparator(',');
+ this.formatter.setDecimalFormatSymbols(this.symbols);
+ }
+
+ public void resetStatistics() {
+ this.comparisons = 0;
+ }
+
+ public void addComparison() {
+ this.comparisons++;
+ }
+
+ public String displayComparisons() {
+ if(this.comparisons < 0) {
+ this.comparisons = Long.MIN_VALUE;
+ return "Over " + this.formatter.format(Long.MAX_VALUE);
+ }
+ else {
+ if(this.comparisons == 1) return this.comparisons + " Comparison";
+ else return this.formatter.format(this.comparisons) + " Comparisons";
+ }
+ }
+
+ public long getComparisons() {
+ return this.comparisons;
+ }
+
+ public void setComparisons(long value) {
+ this.comparisons = value;
+ }
+
+ public int compare(int left, int right) {
+ this.comparisons++;
+
+ int cmpVal = 0;
+
+ Timer.startLap();
+
+ if(left > right) cmpVal = 1;
+ else if(left < right) cmpVal = -1;
+ else cmpVal = 0;
+
+ Timer.stopLap();
+
+ return cmpVal;
+ }
+
+ public int analyzeMax(int[] array, int length, double sleep, boolean mark) {
+ ArrayVisualizer.toggleAnalysis(true);
+
+ int max = 0;
+
+ for(int i = 0; i < length; i++) {
+ Timer.startLap();
+
+ if(array[i] > max) max = array[i];
+
+ Timer.stopLap();
+
+ if(mark) {
+ Highlights.markArray(1, i);
+ Delays.sleep(sleep);
+ }
+ }
+
+ ArrayVisualizer.toggleAnalysis(false);
+
+ return max;
+ }
+
+ public int analyzeMaxLog(int[] array, int length, int base, double sleep, boolean mark) {
+ ArrayVisualizer.toggleAnalysis(true);
+
+ int max = 0;
+
+ for(int i = 0; i < length; i++) {
+ int log = (int) (Math.log(array[i]) / Math.log(base));
+
+ Timer.startLap();
+
+ if(log > max) max = log;
+
+ Timer.stopLap();
+
+ if(mark) {
+ Highlights.markArray(1, i);
+ Delays.sleep(sleep);
+ }
+ }
+
+ ArrayVisualizer.toggleAnalysis(false);
+
+ return max;
+ }
+
+ public int analyzeBit(int[] array, int length) {
+ ArrayVisualizer.toggleAnalysis(true);
+
+ // Find highest bit of highest value
+ int max = 0;
+
+ for(int i = 0; i < length; i++) {
+ Timer.startLap();
+
+ max = Math.max(max, array[i]);
+
+ Timer.stopLap();
+
+ Highlights.markArray(1, i);
+ Delays.sleep(0.75);
+ }
+ Timer.startLap();
+
+ int analysis = 31 - Integer.numberOfLeadingZeros(max);
+
+ Timer.stopLap();
+
+ ArrayVisualizer.toggleAnalysis(false);
+ return analysis;
+ }
+
+ public int getDigit(int a, int power, int radix) {
+ return (int) (a / Math.pow(radix, power)) % radix;
+ }
+
+ public boolean getBit(int n, int k) {
+ // Find boolean value of bit k in n
+ return ((n >> k) & 1) == 1;
+ }
+}
\ No newline at end of file
diff --git a/src/utils/Renderer.java b/src/utils/Renderer.java
new file mode 100644
index 00000000..a46fa377
--- /dev/null
+++ b/src/utils/Renderer.java
@@ -0,0 +1,190 @@
+package utils;
+
+import java.awt.BasicStroke;
+
+import main.ArrayVisualizer;
+import visuals.VisualStyles;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+// TODO: Many of these methods should exist solely in visual classes
+
+final class WindowState {
+ private boolean windowUpdated;
+ private boolean windowResized;
+
+ public WindowState(boolean windowUpdate, boolean windowResize) {
+ this.windowUpdated = windowUpdate;
+ this.windowResized = windowResize;
+ }
+
+ public boolean updated() {
+ return this.windowUpdated;
+ }
+
+ public boolean resized() {
+ return this.windowResized;
+ }
+}
+
+final public class Renderer {
+ private volatile double xscl; //TODO: Change to xScale/yScale
+ private volatile double yscl;
+
+ private volatile int amt;
+
+ private int linkedpixdrawx; //TODO: Change names
+ private int linkedpixdrawy;
+
+ private int doth; //TODO: Change names
+ private int dotw;
+ private int dots; //TODO: Change name to dotDims/dotDimensions
+
+ public Renderer(ArrayVisualizer ArrayVisualizer) {
+ ArrayVisualizer.setWindowHeight();
+ ArrayVisualizer.setWindowWidth();
+ ArrayVisualizer.setThickStroke(new BasicStroke(8));
+ }
+
+ public double getXScale() {
+ return this.xscl;
+ }
+ public double getYScale() {
+ return this.yscl;
+ }
+ public int getOffset() {
+ return this.amt;
+ }
+ public int getDotWidth() {
+ return this.dotw;
+ }
+ public int getDotHeight() {
+ return this.doth;
+ }
+ public int getDotDimensions() {
+ return this.dots;
+ }
+ public int getLineX() {
+ return this.linkedpixdrawx;
+ }
+ public int getLineY() {
+ return this.linkedpixdrawy;
+ }
+
+ public void setOffset(int amount) {
+ this.amt = amount;
+ }
+ public void setLineX(int x) {
+ this.linkedpixdrawx = x;
+ }
+ public void setLineY(int y) {
+ this.linkedpixdrawy = y;
+ }
+
+ public static void createRenders(ArrayVisualizer ArrayVisualizer) {
+ ArrayVisualizer.createVolatileImage();
+ ArrayVisualizer.setMainRender();
+ ArrayVisualizer.setExtraRender();
+ }
+
+ public static void initializeVisuals(ArrayVisualizer ArrayVisualizer) {
+ Renderer.createRenders(ArrayVisualizer);
+ ArrayVisualizer.updateFontSize();
+ ArrayVisualizer.repositionFrames();
+ }
+
+ public static void updateGraphics(ArrayVisualizer ArrayVisualizer) {
+ Renderer.createRenders(ArrayVisualizer);
+ ArrayVisualizer.updateVisuals();
+ }
+
+ private static WindowState checkWindowResizeAndReposition(ArrayVisualizer ArrayVisualizer) {
+ boolean windowUpdate = false;
+ boolean windowResize = false;
+
+ if(ArrayVisualizer.currentHeight() != ArrayVisualizer.windowHeight()) {
+ windowUpdate = true;
+ windowResize = true;
+ }
+ if(ArrayVisualizer.currentWidth() != ArrayVisualizer.windowWidth()) {
+ windowUpdate = true;
+ windowResize = true;
+ }
+ if(ArrayVisualizer.currentX() != ArrayVisualizer.windowXCoordinate()) {
+ windowUpdate = true;
+ }
+ if(ArrayVisualizer.currentY() != ArrayVisualizer.windowYCoordinate()) {
+ windowUpdate = true;
+ }
+
+ return new WindowState(windowUpdate, windowResize);
+ }
+
+ public void updateVisuals(ArrayVisualizer ArrayVisualizer) {
+ WindowState WindowState = checkWindowResizeAndReposition(ArrayVisualizer);
+
+ if(WindowState.updated()) {
+ ArrayVisualizer.repositionFrames();
+ ArrayVisualizer.updateCoordinates();
+
+ /*
+ if(v != null && v.isVisible())
+ v.reposition();
+ */
+
+ if(WindowState.resized()) {
+ ArrayVisualizer.updateDimensions();
+ updateGraphics(ArrayVisualizer);
+ }
+
+ ArrayVisualizer.updateFontSize();
+ }
+
+ ArrayVisualizer.renderBackground();
+
+ //CURRENT = WINDOW
+ //WINDOW = C VARIABLES
+
+ this.xscl = (double) (ArrayVisualizer.currentWidth() - 40) / ArrayVisualizer.getCurrentLength();
+ this.yscl = (double) (ArrayVisualizer.currentHeight() - 96) / ArrayVisualizer.getCurrentLength();
+
+ this.amt = 0; //TODO: rename to barCount
+
+ this.linkedpixdrawx = 0;
+ this.linkedpixdrawy = 0;
+
+ this.dotw = (int) (2 * (ArrayVisualizer.currentWidth() / 640.0));
+ this.doth = (int) (2 * (ArrayVisualizer.currentHeight() / 480.0));
+ this.dots = (this.dotw + this.doth) / 2; //TODO: Does multiply/divide by 2 like this cancel out??
+
+ ArrayVisualizer.resetMainStroke();
+ }
+
+ public void drawVisual(VisualStyles VisualStyles, int[] array, ArrayVisualizer ArrayVisualizer, Highlights Highlights) {
+ VisualStyles.drawVisual(array, ArrayVisualizer, this, Highlights);
+ }
+}
\ No newline at end of file
diff --git a/src/utils/Shuffles.java b/src/utils/Shuffles.java
new file mode 100644
index 00000000..1e3e6e9e
--- /dev/null
+++ b/src/utils/Shuffles.java
@@ -0,0 +1,126 @@
+package utils;
+
+import main.ArrayVisualizer;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+public enum Shuffles {
+ RANDOM {
+ // If you want to learn why the random shuffle was changed,
+ // I highly encourage you read this. It's quite fascinating:
+ // http://datagenetics.com/blog/november42014/index.html
+
+ @Override
+ public void shuffleArray(int[] array, ArrayVisualizer ArrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) {
+ int currentLen = ArrayVisualizer.getCurrentLength();
+
+ //TODO: Consider separate method
+ for(int i = 0; i < currentLen; i++){
+ int randomIndex = (int) (Math.random() * (currentLen - i)) + i;
+ Writes.swap(array, i, randomIndex, 0, true, false);
+
+ if(ArrayVisualizer.shuffleEnabled()) Delays.sleep(1);
+ }
+ }
+ },
+ REVERSE {
+ @Override
+ public void shuffleArray(int[] array, ArrayVisualizer ArrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) {
+ int currentLen = ArrayVisualizer.getCurrentLength();
+
+ for (int left = 0, right = currentLen - 1; left < right; left++, right--) {
+ // swap the values at the left and right indices
+ Writes.swap(array, left, right, 0, true, false);
+
+ if(ArrayVisualizer.shuffleEnabled()) Delays.sleep(1);
+ }
+ }
+ },
+ SIMILAR {
+ @Override
+ public void shuffleArray(int[] array, ArrayVisualizer ArrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) {
+ int currentLen = ArrayVisualizer.getCurrentLength();
+
+ for(int i = 0; i < currentLen - 8; i++) {
+ array[i] = currentLen / 2;
+
+ if(ArrayVisualizer.shuffleEnabled()) Delays.sleep(1);
+ }
+ for(int i = currentLen - 8; i < currentLen; i++) {
+ array[i] = (int) (Math.random() < 0.5 ? currentLen * 0.75 : currentLen * 0.25);
+
+ if(ArrayVisualizer.shuffleEnabled()) Delays.sleep(1);
+ }
+ for(int i = 0; i < currentLen; i++){
+ int randomIndex = (int) (Math.random() * (currentLen - i)) + i;
+ Writes.swap(array, i, randomIndex, 0, true, false);
+
+ if(ArrayVisualizer.shuffleEnabled()) Delays.sleep(1);
+ }
+ }
+ },
+ ALMOST {
+ @Override
+ public void shuffleArray(int[] array, ArrayVisualizer ArrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) {
+ int currentLen = ArrayVisualizer.getCurrentLength();
+
+ for(int i = 0; i < Math.max(currentLen / 20, 1); i++){
+ Writes.swap(array, (int)(Math.random()*currentLen), (int)(Math.random()*currentLen), 0, true, false);
+ if(ArrayVisualizer.shuffleEnabled()) Delays.sleep(2);
+ }
+
+ /*
+ int step = (int) Math.sqrt(currentLen);
+
+ //TODO: *Strongly* consider randomSwap method
+ for(int i = 0; i < currentLen; i += step){
+ int randomIndex = (int) (Math.random() * step);
+ randomIndex = Math.max(randomIndex, 1);
+ randomIndex = Math.min(randomIndex, currentLen - i - 1);
+ Writes.swap(array, i, i + randomIndex, 0, true, false);
+
+ if(ArrayVisualizer.shuffleEnabled()) Delays.sleep(2);
+ }
+ */
+ }
+ },
+ ALREADY {
+ @Override
+ public void shuffleArray(int[] array, ArrayVisualizer ArrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes) {
+ int currentLen = ArrayVisualizer.getCurrentLength();
+
+ for(int i = 0; i < currentLen; i++) {
+ if(ArrayVisualizer.shuffleEnabled()) {
+ Highlights.markArray(1, i);
+ Delays.sleep(1);
+ }
+ }
+ }
+ };
+
+ public abstract void shuffleArray(int[] array, ArrayVisualizer ArrayVisualizer, Delays Delays, Highlights Highlights, Writes Writes);
+}
\ No newline at end of file
diff --git a/src/utils/Sounds.java b/src/utils/Sounds.java
new file mode 100644
index 00000000..3f5c27a0
--- /dev/null
+++ b/src/utils/Sounds.java
@@ -0,0 +1,210 @@
+package utils;
+
+import java.io.InputStream;
+
+import javax.sound.midi.MidiChannel;
+import javax.sound.midi.MidiSystem;
+import javax.sound.midi.MidiUnavailableException;
+import javax.sound.midi.Synthesizer;
+import javax.swing.JOptionPane;
+
+import main.ArrayVisualizer;
+import soundfont.SFXFetcher;
+import soundfont.SFXFetcher;
+import templates.JErrorPane;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class Sounds {
+ private int[] array;
+
+ private ArrayVisualizer ArrayVisualizer;
+
+ private Thread AudioThread;
+
+ private Highlights Highlights;
+
+ private Synthesizer synth;
+ private MidiChannel[] channels;
+
+ private volatile int noteDelay;
+
+ private volatile boolean SOUND;
+ private boolean MIDI;
+ private int NUMCHANNELS; //Number of Audio Channels
+ private double PITCHMIN; //Minimum Pitch
+ private double PITCHMAX; //Maximum Pitch
+ private double SOUNDMUL;
+ private boolean SOFTERSOUNDS;
+
+ final private int REVERB = 91;
+
+ public Sounds(int[] array, ArrayVisualizer arrayVisualizer) {
+ this.array = array;
+ this.ArrayVisualizer = arrayVisualizer;
+ this.Highlights = ArrayVisualizer.getHighlights();
+
+ this.SOUND = true;
+ this.MIDI = true;
+ this.NUMCHANNELS = 16;
+ this.PITCHMIN = 25d;
+ this.PITCHMAX = 105d;
+ this.SOUNDMUL = 1d;
+
+ this.noteDelay = 1;
+
+ try {
+ MidiSystem.getSequencer(false);
+ this.synth = MidiSystem.getSynthesizer();
+ this.synth.open();
+ } catch (MidiUnavailableException e) {
+ JOptionPane.showMessageDialog(null, e.getMessage() + ": The MIDI device is unavailable, possibly because it is already being used by another application. Sound is disabled.");
+ }
+
+ SFXFetcher sfxFetcher = new SFXFetcher();
+ InputStream stream = sfxFetcher.getSFXFile();
+ try {
+ this.synth.loadAllInstruments(MidiSystem.getSoundbank(stream));
+ } catch (Exception e) {
+ JErrorPane.invokeErrorMessage(e);
+ }
+ finally {
+ try {
+ stream.close();
+ } catch (Exception e) {
+ JErrorPane.invokeErrorMessage(e);
+ }
+ }
+ this.channels = new MidiChannel[this.NUMCHANNELS];
+
+ for(int i = 0; i < this.NUMCHANNELS; i++) {
+ this.channels[i] = this.synth.getChannels()[i];
+ //this.channels[i].programChange(this.synth.getLoadedInstruments()[197].getPatch().getProgram());
+ this.channels[i].programChange(this.synth.getLoadedInstruments()[16].getPatch().getProgram()); // MIDI Instrument 16 is a Rock Organ.
+ this.channels[i].setChannelPressure(1);
+ }
+ if(this.channels[0].getProgram() == -1) {
+ JOptionPane.showMessageDialog(null, "Could not find a valid MIDI instrument. Sound is disabled.");
+ }
+
+ this.AudioThread = new Thread() {
+ @Override
+ public void run() {
+ while(true) {
+ for(MidiChannel channel : channels) {
+ channel.allNotesOff();
+ }
+ if(SOUND == false || MIDI == false || JErrorPane.errorMessageActive) {
+ continue;
+ }
+
+ int noteCount = Math.min(Highlights.getMarkCount(), NUMCHANNELS);
+ int voice = 0;
+
+ for(int i : Highlights.highlightList()) {
+ try {
+ if(i != -1) {
+ int currentLen = ArrayVisualizer.getCurrentLength();
+
+ //PITCH
+ double pitch = Sounds.this.array[Math.min(Math.max(i, 0), currentLen - 1)] / (double) currentLen * (PITCHMAX - PITCHMIN) + PITCHMIN;
+ int pitchmajor = (int) pitch;
+ int pitchminor = (int)((pitch-((int)pitch))*8192d)+8192;
+
+ int vel = (int) (Math.pow(PITCHMAX - pitchmajor, 2d) * (Math.pow(noteCount, -0.25)) * 64d * SOUNDMUL) / 2; //I'VE SOLVED IT!!
+
+ if(SOUNDMUL >= 1 && vel < 256) {
+ vel *= vel;
+ }
+
+ channels[voice].noteOn(pitchmajor, vel);
+ channels[voice].setPitchBend(pitchminor);
+ channels[voice].controlChange(REVERB, 10);
+
+ if((++voice % Math.max(noteCount, 1)) == 0)
+ break;
+ }
+ }
+ catch (Exception e) {
+ JErrorPane.invokeErrorMessage(e);
+ }
+ }
+ try {
+ for(int i = 0; i < Sounds.this.noteDelay; i++) {
+ sleep(1);
+ }
+ } catch(Exception e) {
+ JErrorPane.invokeErrorMessage(e);
+ }
+ }
+ }
+ };
+ }
+
+ public synchronized void toggleSounds(boolean val) {
+ this.SOUND = val;
+ }
+
+ public synchronized void toggleSound(boolean val) {
+ this.MIDI = val;
+ }
+
+ //Double check logic
+ public void toggleSofterSounds(boolean val) {
+ this.SOFTERSOUNDS = val;
+
+ if(this.SOFTERSOUNDS) this.SOUNDMUL = 0.01;
+ else this.SOUNDMUL = 1;
+ }
+
+ public double getVolume() {
+ return this.SOUNDMUL;
+ }
+ public void changeVolume(double val) {
+ this.SOUNDMUL = val;
+ }
+
+ public void changeNoteDelayAndFilter(int noteFactor) {
+ if(noteFactor != this.noteDelay) {
+ if(noteFactor > 1) {
+ this.noteDelay = noteFactor;
+ this.SOUNDMUL = 1d / noteFactor;
+ }
+ //Double check logic
+ else {
+ this.noteDelay = 1;
+
+ if(this.SOFTERSOUNDS) this.SOUNDMUL = 0.01;
+ else this.SOUNDMUL = 1;
+ }
+ }
+ }
+
+ public void startAudioThread() {
+ AudioThread.start();
+ }
+}
\ No newline at end of file
diff --git a/src/utils/Timer.java b/src/utils/Timer.java
new file mode 100644
index 00000000..d3882007
--- /dev/null
+++ b/src/utils/Timer.java
@@ -0,0 +1,125 @@
+package utils;
+
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.NumberFormat;
+import java.util.Locale;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class Timer {
+ private DecimalFormat formatter;
+ private DecimalFormatSymbols symbols;
+
+ private volatile String minuteFormat;
+ private volatile String secondFormat;
+
+ private volatile int elapsedTime;
+ private volatile double realTimer;
+ private volatile boolean REALTIMER;
+ private volatile double sortRunTime;
+ private volatile boolean timerEnabled;
+
+ private long timeStart;
+ private long timeStop;
+
+ public Timer() {
+ this.REALTIMER = true;
+
+ this.timeStart = 0;
+ this.timeStop = 0;
+
+ this.formatter = (DecimalFormat) NumberFormat.getInstance(Locale.US);
+ this.symbols = this.formatter.getDecimalFormatSymbols();
+
+ this.symbols.setGroupingSeparator(',');
+ this.formatter.setDecimalFormatSymbols(this.symbols);
+ }
+
+ public String getVisualTime() {
+ if(this.timerEnabled) {
+ this.elapsedTime = (int) ((System.nanoTime() - this.sortRunTime) / 1e+9);
+
+ secondFormat = "" + ((this.elapsedTime % 60) / 10) + (this.elapsedTime % 10);
+ minuteFormat = (this.elapsedTime / 60) + ":" + secondFormat;
+ }
+
+ if(!this.timerEnabled && this.elapsedTime == 0) return "-:--";
+ else if(this.elapsedTime >= 60) return minuteFormat;
+ else if(this.elapsedTime >= 1) return "0:" + secondFormat;
+ else return "0:00";
+ }
+
+ public String getRealTime() {
+ if(!this.REALTIMER) {
+ return "Disabled";
+ }
+ else if(this.realTimer == 0) {
+ if(this.timerEnabled) return "0.000ms";
+ else return "---ms";
+ }
+ else if(this.realTimer < 0.001) return "< 0.001ms";
+ else if(this.realTimer >= 60000.000) return "~" + this.formatter.format((int) (this.realTimer / 60000)) + "m" + (int) ((this.realTimer % 60000) / 1000) + "s";
+ else if(this.realTimer >= 1000.000) return "~" + this.formatter.format(this.realTimer / 1000) + "s";
+ else return "~" + this.formatter.format(this.realTimer) + "ms";
+ }
+
+ public void toggleRealTimer(boolean Bool) {
+ this.REALTIMER = Bool;
+ }
+
+ public void enableRealTimer() {
+ if(REALTIMER) this.timerEnabled = true;
+ this.sortRunTime = System.nanoTime();
+ this.realTimer = 0;
+ }
+
+ public void disableRealTimer() {
+ this.timerEnabled = false;
+ }
+
+ public boolean timerEnabled() {
+ return this.timerEnabled;
+ }
+
+ public void startLap() {
+ if(this.timerEnabled) this.timeStart = System.nanoTime();
+ }
+
+ public void stopLap() {
+ this.timeStop = System.nanoTime();
+ if(this.timerEnabled) this.realTimer += (timeStop - timeStart) * 1e-6d;
+ }
+
+ void manualAddTime(long milliseconds) {
+ this.realTimer += milliseconds;
+ }
+
+ public void manualSetTime(long milliseconds) {
+ this.realTimer = milliseconds;
+ }
+}
\ No newline at end of file
diff --git a/src/utils/Writes.java b/src/utils/Writes.java
new file mode 100644
index 00000000..09bf7712
--- /dev/null
+++ b/src/utils/Writes.java
@@ -0,0 +1,322 @@
+package utils;
+
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.Locale;
+
+import main.ArrayVisualizer;
+import templates.TimSorting;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+/**
+ *
+ * @author S630690
+ */
+final public class Writes {
+ private volatile long reversals;
+ private volatile long swaps;
+ private volatile long tempWrites;
+ private volatile long writes;
+
+ private DecimalFormat formatter;
+ private DecimalFormatSymbols symbols;
+
+ private Delays Delays;
+ private Highlights Highlights;
+ private Timer Timer;
+
+ public Writes(ArrayVisualizer ArrayVisualizer) {
+ this.reversals = 0;
+ this.swaps = 0;
+ this.tempWrites = 0;
+ this.writes = 0;
+
+ this.Delays = ArrayVisualizer.getDelays();
+ this.Highlights = ArrayVisualizer.getHighlights();
+ this.Timer = ArrayVisualizer.getTimer();
+
+ this.formatter = (DecimalFormat) NumberFormat.getInstance(Locale.US);
+ this.symbols = this.formatter.getDecimalFormatSymbols();
+
+ this.symbols.setGroupingSeparator(',');
+ this.formatter.setDecimalFormatSymbols(this.symbols);
+ }
+
+ public void resetStatistics() {
+ this.reversals = 0;
+ this.swaps = 0;
+ this.tempWrites = 0;
+ this.writes = 0;
+ }
+
+ public String getReversals() {
+ if(this.reversals < 0) {
+ this.reversals = Long.MIN_VALUE;
+ return "Over " + this.formatter.format(Long.MAX_VALUE);
+ }
+ else {
+ if(reversals == 1) return this.reversals + " Reversal";
+ else return this.formatter.format(this.reversals) + " Reversals";
+ }
+ }
+
+ public String getSwaps() {
+ if(this.swaps < 0) {
+ this.swaps = Long.MIN_VALUE;
+ return "Over " + this.formatter.format(Long.MAX_VALUE);
+ }
+ else {
+ if(this.swaps == 1) return this.swaps + " Swap";
+ else return this.formatter.format(this.swaps) + " Swaps";
+ }
+ }
+
+ public String getTempWrites() {
+ if(this.tempWrites < 0) {
+ this.tempWrites = Long.MIN_VALUE;
+ return "Over " + this.formatter.format(Long.MAX_VALUE);
+ }
+ else {
+ if(this.tempWrites == 1) return this.tempWrites + " Write to Auxiliary Array(s)";
+ else return this.formatter.format(this.tempWrites) + " Writes to Auxiliary Array(s)";
+ }
+ }
+
+ public String getWrites() {
+ if(this.writes < 0) {
+ this.writes = Long.MIN_VALUE;
+ return "Over " + this.formatter.format(Long.MAX_VALUE);
+ }
+ else {
+ if(this.writes == 1) return this.writes + " Write to Main Array";
+ else return this.formatter.format(this.writes) + " Writes to Main Array";
+ }
+ }
+
+ public void changeTempWrites(int value) {
+ this.tempWrites += value;
+ }
+
+ public void changeWrites(int value) {
+ this.writes += value;
+ }
+
+ private void updateSwap(boolean auxwrite) {
+ this.swaps++;
+ if(auxwrite) this.tempWrites += 2;
+ else this.writes += 2;
+ }
+
+ private void markSwap(int a, int b) {
+ Highlights.markArray(1, a);
+ Highlights.markArray(2, b);
+ }
+
+ public void swap(int[] array, int a, int b, double pause, boolean mark, boolean auxwrite) {
+ if(mark) this.markSwap(a, b);
+
+ Timer.startLap();
+
+ int temp = array[a];
+ array[a] = array[b];
+ array[b] = temp;
+
+ Timer.stopLap();
+
+ this.updateSwap(auxwrite);
+ Delays.sleep(pause);
+ }
+
+ 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++) {
+ this.swap(array, i, i + 1, 0, mark, auxwrite);
+ Delays.sleep(sleep);
+ }
+ }
+ else {
+ for(int i = pos; i > to; i--) {
+ this.swap(array, i, i - 1, 0, mark, auxwrite);
+ Delays.sleep(sleep);
+ }
+ }
+ }
+
+ public void reversal(int[] array, int start, int length, double sleep, boolean mark, boolean auxwrite) {
+ this.reversals++;
+
+ for(int i = start; i < start + ((length - start + 1) / 2); i++) {
+ this.swap(array, i, start + length - i, sleep, mark, auxwrite);
+ }
+ }
+
+ public void write(int[] array, int at, int equals, double pause, boolean mark, boolean auxwrite) {
+ if(mark) Highlights.markArray(1, at);
+
+ if(auxwrite) tempWrites++;
+ else writes++;
+
+ Timer.startLap();
+
+ array[at] = equals;
+
+ Timer.stopLap();
+
+ Delays.sleep(pause);
+ }
+
+ public void multiDimWrite(int[][] array, int x, int y, int equals, double pause, boolean mark, boolean auxwrite) {
+ if(mark) Highlights.markArray(1, x);
+
+ if(auxwrite) tempWrites++;
+ else writes++;
+
+ Timer.startLap();
+
+ array[x][y] = equals;
+
+ Timer.stopLap();
+
+ Delays.sleep(pause);
+ }
+
+ //Simulates a write in order to better estimate time for values being written to an ArrayList
+ public void mockWrite(int length, int pos, int val, double pause) {
+ int[] mockArray = new int[length];
+
+ this.tempWrites++;
+
+ Timer.startLap();
+
+ mockArray[pos] = val;
+
+ Timer.stopLap();
+
+ Delays.sleep(pause);
+ }
+
+ public void transcribe(int[] array, ArrayList[] registers, int start, boolean mark, boolean auxwrite) {
+ int total = start;
+
+ for(int index = 0; index < registers.length; index++) {
+ for(int i = 0; i < registers[index].size(); i++) {
+ this.write(array, total++, registers[index].get(i), 0, mark, auxwrite);
+ if(mark) Delays.sleep(1);
+ }
+ registers[index].clear();
+ }
+ }
+
+ public void transcribeMSD(int[] array, ArrayList[] registers, int start, int min, double sleep, boolean mark, boolean auxwrite) {
+ int total = start;
+ int temp = 0;
+
+ for(ArrayList list : registers) {
+ total += list.size();
+ }
+
+ for(int index = registers.length - 1; index >= 0; index--) {
+ for(int i = registers[index].size() - 1; i >= 0; i--) {
+ this.write(array, total + min - temp++ - 1, registers[index].get(i), 0, mark, auxwrite);
+ if(mark) Delays.sleep(sleep);
+ }
+ }
+ }
+
+ public void fancyTranscribe(int[] array, int length, ArrayList[] registers, double sleep) {
+ int[] tempArray = new int[length];
+ boolean[] tempWrite = new boolean[length];
+ int radix = registers.length;
+
+ this.transcribe(tempArray, registers, 0, false, true);
+ tempWrites -= length;
+
+ for(int i = 0; i < length; i++) {
+ int register = i % radix;
+ int pos = (register * (length / radix)) + (i / radix);
+
+ if(!tempWrite[pos]) {
+ this.write(array, pos, tempArray[pos], 0, false, false);
+ tempWrite[pos] = true;
+ }
+
+ Highlights.markArray(register, pos);
+ if(register == 0) Delays.sleep(sleep);
+ }
+ for(int i = 0; i < length; i++) {
+ if(!tempWrite[i]){
+ this.write(array, i, tempArray[i], 0, false, false);
+ }
+ }
+
+ Highlights.clearAllMarks();
+ }
+
+ //Methods mocking System.arraycopy (reversearraycopy is for TimSort's MergeHi and BinaryInsert, and WikiSort's Rotate)
+ public void arraycopy(int[] src, int srcPos, int[] dest, int destPos, int length, double sleep, boolean mark, boolean temp) {
+ for(int i = 0; i < length; i++) {
+ if(mark) {
+ if(temp) Highlights.markArray(1, srcPos + i);
+ else Highlights.markArray(1, destPos + i);
+ }
+
+ //TODO: Handle order of Delays in write method better
+ this.write(dest, destPos + i, src[srcPos + i], sleep, false, temp);
+ }
+ }
+
+ public void reversearraycopy(int[] src, int srcPos, int[] dest, int destPos, int length, double sleep, boolean mark, boolean temp) {
+ for(int i = length - 1; i >= 0; i--) {
+ if(mark) {
+ if(temp) Highlights.markArray(1, srcPos + i);
+ else Highlights.markArray(1, destPos + i);
+ }
+
+ this.write(dest, destPos + i, src[srcPos + i], sleep, false, temp);
+ }
+ }
+
+ //TODO: These methods should be solely controlled by Timer class
+ public void addTime(long milliseconds) {
+ if(Timer.timerEnabled()) Timer.manualAddTime(milliseconds);
+ }
+
+ public void setTime(long milliseconds) {
+ if(Timer.timerEnabled()) Timer.manualSetTime(milliseconds);
+ }
+
+ public void startLap() {
+ if(Timer.timerEnabled()) Timer.startLap();
+ }
+
+ public void stopLap() {
+ if(Timer.timerEnabled()) Timer.stopLap();
+ }
+}
\ No newline at end of file
diff --git a/src/visuals/Bars.java b/src/visuals/Bars.java
new file mode 100644
index 00000000..eb4a9ee2
--- /dev/null
+++ b/src/visuals/Bars.java
@@ -0,0 +1,117 @@
+package visuals;
+
+import java.awt.Color;
+
+import main.ArrayVisualizer;
+import templates.Visual;
+import utils.Highlights;
+import utils.Renderer;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class Bars extends Visual {
+ public Bars(ArrayVisualizer ArrayVisualizer) {
+ super(ArrayVisualizer);
+ }
+
+ @Override
+ public void drawVisual(int[] array, ArrayVisualizer ArrayVisualizer, Renderer Renderer, Highlights Highlights) {
+ for(int i = 0; i < ArrayVisualizer.getCurrentLength(); i++){
+ if(Highlights.fancyFinishActive()) {
+ if(i < Highlights.getFancyFinishPosition()) {
+ this.mainRender.setColor(Color.GREEN);
+ }
+ else if(ArrayVisualizer.rainbowEnabled() || ArrayVisualizer.colorEnabled()) {
+ this.mainRender.setColor(getIntColor(array[i], ArrayVisualizer.getCurrentLength()));
+ }
+ else this.mainRender.setColor(Color.WHITE);
+
+ drawFancyFinish(ArrayVisualizer.getLogBaseTwoOfLength(), i, Highlights.getFancyFinishPosition(), this.mainRender, ArrayVisualizer.colorEnabled(), ArrayVisualizer.rainbowEnabled());
+ }
+ else {
+ if(ArrayVisualizer.rainbowEnabled() || ArrayVisualizer.colorEnabled()) {
+ this.mainRender.setColor(getIntColor(array[i], ArrayVisualizer.getCurrentLength()));
+ }
+ else this.mainRender.setColor(Color.WHITE);
+
+ if(ArrayVisualizer.getCurrentLength() != 2) {
+ colorMarkedBars(ArrayVisualizer.getLogBaseTwoOfLength(), i, Highlights, this.mainRender, ArrayVisualizer.colorEnabled(), ArrayVisualizer.rainbowEnabled(), ArrayVisualizer.analysisEnabled());
+ }
+ }
+ /*
+ int markHeight = 0;
+ Color currentColor = mainRender.getColor();
+ if(currentColor == Color.BLACK || currentColor == Color.RED || currentColor == Color.BLUE) {
+ markHeight = 5;
+ }
+ */
+
+ int y = 0;
+ int width = (int) (Renderer.getXScale() * (i + 1)) - Renderer.getOffset();
+
+ if(ArrayVisualizer.rainbowEnabled()) {
+ if(width > 0) {
+ this.mainRender.fillRect(Renderer.getOffset() + 20, 0, width, ArrayVisualizer.windowHeight());
+ }
+
+ Renderer.setOffset(Renderer.getOffset() + width);
+ }
+ else if(ArrayVisualizer.waveEnabled()) {
+ if(width > 0) {
+ y = (int) ((ArrayVisualizer.windowHeight() / 4) * Math.sin((2 * Math.PI * ((double) array[i] / ArrayVisualizer.getCurrentLength()))) + ArrayVisualizer.windowHalfHeight());
+ this.mainRender.fillRect(Renderer.getOffset() + 20, y, width, 20);
+ }
+ Renderer.setOffset(Renderer.getOffset() + width);
+ }
+ else {
+ if(width > 0) {
+ /*
+ int gap = 0;
+ if(width > 5) {
+ gap = 5;
+ }
+ */
+
+ y = (int) (((ArrayVisualizer.windowHeight() - 20)) - (array[i] + 1) * Renderer.getYScale());
+ mainRender.fillRect(Renderer.getOffset() + 20, y, width, (int) ((array[i] + 1) * Renderer.getYScale()));
+
+ //mainRender.fillRect(Renderer.getOffset() + 20, y /*- markHeight*/, width /*- gap*/, (int) ((array[i] + 1) * Renderer.getYScale()) /*+ markHeight*/);
+
+ /*
+ double thickness = 1;
+ Stroke oldStroke = mainRender.getStroke();
+ mainRender.setStroke(new BasicStroke((float) thickness));
+ mainRender.setColor(Color.BLACK);
+ mainRender.drawLine(Renderer.getOffset() + 20, y, Renderer.getOffset() + 20, (int) Math.max(array[i] * Renderer.getYScale()-1, 1) + y);
+ mainRender.setStroke(oldStroke);
+ */
+ }
+ Renderer.setOffset(Renderer.getOffset() + width);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/visuals/Circular.java b/src/visuals/Circular.java
new file mode 100644
index 00000000..753a4d96
--- /dev/null
+++ b/src/visuals/Circular.java
@@ -0,0 +1,280 @@
+package visuals;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Polygon;
+
+import main.ArrayVisualizer;
+import templates.Visual;
+import utils.Highlights;
+import utils.Renderer;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class Circular extends Visual {
+ final private static double CIRC_HEIGHT_RATIO = (9/6.0843731432) * (16/9d);
+ final private static double CIRC_WIDTH_RATIO = (16/6.0843731432) * (16/9d);
+
+ public Circular(ArrayVisualizer ArrayVisualizer) {
+ super(ArrayVisualizer);
+ }
+
+ // The reason we use cosine with height (expressed in terms of y) and sine with width (expressed in terms of x) is because our circles are rotated 90 degrees.
+ // After that rotation, sine is on the x-axis and cosine is on the y-axis.
+
+ // If we we use sine with height and cosine with width, the sorts would start from the right side of the circle,
+ // just like the unit circle from trigonometry.
+
+ private static double getSinOfDegrees(double d, int halfCirc) {
+ return Math.sin((d * Math.PI) / halfCirc);
+ }
+
+ private static double getCosOfDegrees(double d, int halfCirc) {
+ return Math.cos((d * Math.PI) / halfCirc);
+ }
+
+ @Override
+ public void drawVisual(int[] array, ArrayVisualizer ArrayVisualizer, Renderer Renderer, Highlights Highlights) {
+ for(int i = 0; i < ArrayVisualizer.getCurrentLength(); i++){
+ if(i < Highlights.getFancyFinishPosition()) {
+ this.mainRender.setColor(Color.getHSBColor((1f/3f), 1f, 0.8f));
+ }
+ else if(!ArrayVisualizer.colorEnabled() && (ArrayVisualizer.spiralEnabled() || ArrayVisualizer.distanceEnabled() || ArrayVisualizer.pixelsEnabled())) {
+ this.mainRender.setColor(Color.WHITE);
+ }
+ else this.mainRender.setColor(getIntColor(array[i], ArrayVisualizer.getCurrentLength()));
+
+ if(Highlights.fancyFinishActive()) {
+ drawFancyFinish(ArrayVisualizer.getLogBaseTwoOfLength(), i, Highlights.getFancyFinishPosition(), this.mainRender, ArrayVisualizer.rainbowEnabled(), ArrayVisualizer.colorEnabled());
+ }
+ else {
+ /*
+ if(ArrayVisualizer.pointerActive()) {
+ if(Highlights.containsPosition(i)) {
+ if(ArrayVisualizer.analysisEnabled()) {
+ this.extraRender.setColor(Color.GRAY);
+ }
+ else {
+ this.extraRender.setColor(Color.WHITE);
+ }
+
+ //Create new Polygon for the pointer
+ Polygon pointer = new Polygon();
+
+ //Calculate radians
+ double degrees = 360 * ((double) i / ArrayVisualizer.getCurrentLength());
+ double radians = Math.toRadians(degrees);
+
+ int pointerWidthRatio = (int) (ArrayVisualizer.windowHalfWidth() / CIRC_WIDTH_RATIO);
+ int pointerHeightRatio = (int) (ArrayVisualizer.windowHalfHeight() / CIRC_HEIGHT_RATIO);
+
+ //First step: draw a triangle
+ int[] pointerXValues = {pointerWidthRatio - 10,
+ pointerWidthRatio,
+ pointerWidthRatio + 10};
+
+ int[] pointerYValues = {pointerHeightRatio - 10,
+ pointerHeightRatio + 10,
+ pointerHeightRatio - 10};
+
+ //Second step: rotate triangle (https://en.wikipedia.org/wiki/Rotation_matrix)
+ for(int j = 0; j < pointerXValues.length; j++) {
+ double x = pointerXValues[j] - pointerWidthRatio;
+ double y = pointerYValues[j] - pointerHeightRatio;
+
+ pointerXValues[j] = (int) (pointerWidthRatio
+ + x*Math.cos(radians)
+ - y*Math.sin(radians));
+ pointerYValues[j] = (int) (pointerHeightRatio
+ + x*Math.sin(radians)
+ + y*Math.cos(radians));
+ }
+
+ for(int j = 0; j < pointerXValues.length; j++) {
+ pointer.addPoint(pointerXValues[j], pointerYValues[j]);
+ }
+
+ this.extraRender.fillPolygon(pointer);
+ }
+ }
+ else */ if(ArrayVisualizer.getCurrentLength() != 2){
+ colorMarkedBars(ArrayVisualizer.getLogBaseTwoOfLength(), i, Highlights, this.mainRender, ArrayVisualizer.rainbowEnabled(), ArrayVisualizer.colorEnabled(), ArrayVisualizer.analysisEnabled());
+ }
+ }
+
+ if(ArrayVisualizer.distanceEnabled()) {
+ //TODO: Rewrite this abomination
+ double len = ((ArrayVisualizer.getCurrentLength() / 2d) - Math.min(Math.min(Math.abs(i - array[i]), Math.abs(i - array[i] + ArrayVisualizer.getCurrentLength())), Math.abs(i - array[i] - ArrayVisualizer.getCurrentLength()))) / (ArrayVisualizer.getCurrentLength() / 2d);
+
+ if(ArrayVisualizer.pixelsEnabled()) {
+ int linkedpixX = ArrayVisualizer.windowHalfWidth() + (int) (Circular.getSinOfDegrees(i, ArrayVisualizer.halfCircle()) * ((ArrayVisualizer.windowWidth() - 64) / CIRC_WIDTH_RATIO * len)) + Renderer.getDotWidth() / 2;
+ int linkedpixY = ArrayVisualizer.windowHalfHeight() - (int) (Circular.getCosOfDegrees(i, ArrayVisualizer.halfCircle()) * ((ArrayVisualizer.windowHeight() - 96) / CIRC_HEIGHT_RATIO * len)) + Renderer.getDotHeight() / 2;
+
+ if(ArrayVisualizer.linesEnabled()) {
+ if(i > 0) {
+ if(Highlights.fancyFinishActive()) {
+ if(i < Highlights.getFancyFinishPosition()) {
+ lineFancy(this.mainRender, ArrayVisualizer.currentWidth());
+ }
+ else {
+ lineClear(this.mainRender, ArrayVisualizer.colorEnabled(), array, i, ArrayVisualizer.getCurrentLength(), ArrayVisualizer.currentWidth());
+ }
+
+ drawFancyFinishLine(ArrayVisualizer.getLogBaseTwoOfLength(), i, Highlights.getFancyFinishPosition(), this.mainRender, ArrayVisualizer.currentWidth(), ArrayVisualizer.colorEnabled());
+ }
+ else {
+ if(Highlights.containsPosition(i)) {
+ lineMark(this.mainRender, ArrayVisualizer.currentWidth(), ArrayVisualizer.colorEnabled(), ArrayVisualizer.analysisEnabled());
+ }
+ else lineClear(this.mainRender, ArrayVisualizer.colorEnabled(), array, i, ArrayVisualizer.getCurrentLength(), ArrayVisualizer.currentWidth());
+ }
+ this.mainRender.drawLine(linkedpixX, linkedpixY, Renderer.getLineX(), Renderer.getLineY());
+ }
+ Renderer.setLineX(linkedpixX);
+ Renderer.setLineY(linkedpixY);
+ }
+ else {
+ boolean drawRect = false;
+ if(Highlights.containsPosition(i)) {
+ setRectColor(this.extraRender, ArrayVisualizer.colorEnabled(), ArrayVisualizer.analysisEnabled());
+ drawRect = true;
+ }
+
+ if(drawRect) {
+ this.extraRender.setStroke(ArrayVisualizer.getThickStroke());
+ if(Highlights.fancyFinishActive()) {
+ this.extraRender.fillRect((linkedpixX - Renderer.getDotWidth() / 2) - 10, (linkedpixY - Renderer.getDotHeight() / 2) - 10, Renderer.getDotWidth() + 20, Renderer.getDotHeight() + 20);
+ }
+ else {
+ this.extraRender.drawRect((linkedpixX - Renderer.getDotWidth() / 2) - 10, (linkedpixY - Renderer.getDotHeight() / 2) - 10, Renderer.getDotWidth() + 20, Renderer.getDotHeight() + 20);
+ }
+ this.extraRender.setStroke(new BasicStroke(3f * (ArrayVisualizer.currentWidth() / 1280f)));
+ }
+ this.mainRender.fillRect(linkedpixX - Renderer.getDotWidth() / 2, linkedpixY - Renderer.getDotHeight() / 2, Renderer.getDotWidth(), Renderer.getDotHeight());
+ }
+ }
+ else {
+ Polygon p = new Polygon();
+
+ p.addPoint(ArrayVisualizer.windowHalfWidth(),
+ ArrayVisualizer.windowHalfHeight());
+
+ p.addPoint(ArrayVisualizer.windowHalfWidth() + (int) (Circular.getSinOfDegrees(i, ArrayVisualizer.halfCircle()) * (((ArrayVisualizer.currentWidth() - 64) / CIRC_WIDTH_RATIO) * len)),
+ ArrayVisualizer.windowHalfHeight() - (int) (Circular.getCosOfDegrees(i, ArrayVisualizer.halfCircle()) * (((ArrayVisualizer.currentHeight() - 96) / CIRC_HEIGHT_RATIO) * len)));
+
+ p.addPoint(ArrayVisualizer.windowHalfWidth() + (int) (Circular.getSinOfDegrees(i + 1, ArrayVisualizer.halfCircle()) * (((ArrayVisualizer.currentWidth() - 64) / CIRC_WIDTH_RATIO) * len)),
+ ArrayVisualizer.windowHalfHeight() - (int) (Circular.getCosOfDegrees(i + 1, ArrayVisualizer.halfCircle()) * (((ArrayVisualizer.currentHeight() - 96) / CIRC_HEIGHT_RATIO) * len)));
+
+ this.mainRender.fillPolygon(p);
+ }
+ }
+ else if(ArrayVisualizer.spiralEnabled()) {
+ if(ArrayVisualizer.pixelsEnabled()) {
+ if(ArrayVisualizer.linesEnabled()) {
+ if(i > 0) {
+ if(Highlights.fancyFinishActive()) {
+ if(i < Highlights.getFancyFinishPosition()) {
+ lineFancy(this.mainRender, ArrayVisualizer.currentWidth());
+ }
+ else lineClear(this.mainRender, ArrayVisualizer.colorEnabled(), array, i, ArrayVisualizer.getCurrentLength(), ArrayVisualizer.currentWidth());
+
+ drawFancyFinishLine(ArrayVisualizer.getLogBaseTwoOfLength(), i, Highlights.getFancyFinishPosition(), this.mainRender, ArrayVisualizer.currentWidth(), ArrayVisualizer.colorEnabled());
+ }
+ else {
+ if(Highlights.containsPosition(i)) {
+ lineMark(this.mainRender, ArrayVisualizer.currentWidth(), ArrayVisualizer.colorEnabled(), ArrayVisualizer.analysisEnabled());
+ }
+ else lineClear(this.mainRender, ArrayVisualizer.colorEnabled(), array, i, ArrayVisualizer.getCurrentLength(), ArrayVisualizer.currentWidth());
+ }
+ this.mainRender.drawLine(ArrayVisualizer.windowHalfWidth() + (int) (Circular.getSinOfDegrees(i, ArrayVisualizer.halfCircle()) * ((((ArrayVisualizer.windowWidth() - 64) / 3.0) * array[i]) / ArrayVisualizer.getCurrentLength())),
+ ArrayVisualizer.windowHalfHeight() - (int) (Circular.getCosOfDegrees(i, ArrayVisualizer.halfCircle()) * ((((ArrayVisualizer.windowHeight() - 96) / 2.0) * array[i]) / ArrayVisualizer.getCurrentLength())),
+ Renderer.getLineX(),
+ Renderer.getLineY());
+ }
+ Renderer.setLineX(ArrayVisualizer.windowHalfWidth() + (int) (Circular.getSinOfDegrees(i, ArrayVisualizer.halfCircle()) * ((((ArrayVisualizer.windowWidth() - 64) / 3.0) * array[i]) / ArrayVisualizer.getCurrentLength())));
+ Renderer.setLineY(ArrayVisualizer.windowHalfHeight() - (int) (Circular.getCosOfDegrees(i, ArrayVisualizer.halfCircle()) * ((((ArrayVisualizer.windowHeight() - 96) / 2.0) * array[i]) / ArrayVisualizer.getCurrentLength())));
+ }
+ else {
+ boolean drawRect = false;
+ if(Highlights.containsPosition(i)) {
+ setRectColor(this.extraRender, ArrayVisualizer.colorEnabled(), ArrayVisualizer.analysisEnabled());
+ drawRect = true;
+ }
+
+ int rectx = ArrayVisualizer.windowHalfWidth() + (int) (Circular.getSinOfDegrees(i, ArrayVisualizer.halfCircle()) * (((((ArrayVisualizer.windowWidth() - 64) / 3.0) * array[i]) / ArrayVisualizer.getCurrentLength())));
+ int recty = ArrayVisualizer.windowHalfHeight() - (int) (Circular.getCosOfDegrees(i, ArrayVisualizer.halfCircle()) * (((((ArrayVisualizer.windowHeight() - 96) / 2.0) * array[i]) / ArrayVisualizer.getCurrentLength())));
+
+ this.mainRender.fillRect(rectx, recty, Renderer.getDotWidth(), Renderer.getDotHeight());
+
+ if(drawRect) {
+ this.extraRender.setStroke(ArrayVisualizer.getThickStroke());
+ if(Highlights.fancyFinishActive()) {
+ this.extraRender.fillRect(rectx - 10, recty - 10, Renderer.getDotWidth() + 20, Renderer.getDotHeight() + 20);
+ }
+ else {
+ this.extraRender.drawRect(rectx - 10, recty - 10, Renderer.getDotWidth() + 20, Renderer.getDotHeight() + 20);
+ }
+ this.extraRender.setStroke(new BasicStroke(3f * (ArrayVisualizer.currentWidth() / 1280f)));
+ }
+ }
+ }
+ else {
+ if(Highlights.containsPosition(i)) {
+ markBar(this.mainRender, ArrayVisualizer.colorEnabled(), ArrayVisualizer.rainbowEnabled(), ArrayVisualizer.analysisEnabled());
+ }
+
+ Polygon p = new Polygon();
+
+ p.addPoint(ArrayVisualizer.windowHalfWidth(),
+ ArrayVisualizer.windowHalfHeight());
+
+ p.addPoint(ArrayVisualizer.windowHalfWidth() + (int) (Circular.getSinOfDegrees(i, ArrayVisualizer.halfCircle()) * ((((ArrayVisualizer.windowWidth() - 64) / 3.0) * array[i]) / ArrayVisualizer.getCurrentLength())),
+ ArrayVisualizer.windowHalfHeight() - (int) (Circular.getCosOfDegrees(i, ArrayVisualizer.halfCircle()) * ((((ArrayVisualizer.windowHeight() - 96) / 2.0) * array[i]) / ArrayVisualizer.getCurrentLength())));
+
+ p.addPoint(ArrayVisualizer.windowHalfWidth() + (int) (Circular.getSinOfDegrees(i + 1, ArrayVisualizer.halfCircle()) * ((((ArrayVisualizer.windowWidth() - 64) / 3.0) * array[Math.min(i + 1, ArrayVisualizer.getCurrentLength() - 1)]) / ArrayVisualizer.getCurrentLength())),
+ ArrayVisualizer.windowHalfHeight() - (int) (Circular.getCosOfDegrees(i + 1, ArrayVisualizer.halfCircle()) * ((((ArrayVisualizer.windowHeight() - 96) / 2.0) * array[Math.min(i + 1, ArrayVisualizer.getCurrentLength() - 1)]) / ArrayVisualizer.getCurrentLength())));
+
+ this.mainRender.fillPolygon(p);
+ }
+ }
+ else {
+ Polygon p = new Polygon();
+
+ p.addPoint(ArrayVisualizer.windowHalfWidth(),
+ ArrayVisualizer.windowHalfHeight());
+
+ p.addPoint(ArrayVisualizer.windowHalfWidth() + (int) (Circular.getSinOfDegrees(i, ArrayVisualizer.halfCircle()) * ((ArrayVisualizer.windowWidth() - 64) / CIRC_WIDTH_RATIO)),
+ ArrayVisualizer.windowHalfHeight() - (int) (Circular.getCosOfDegrees(i, ArrayVisualizer.halfCircle()) * ((ArrayVisualizer.windowHeight() - 96) / CIRC_HEIGHT_RATIO)));
+
+ p.addPoint(ArrayVisualizer.windowHalfWidth() + (int) (Circular.getSinOfDegrees(i + 1, ArrayVisualizer.halfCircle()) * ((ArrayVisualizer.windowWidth() - 64) / CIRC_WIDTH_RATIO)),
+ ArrayVisualizer.windowHalfHeight() - (int) (Circular.getCosOfDegrees(i + 1, ArrayVisualizer.halfCircle()) * ((ArrayVisualizer.windowHeight() - 96) / CIRC_HEIGHT_RATIO)));
+
+ this.mainRender.fillPolygon(p);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/visuals/Hoops.java b/src/visuals/Hoops.java
new file mode 100644
index 00000000..9041e6e2
--- /dev/null
+++ b/src/visuals/Hoops.java
@@ -0,0 +1,121 @@
+package visuals;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics2D;
+
+import main.ArrayVisualizer;
+import templates.Visual;
+import utils.Highlights;
+import utils.Renderer;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class Hoops extends Visual {
+ public Hoops(ArrayVisualizer ArrayVisualizer) {
+ super(ArrayVisualizer);
+ }
+
+ @SuppressWarnings("fallthrough")
+ public static void markHoops(int logOfLen, int index, Highlights Highlights, Graphics2D mainRender) {
+ switch(logOfLen) {
+ case 14: if(Highlights.containsPosition(index - 13)) mainRender.setColor(Color.BLACK);
+ case 13: if(Highlights.containsPosition(index - 12)) mainRender.setColor(Color.BLACK);
+ case 12: if(Highlights.containsPosition(index - 11)) mainRender.setColor(Color.BLACK);
+ case 11: if(Highlights.containsPosition(index - 10)) mainRender.setColor(Color.BLACK);
+ case 10: if(Highlights.containsPosition(index - 9)) mainRender.setColor(Color.BLACK);
+ case 9: if(Highlights.containsPosition(index - 8)) mainRender.setColor(Color.BLACK);
+ case 8: if(Highlights.containsPosition(index - 7)) mainRender.setColor(Color.BLACK);
+ case 7: if(Highlights.containsPosition(index - 6)) mainRender.setColor(Color.BLACK);
+ case 6: if(Highlights.containsPosition(index - 5)) mainRender.setColor(Color.BLACK);
+ case 5: if(Highlights.containsPosition(index - 4)) mainRender.setColor(Color.BLACK);
+ case 4: if(Highlights.containsPosition(index - 3)) mainRender.setColor(Color.BLACK);
+ case 3: if(Highlights.containsPosition(index - 2)) mainRender.setColor(Color.BLACK);
+ case 2: if(Highlights.containsPosition(index - 1)) mainRender.setColor(Color.BLACK);
+ default: if(Highlights.containsPosition(index)) mainRender.setColor(Color.BLACK);
+ }
+ }
+
+ @SuppressWarnings("fallthrough")
+ public static void drawFancyFinishHoops(int logOfLen, int index, int position, Graphics2D mainRender) {
+ switch(logOfLen) {
+ case 14: if(index == position - 13) mainRender.setColor(Color.BLACK);
+ case 13: if(index == position - 12) mainRender.setColor(Color.BLACK);
+ case 12: if(index == position - 11) mainRender.setColor(Color.BLACK);
+ case 11: if(index == position - 10) mainRender.setColor(Color.BLACK);
+ case 10: if(index == position - 9) mainRender.setColor(Color.BLACK);
+ case 9: if(index == position - 8) mainRender.setColor(Color.BLACK);
+ case 8: if(index == position - 7) mainRender.setColor(Color.BLACK);
+ case 7: if(index == position - 6) mainRender.setColor(Color.BLACK);
+ case 6: if(index == position - 5) mainRender.setColor(Color.BLACK);
+ case 5: if(index == position - 4) mainRender.setColor(Color.BLACK);
+ case 4: if(index == position - 3) mainRender.setColor(Color.BLACK);
+ case 3: if(index == position - 2) mainRender.setColor(Color.BLACK);
+ case 2: if(index == position - 1) mainRender.setColor(Color.BLACK);
+ default: if(index == position) mainRender.setColor(Color.BLACK);
+ }
+ }
+
+ //TODO: Fix scaling to ensure Hoops close at the center
+ //TODO: Too many rings highlighted at once!!
+ @Override
+ public void drawVisual(int[] array, ArrayVisualizer ArrayVisualizer, Renderer Renderer, Highlights Highlights) {
+ this.mainRender.setStroke(new BasicStroke(1.0f)); //thin strokes significantly increased performance
+
+ //This StackOverflow thread may be related: https://stackoverflow.com/questions/47102734/performances-issue-when-drawing-dashed-line-in-java
+
+ double diameter = 2.0;
+ double diamstep = Math.min(Renderer.getXScale(), Renderer.getYScale());
+
+ for(int i = 0; i < ArrayVisualizer.getCurrentLength(); i++) {
+ if(Highlights.fancyFinishActive()) {
+ if(i < Highlights.getFancyFinishPosition()) {
+ this.mainRender.setColor(Color.GREEN);
+ }
+ else this.mainRender.setColor(getIntColor(array[i], ArrayVisualizer.getCurrentLength()));
+
+ drawFancyFinishHoops(ArrayVisualizer.getLogBaseTwoOfLength(), i, Highlights.getFancyFinishPosition(), this.mainRender);
+ }
+ else {
+ this.mainRender.setColor(getIntColor(array[i], ArrayVisualizer.getCurrentLength()));
+ }
+
+ if(ArrayVisualizer.getCurrentLength() != 2) {
+ markHoops(ArrayVisualizer.getLogBaseTwoOfLength(), i, Highlights, this.mainRender);
+ }
+
+ int radius = (int) (diameter / 2.0);
+
+ this.mainRender.drawOval(ArrayVisualizer.windowHalfWidth() - radius,
+ ArrayVisualizer.windowHalfHeight() - radius + 12,
+ (int) diameter,
+ (int) diameter);
+
+ diameter += diamstep;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/visuals/Mesh.java b/src/visuals/Mesh.java
new file mode 100644
index 00000000..03bbf650
--- /dev/null
+++ b/src/visuals/Mesh.java
@@ -0,0 +1,138 @@
+package visuals;
+
+import java.awt.Color;
+
+import main.ArrayVisualizer;
+import templates.Visual;
+import utils.Highlights;
+import utils.Renderer;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class Mesh extends Visual {
+ public Mesh(ArrayVisualizer ArrayVisualizer) {
+ super(ArrayVisualizer);
+ }
+
+ //TODO: Change these to be more consistent between array lengths. These heights and counts are a bit random.
+ public static int getTriangleHeight(int length, double height) {
+ switch(length) {
+ case 2: height *= 20; break;
+ case 4: height *= 13; break;
+ case 8: height *= 8; break;
+ case 16:
+ case 32: height *= 4.4; break;
+ case 64: height *= 2.3; break;
+ case 128: height *= 2.35; break;
+ case 256: height *= 1.22; break;
+ default: height *= 1;
+ }
+
+ return (int) height;
+ }
+
+ public static int getTrianglesPerRow(int length, int trianglesPerColumn) {
+ int trianglesPerRow;
+
+ switch(length) {
+ case 32:
+ case 64: trianglesPerRow = 4; break;
+ case 128:
+ case 256: trianglesPerRow = 8; break;
+ default: trianglesPerRow = Math.max(length / trianglesPerColumn, 2);
+ }
+
+ return trianglesPerRow;
+ }
+
+ @Override
+ public void drawVisual(int[] array, ArrayVisualizer ArrayVisualizer, Renderer Renderer, Highlights Highlights) {
+ int trih = getTriangleHeight(ArrayVisualizer.getCurrentLength(), ArrayVisualizer.windowHeight() / 20); //Height of triangles to use, Width will be scaled accordingly
+
+ int tripercol = (ArrayVisualizer.windowHeight() / trih) * 2; //Triangles per column
+ int triperrow = getTrianglesPerRow(ArrayVisualizer.getCurrentLength(), tripercol); //Triangles per row
+
+ double triw = (double) ArrayVisualizer.windowWidth() / triperrow; //Width of triangles to use
+
+ double curx = 0;
+ int cury = 15;
+
+ int[] triptsx = new int[3];
+ int[] triptsy = new int[3];
+
+ for(int i = 0; i < ArrayVisualizer.getCurrentLength(); i++){
+ if(Highlights.containsPosition(i) && ArrayVisualizer.getCurrentLength() != 2) {
+ if(ArrayVisualizer.analysisEnabled()) this.mainRender.setColor(Color.WHITE);
+ else this.mainRender.setColor(Color.BLACK);
+ }
+ else {
+ //TODO: Clean up this visual trick
+ if(Highlights.fancyFinishActive() && (i < Highlights.getFancyFinishPosition() && i > Highlights.getFancyFinishPosition() - ArrayVisualizer.getLogBaseTwoOfLength())) {
+ this.mainRender.setColor(Color.GREEN);
+ }
+ else this.mainRender.setColor(getIntColor(array[i], ArrayVisualizer.getCurrentLength()));
+ }
+ //If i/triperrow is even, then triangle points right, else left
+ boolean direction = false;
+ if((i / triperrow) % 2 == 0) direction = true;
+ else direction = false;
+
+ //Make the triangle
+ if(!direction) {
+ //Pointing right
+ triptsx[0] = (int) curx;
+ triptsx[1] = (int) curx;
+ curx += triw;
+ triptsx[2] = (int) curx;
+
+ triptsy[0] = cury;
+ triptsy[2] = cury + (trih / 2);
+ triptsy[1] = cury + trih;
+ }else{
+ //Pointing left
+ triptsx[2] = (int) curx;
+ curx += triw;
+ triptsx[0] = (int) curx;
+ triptsx[1] = (int) curx;
+
+ triptsy[0] = cury;
+ triptsy[2] = cury + (trih / 2);
+ triptsy[1] = cury + trih;
+ }
+
+ //Draw it
+ this.mainRender.fillPolygon(triptsx, triptsy, triptsx.length);
+
+ //If at the end of a row, reset curx
+ //(i != 0 || i != currentLen - 1)
+ if((i + 1) % triperrow == 0){
+ curx = 0;
+ cury += trih / 2;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/visuals/Pixels.java b/src/visuals/Pixels.java
new file mode 100644
index 00000000..a1fad716
--- /dev/null
+++ b/src/visuals/Pixels.java
@@ -0,0 +1,141 @@
+package visuals;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+
+import main.ArrayVisualizer;
+import templates.Visual;
+import utils.Highlights;
+import utils.Renderer;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+final public class Pixels extends Visual {
+ public Pixels(ArrayVisualizer ArrayVisualizer) {
+ super(ArrayVisualizer);
+ }
+
+ @Override
+ public void drawVisual(int[] array, ArrayVisualizer ArrayVisualizer, Renderer Renderer, Highlights Highlights) {
+ if(ArrayVisualizer.linesEnabled()) {
+ //TODO: Wave visual needs to be *heavily* refactored
+ if(ArrayVisualizer.waveEnabled()) {
+ Renderer.setLineY((int) ((ArrayVisualizer.windowHeight() / 4) * Math.sin((2 * Math.PI * ((double) array[1] / ArrayVisualizer.getCurrentLength()))) + ArrayVisualizer.windowHalfHeight()));
+ }
+ else {
+ Renderer.setLineY((int) ((ArrayVisualizer.windowHeight() - 20) - array[0] * Renderer.getYScale()));
+ }
+ for(int i = 0; i < ArrayVisualizer.getCurrentLength(); i++) {
+ int y;
+ if(ArrayVisualizer.waveEnabled()) {
+ y = (int) ((ArrayVisualizer.windowHeight() / 4) * Math.sin((2 * Math.PI * ((double) array[i] / ArrayVisualizer.getCurrentLength()))) + ArrayVisualizer.windowHalfHeight());
+ }
+ else {
+ y = (int) ((ArrayVisualizer.windowHeight() - 20) - (Math.max(array[i], 1) * Renderer.getYScale()));
+
+ // Quick patch to fix the first line being horizontal for some reason
+ if(i == 0) y += ((ArrayVisualizer.windowHeight() - 20) - array[1] * Renderer.getYScale())
+ - ((ArrayVisualizer.windowHeight() - 20) - array[2] * Renderer.getYScale());
+ }
+
+ int width = (int) (Renderer.getXScale() * (i + 1)) - Renderer.getOffset();
+
+ if(width > 0) {
+ if(i > 0) {
+ if(Highlights.fancyFinishActive()) {
+ if(i < Highlights.getFancyFinishPosition()) {
+ lineFancy(this.mainRender, ArrayVisualizer.currentWidth());
+ }
+ else lineClear(this.mainRender, ArrayVisualizer.colorEnabled(), array, i, ArrayVisualizer.getCurrentLength(), ArrayVisualizer.currentWidth());
+
+ drawFancyFinishLine(ArrayVisualizer.getLogBaseTwoOfLength(), i, Highlights.getFancyFinishPosition(), this.mainRender, ArrayVisualizer.currentWidth(), ArrayVisualizer.colorEnabled());
+ }
+ else if(Highlights.containsPosition(i) && ArrayVisualizer.getCurrentLength() != 2) {
+ lineMark(this.mainRender, ArrayVisualizer.currentWidth(), ArrayVisualizer.colorEnabled(), ArrayVisualizer.analysisEnabled());
+ }
+ else lineClear(this.mainRender, ArrayVisualizer.colorEnabled(), array, i, ArrayVisualizer.getCurrentLength(), ArrayVisualizer.currentWidth());
+
+ this.mainRender.drawLine(Renderer.getOffset() + 20, y, Renderer.getLineX() + 20, Renderer.getLineY());
+ }
+ Renderer.setLineX(Renderer.getOffset());
+ Renderer.setLineY(y);
+ }
+ Renderer.setOffset(Renderer.getOffset() + width);
+ }
+ }
+ else {
+ for(int i = 0; i < ArrayVisualizer.getCurrentLength(); i++) {
+ if(i < Highlights.getFancyFinishPosition()) {
+ this.mainRender.setColor(Color.GREEN);
+ }
+ else if(i == Highlights.getFancyFinishPosition() && Highlights.fancyFinishActive()) {
+ if(ArrayVisualizer.colorEnabled()) {
+ this.mainRender.setColor(Color.WHITE);
+ }
+ else this.mainRender.setColor(Color.RED);
+ }
+ else if(ArrayVisualizer.colorEnabled()) {
+ this.mainRender.setColor(getIntColor(array[i], ArrayVisualizer.getCurrentLength()));
+ }
+ else this.mainRender.setColor(Color.WHITE);
+
+ int y = 0;
+ int width = (int) (Renderer.getXScale() * (i + 1)) - Renderer.getOffset();
+
+ boolean drawRect = false;
+ if(Highlights.containsPosition(i) && ArrayVisualizer.getCurrentLength() != 2) {
+ setRectColor(this.extraRender, ArrayVisualizer.colorEnabled(), ArrayVisualizer.analysisEnabled());
+ drawRect = true;
+ }
+
+ if(width > 0) {
+ if(ArrayVisualizer.waveEnabled()) {
+ y = (int) ((ArrayVisualizer.windowHeight() / 4) * Math.sin((2 * Math.PI * ((double) array[i] / ArrayVisualizer.getCurrentLength()))) + ArrayVisualizer.windowHalfHeight());
+ }
+ else {
+ y = (int) ((ArrayVisualizer.windowHeight() - 20) - (array[i] * Renderer.getYScale()));
+ }
+ this.mainRender.fillRect(Renderer.getOffset() + 20, y, Renderer.getDotDimensions(), Renderer.getDotDimensions());
+
+ if(drawRect) {
+ this.extraRender.setStroke(ArrayVisualizer.getThickStroke());
+
+ if(Highlights.fancyFinishActive()) {
+ this.extraRender.fillRect(Renderer.getOffset() + 10, y - 10, Renderer.getDotDimensions() + 20, Renderer.getDotDimensions() + 20);
+ }
+ else {
+ this.extraRender.drawRect(Renderer.getOffset() + 10, y - 10, Renderer.getDotDimensions() + 20, Renderer.getDotDimensions() + 20);
+ }
+
+ this.extraRender.setStroke(new BasicStroke(3f * (ArrayVisualizer.currentWidth() / 1280f))); //TODO: This BasicStroke should have a getDefaultStroke() method
+ }
+ }
+ Renderer.setOffset(Renderer.getOffset() + width);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/visuals/VisualStyles.java b/src/visuals/VisualStyles.java
new file mode 100644
index 00000000..d7b8f683
--- /dev/null
+++ b/src/visuals/VisualStyles.java
@@ -0,0 +1,70 @@
+package visuals;
+
+import main.ArrayVisualizer;
+import utils.Highlights;
+import utils.Renderer;
+
+/*
+ *
+MIT License
+
+Copyright (c) 2019 w0rthy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ *
+ */
+
+public enum VisualStyles {
+ BARS {
+ @Override
+ public void drawVisual(int[] array, ArrayVisualizer ArrayVisualizer, Renderer Renderer, Highlights Highlights) {
+ ArrayVisualizer.getVisuals()[0].drawVisual(array, ArrayVisualizer, Renderer, Highlights);
+ }
+ },
+ CIRCULAR {
+ @Override
+ public void drawVisual(int[] array, ArrayVisualizer ArrayVisualizer, Renderer Renderer, Highlights Highlights) {
+ ArrayVisualizer.getVisuals()[1].drawVisual(array, ArrayVisualizer, Renderer, Highlights);
+ }
+ },
+ HOOPS {
+ @Override
+ public void drawVisual(int[] array, ArrayVisualizer ArrayVisualizer, Renderer Renderer, Highlights Highlights) {
+ ArrayVisualizer.getVisuals()[2].drawVisual(array, ArrayVisualizer, Renderer, Highlights);
+ }
+ },
+ MESH {
+ @Override
+ public void drawVisual(int[] array, ArrayVisualizer ArrayVisualizer, Renderer Renderer, Highlights Highlights) {
+ ArrayVisualizer.getVisuals()[3].drawVisual(array, ArrayVisualizer, Renderer, Highlights);
+ }
+ },
+ PIXELS {
+ @Override
+ public void drawVisual(int[] array, ArrayVisualizer ArrayVisualizer, Renderer Renderer, Highlights Highlights) {
+ ArrayVisualizer.getVisuals()[4].drawVisual(array, ArrayVisualizer, Renderer, Highlights);
+ }
+ };
+
+ public VisualStyles getCurrentVisual() {
+ return this;
+ }
+
+ public abstract void drawVisual(int[] array, ArrayVisualizer ArrayVisualizer, Renderer Renderer, Highlights Highlights);
+}
\ No newline at end of file