diff --git a/.classpath b/.classpath new file mode 100644 index 00000000..7b88b967 --- /dev/null +++ b/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/.gitignore b/.gitignore index 2a713cd9..cee99345 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ -*.jar *.class - +/bin/ \ No newline at end of file diff --git a/.project b/.project new file mode 100644 index 00000000..09bece30 --- /dev/null +++ b/.project @@ -0,0 +1,22 @@ + + + ArrayVisualizer-master + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.zeroturnaround.eclipse.rebelXmlBuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..87b7a7a3 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,13 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/.settings/org.eclipse.jdt.ui.prefs b/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 00000000..7103be72 --- /dev/null +++ b/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.ui.text.custom_code_templates= diff --git a/LICENSE b/LICENSE index 4354f76f..3926e52f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ MIT License Copyright (c) 2019 w0rthy +Copyright (c) 2020 MusicTheorist Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 2abcf067..e06f4f94 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,180 @@ -# ArrayVisualizer -Sorting Visualizer/Audiolizer +# w0rthy's Array Visualizer, Revamped +[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=Q5QLCFZ8G7WY6¤cy_code=USD&source=url) -## Videos -[Vertical Pyramid](https://www.youtube.com/watch?v=QOYcpGnHH0g) +**DEPRECATED** - Visit the new home of ArrayV over here where I'll be contributing from time to time! https://github.com/Gaming32/ArrayV-v4.0 +I'll be working on a brand new algorithm visualizer of my own over the next few months, so stay tuned... Visit our community Discord for updates!! -[Dynamic Hoops](https://www.youtube.com/watch?v=S0RtR2Yllzk) +Over 75 sorting algorithms animated with 12 unique graphic designs -[TriMesh](https://www.youtube.com/watch?v=Zc__8qaLfJk) +Follow the project's development behind-the-scenes in our Discord: https://discord.com/invite/2xGkKC2 -[Horizontal Pyramid](https://www.youtube.com/watch?v=vmT3XUBoxiQ) +This new version of the program features additions inspired by Timo Bingmann's "The Sound of Sorting" and w0rthy's updates to the original visualizer. -[Static Hoops](https://www.youtube.com/watch?v=jXs1y3tCKQg) +To compile (After -cp, use ";" with Windows and ":" with Mac/Linux): +``` +ant +java -cp bin;lib/classgraph-4.8.47.jar main.ArrayVisualizer +``` +To build a runnable jar, simply run Apache Ant inside the 'dist' directory! -[Christmas Tree](https://www.youtube.com/watch?v=xY1tiHzo8mE) +### Features: +- 40+ new sorting algorithms +- Updated visuals and sound effects +- You can select different array sizes now +- New shuffle types, including reversed, mostly similar numbers, almost sorted, and already sorted +- Skip Sort button +- Softer sounds toggle +- A real time in milliseconds estimate +- Toggle shuffle animation +- Toggle Timo Bingmann's "end sweep" animation +- Refactored / optimized code -[Variable Width TriMesh](https://www.youtube.com/watch?v=0tr6AtLu4pg) +## 6/8/2020 - Version 3.5 +- NEW VISUALS: Sine Wave and Wave Dots!! +- New sort: Bogobogosort +- The bogo shuffle method is now unbiased +- MultipleSortThreads further refactored +- Visuals, VisualStyles enum, and Renderer significantly refactored (more to come!) -[Color Circle](https://www.youtube.com/watch?v=sVYtGyPiGik) +## 6/4/2020 - Version 3.2 +- New sort: Optimized Cocktail Shaker Sort +- Significant refactoring for MultipleSortThreads and RunAllSorts +- "Run All" button approx. time simplified +- Modified delays for Binary Gnomesort +- Documentation of GCC's median-of-three pivot selection in Introsort -# How to use +## 6/3/2020 - Version 3.12 +- Counting Sort fixed +- Optimized Bubblesort now optimized for already sorted inputs +- Speeds for Quicksorts and Weave Merge during "Run All Sorts" improved -### Build -``` -mkdir -p dist target -javac src/array/visualizer/ArrayVisualizer.java -sourcepath src -d target/ -jar -cvfm dist/ArrayVisualizer.jar manifest.mf -C target/ ./ -``` +## 6/2/2020 - Version 3.11 +- Minor update to MIT license +- Fixed typo in Flipped Min Heapsort +- Improved highlights on Heapsorts (Already sorted heaps now display redundant comparisons) +- Bug fix for Patiencesort on reversed arrays +- Quicksorts exhibiting worst-case behavior during "Run All Sorts" run much faster +- Same tweak as above to Weave Merge Sort -### Run -``` -java -jar dist/ArrayVisualizer.jar -``` -======= \ No newline at end of file +## 5/30/2020 - Version 3.1 +- Error messages with detailed information will now appear within the program! +- Sound effects are now consistent on all platforms +- New sort: "Flipped Min Heap Sort" by 'landfillbaby'! +- Minor changes to code organization +- New webhook to my Discord server! Check it out here: https://discord.com/invite/2xGkKC2 + +## 5/22/2020 - Version 3.01 +- Quick bug fix to the "Linked Dots" visual; + The first line is no longer horizontal. + +## 5/21/2020 - Version 3.0 is now released! +- Sound effects are much more pleasant at slower speeds +- Revamped "Run All Sorts" (It is now easier to create your own sequence of sorts!) +- More accurate delay algorithm +- Improved random shuffle algorithm (now with 0% bias!) +- Cleaner statistics +- Sort an array up to 16,384 (2^14) numbers! +- The "green sweep" animation also verifies an array is properly sorted after watching a sort. + If a sort fails, a warning message pops up, highlighting where the first out-of-order item is. +- Minor tweak to the sort time method. It should be a slight bit more accurate now. +- Slowsort and Sillysort's comparisons are now shown. +- Gravity Sort has a more detailed visual now +- Pancake Sorting is fixed +- Counting Sort is fixed +- Holy Grail Sort is enabled, but just note that it's a mock algorithm; not finished yet. +- "Auxillary" typo fixed; program now says 'Writes to Auxiliary Array(s)' +- Bug fixes and minor tweaks + - Minor fixes to "Skip Sort" button + - Weird static line bug with linked dots squashed + - Other miscellaneous fixes and changes here and there + +## 10/19/2019 - Version 2.1 +- Both Odd-Even Mergesorts now display comparisons +- PDQSort's Insertion Sorts have been slowed down +- New sorts, courtesy of Piotr Grochowski (https://github.com/PiotrGrochowski/ArrayVisualizer): + - Iterative Pairwise Sorting Network + - Recursive Pairwise Sorting Network + - Recursive Combsort + +## 10/13/2019 - Version 2.0 is now released! +- Now includes 73 sorting algorithms, with 2 more that will be finished in the future + - NEW SORTS: + - Unoptimized Bubble Sort + - Rotation-based In-Place Merge Sort + - "Lazy Stable Sort" from Andrey Astrelin's GrailSort + - Grail sorting with a static buffer + - Grail sorting with a dynamic buffer + - Andrey Astrelin's "SqrtSort" + - CircleSort + - Introspective CircleSort + - Orson Peters' "Pattern-Defeating Quicksort" (PDQSort) + - Branchless PDQSort + - Morween's implementation of "Poplar Heapsort" + - Recursive Binary Quicksort + - Iterative Binary Quicksort + - Iterative Bitonic Sort + - Iterative Odd-Even Mergesort + - "Bubble Bogosort" + - "Exchange Bogosort" + - Treesort + - Optimized Gnomesort with Binary Search + - "Cocktail Mergesort" (https://www.youtube.com/watch?v=fWubJgIWyxQ) + - NOTE: "Quick Shell Sort" has been removed. +- Significantly refactored code, more object-oriented +- Optimized visuals -- the program runs smoother than ever! +- Plug-and-play functionality -- using classgraph, you can now easily add your own sorting algorithms to the program! Documentation on that will be available in the future. +- Sort delay system redesigned -- you can now change the speed of the program in the middle of a delayed compare or swap +- Speed dialogue is now disabled while other windows are open +- WikiSort no longer gets stuck on sorting its internal buffer +- Tweaks to TimSort, mostly reimplementing its binary insertion sort +- Binary Insertion Sort is now stable +- The write/swap counts for inputs already sorted are fixed +- The main/auxillary array write counts for Bottom-up Merge are fixed +- Shuffling the array now clears the statistics +- The highest pitches of the program's sound effects are fixed +- Speeds for the "green sweep" and shuffling animations have been tweaked +- Shatter Sort's highlights slightly tweaked +- GrailSort's highlights slightly tweaked + +**KNOWN BUGS:** +- Certain sorts (comb sort, radix sorts) cause the program to forget the current speed +- Certain sorts do not work with the "Skip Sort" button +- Missing soundfont +- SkaSort and HolyGrailSort produce errors -- this is normal, they aren't finished yet +- No circular pointer -- will be fixed soon + +**PLANS FOR FUTURE RELEASES:** +- Javadocs!! +- SkaSort +- "Holy Grail Sort" +- Options to: + - Enter in your own set of numbers + - Select CombSort gap sequence + - Select ShellSort gap sequence + - Change TimSort "minrun" value + - Change IntroSort threshold for insertion/heap sort + - Change Simple Shatter Sort rate(?) + - Stop Run All Sorts(?) + - Stop TimeSort(?) +- Pre-shuffled arrays +- Organize list of sorts into more categories +- Run All Sorts in specific category +- Subheadings for customizable sorts (e.g. display the number of buckets during a bucket sort) +- "Many Similar" distribution ((i/5) * 5, as an example) +- "Pipe organ" distribution (half ascending, half descending) +- Fixed circular pointer with much cleaner math +- Toogle between pointer and black bar with circular visuals +- Refactor/reorganize prompts and frames +- Cleaner: + - Tree Sort + - getters/setters + - method parameters +- Small organizational changes + +**If you are experiencing performance issues in Windows 10, look here: https://superuser.com/questions/988379/how-do-i-run-java-apps-upscaled-on-a-high-dpi-display** + +An executable .jar file is available in the dist folder. Have fun! + +Videos this program is featured in: + +https://www.youtube.com/playlist?list=PL5w_-zMAJC8tSgmfaltMMj7Kn390eRzMq diff --git a/build.xml b/build.xml new file mode 100644 index 00000000..9b54fdea --- /dev/null +++ b/build.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dist/arrayVisualizer.jar b/dist/arrayVisualizer.jar new file mode 100644 index 00000000..a22f4497 Binary files /dev/null and b/dist/arrayVisualizer.jar differ diff --git a/dist/build.xml b/dist/build.xml new file mode 100644 index 00000000..7ee04433 --- /dev/null +++ b/dist/build.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dist/jar-in-jar-loader.zip b/dist/jar-in-jar-loader.zip new file mode 100644 index 00000000..7e5a3ab3 Binary files /dev/null and b/dist/jar-in-jar-loader.zip differ diff --git a/lib/classgraph-4.8.47.jar b/lib/classgraph-4.8.47.jar new file mode 100644 index 00000000..7a1a1f9a Binary files /dev/null and b/lib/classgraph-4.8.47.jar differ diff --git a/manifest.mf b/manifest.mf deleted file mode 100644 index 83bd0727..00000000 --- a/manifest.mf +++ /dev/null @@ -1,3 +0,0 @@ -Manifest-Version: 1.0 -Created-By: 1.8.0_181 (Oracle Corporation) -Main-Class: array.visualizer.ArrayVisualizer diff --git a/src/UtilFrame.form b/src/UtilFrame.form index db96cde1..13edae8e 100644 --- a/src/UtilFrame.form +++ b/src/UtilFrame.form @@ -32,16 +32,19 @@ - - - - - - - + + + + + + + + + + - + @@ -54,11 +57,13 @@ - + + + - + @@ -106,5 +111,13 @@ + + + + + + + + diff --git a/src/ViewPrompt.form b/src/ViewPrompt.form index 774a335b..9860af11 100644 --- a/src/ViewPrompt.form +++ b/src/ViewPrompt.form @@ -31,26 +31,16 @@ - - - - - - - - - - - - - - - - + + + + + + - + @@ -85,21 +75,8 @@ - - - - - - - - - - - - - - - + + @@ -188,45 +165,5 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/array/visualizer/AAFrame.java b/src/array/visualizer/AAFrame.java deleted file mode 100644 index f03f9b38..00000000 --- a/src/array/visualizer/AAFrame.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package array.visualizer; - -/** - * - * @author S630690 - */ -interface AAFrame { - abstract void reposition(); - abstract boolean isVisible(); - abstract void dispose(); -} diff --git a/src/array/visualizer/ArrayController.java b/src/array/visualizer/ArrayController.java deleted file mode 100644 index 2b84dd74..00000000 --- a/src/array/visualizer/ArrayController.java +++ /dev/null @@ -1,39 +0,0 @@ -package array.visualizer; - -import java.util.ArrayList; - -public class ArrayController -{ - public final int [] array; - public final int length; - public final ArrayList marked; - public long aa; - public long comps; - - public ArrayController(int length) - { - array = new int[length]; - this.length = length; - marked = new ArrayList<>(); - aa = 0; - comps = 0; - } - - public void clearMarked() - { - for(int i = 0; i < length; i++) - marked.set(i, -5); - } - - public int get(int index) - { - aa ++; - return array[index]; - } - - public int compare(int a, int b) - { - comps ++; - return Integer.compare(array[a], array[b]); - } -} diff --git a/src/array/visualizer/ArrayVisualizer.java b/src/array/visualizer/ArrayVisualizer.java deleted file mode 100644 index 7e56aa62..00000000 --- a/src/array/visualizer/ArrayVisualizer.java +++ /dev/null @@ -1,814 +0,0 @@ -package array.visualizer; - -import array.visualizer.sort.*; - -import java.awt.Color; -import java.awt.Font; -import java.awt.Graphics; -import java.awt.Image; -import java.util.ArrayList; -import javax.sound.midi.MidiChannel; -import javax.sound.midi.MidiSystem; -import javax.sound.midi.Synthesizer; -import javax.swing.JFrame; - -import static array.visualizer.utils.Swaps.*; -import java.awt.BasicStroke; -import java.awt.Graphics2D; -import java.awt.Polygon; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.sound.midi.Instrument; -import javax.swing.JOptionPane; - -public class ArrayVisualizer { - - static final JFrame window = new JFrame(); - - static ArrayController arrayController = new ArrayController(1000); - static String heading = ""; - static int frames; - static int snd = 0; - static long nanos; - static Font fon = new Font("TimesRoman", Font.PLAIN, (int)(640/1280.0*25)); - - static boolean CIRCLEDRAW = false; - static boolean COLORONLY = false; - static boolean PIXELDRAW = false; - static boolean DISPARITYDRAW = false; - static boolean LINKEDPIXELDRAW = false; - static boolean MESHDRAW = false; - static boolean PYRAMIDDRAW = false; - static boolean DRAWFLIPPED = false; - static boolean XMASDRAW = false; - - static boolean SOUND = false; - static double SOUNDMUL = 1.0; - static double SLEEPRATIO = 1.0; - static UtilFrame uf; - static ViewPrompt v; - static Synthesizer synth; - static MidiChannel chan; - static Thread sortingThread; - static boolean SHUFFLEANIM = true; - - static long starttime = 0; - static long stoptime = 0; - static boolean running = false; - static long sleeptime = 0; - - static long rtupdatefreq = 100; //How frequently to update the real time display in ms - static double realt = 0d; - static long lastrtupdate = 0; - - static int COLORSTRAT = 1; //0 = Solid, 1 = Rainbow, 2 = Segments - static Color COLORSTRAT0col = new Color(0, 204, 0); - static ArrayList COLORSTRAT2cols = new ArrayList(); - - static String[] ComparativeSorts = "Selection!Bubble!Insertion!Double Selection!Cocktail Shaker!Quick!Merge!Merge OOP!Weave Merge!Max Heap!Shell".split("!"); - static String[] DistributiveSorts = "Radix LSD!Radix MSD!Radix LSD In-Place!Binary Quicksort!Gravity!Shatter!Counting!Time!Bogo".split("!"); - - static int cx = 0; - static int cy = 0; - - public static double calcVel(){ - double count = 1; - for(int i : arrayController.marked) - if(i!=-5) - count+=0.75; - return count; - } - - public static synchronized void SetSound(boolean val){ - SOUND = val; - } - - static double addamt = 0.0; - public static void sleep(double milis){ - if(milis <= 0) - return; - double tmp = (milis*(1000.0/arrayController.length)); - tmp = tmp * (1/SLEEPRATIO); - addamt += tmp; - if(addamt<1.0) - return; - try{ - long actual = System.nanoTime(); - Thread.sleep((long)addamt); - actual = (System.nanoTime()-actual); - addamt-=(double)actual/1000000.0; - if(running) - sleeptime+=actual; - }catch(Exception ex){ - Logger.getLogger(ArrayVisualizer.class.getName()).log(Level.SEVERE, null, ex); - } - } - - public static void main(String[] args) throws Exception { - - //Segment Colors - COLORSTRAT2cols.add(new Color(0, 204, 0)); - COLORSTRAT2cols.add(new Color(204, 0, 0)); - //COLORSTRAT2cols.add(new Color(204, 204, 0)); - - synth = MidiSystem.getSynthesizer(); - synth.open(); - synth.loadAllInstruments(synth.getDefaultSoundbank()); -// int s = 0; -// for(Instrument i : synth.getAvailableInstruments()){ -// System.out.println(s+" "+i.getName()); -// s++; -// } - chan = synth.getChannels()[0]; - for(Instrument i : synth.getLoadedInstruments()) - if(i.getName().toLowerCase().trim().contains("sine")){ - chan.programChange(i.getPatch().getProgram()); - break; - } - - if(chan.getProgram() == 0) - JOptionPane.showMessageDialog(null, "Could not find a valid instrument. Sound is disabled"); - //chan.programChange(synth.getLoadedInstruments()[197].getPatch().getProgram()); - - for(int i = 0; i < arrayController.length; i++) - arrayController.marked.add(-5); - rianr(arrayController.array); - - window.setSize(640, 480); - window.setLocation(0, 0); - window.setVisible(true); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setTitle("Array Visualizer"); - - uf = new UtilFrame(window); - - //AUDIO THREAD - new Thread() - { - @Override - public void run() - { - while(true){ - chan.allNotesOff(); - if(SOUND == false){ - continue; - } - - int tmp = 1; - for(int i : arrayController.marked) - if(i != -5) - tmp++; - for(int i : arrayController.marked) - if(i != -5){ - int pitch = (int)Math.round((double)arrayController.array[Math.min(Math.max(i, 0), arrayController.length-1)]/arrayController.length*96+16); - //int vel = (int)(((128-pitch)/320.0+0.4) * (128.0/Math.pow(tmp, 0.33))); - //int vel = (int)(64.0/Math.pow(tmp, 0.25)); - //int vel = (int)((64.0-Math.pow((tmp-1)*10, 0.67d))*SOUNDMUL); - int vel = (int)(64.0/(Math.pow((double)tmp, 0.6)) * ((128-pitch)/64.0+1.0)*0.67*SOUNDMUL); //one day - chan.noteOn(pitch, vel); - } - //((int)((127-(array[Math.min(Math.max(i, 0), array.length-1)]/16.0))/Math.sqrt(calcVel())))*5 - //double tmp = (array[Math.min(Math.max(i, 0), array.length-1)]/32.0+47); - //chan.setPitchBend(8192*2-(int)((tmp-Math.floor(tmp))*8192*2)); - /* - ArrayList tmp = new ArrayList(); - for(int i : marked) - if(i != -5) - tmp.add(i); - - if(tmp.size() > 0){ - do{ - int i = tmp.get(snd%tmp.size()); - snd++; - chan.noteOn(array[Math.min(Math.max(i, 0), array.length-1)]/32+47, 127); - double tmpd = (array[Math.min(Math.max(i, 0), array.length-1)]/32.0+47); - chan.setPitchBend(8192*2-(int)((tmpd-Math.floor(tmpd))*8192*2)); - }while(false);}*/ - try{sleep(1);}catch(Exception ex){ - Logger.getLogger(ArrayVisualizer.class.getName()).log(Level.SEVERE, null, ex); - } - } - } - }.start(); - - //DRAW THREAD - new Thread(){ - @Override - public void run(){ - int cw = 640; - int ch = 480; - Image img = window.createVolatileImage(cw, ch); - //Graphics g = window.getGraphics(); - Graphics2D g = (Graphics2D)img.getGraphics(); - - double xscl, yscl; - while(true){ - - if(window.getWidth()!=cw|| window.getHeight()!=ch || window.getX() != cx || window.getY() != cy){ - uf.reposition(); - if(v != null && v.isVisible()) - v.reposition(); - cx = window.getX(); - cy = window.getY(); - } - - if(window.getWidth()!=cw|| window.getHeight()!=ch){ - cw = window.getWidth(); - ch = window.getHeight(); - img = window.createVolatileImage(cw, ch); - fon = new Font("TimesRoman", Font.PLAIN, (int)(cw/1280.0*25)); - g = (Graphics2D)img.getGraphics(); - - } - int gamt = 32;//(int)(frames/1000.0%64); - g.setColor(new Color(gamt, gamt, gamt)); - g.fillRect(0, 0, img.getWidth(null), img.getHeight(null)); - xscl = (double)window.getWidth()/arrayController.length; - yscl = (double)(window.getHeight()-30)/arrayController.length; - int amt = 0; - int circamt = arrayController.length/2; - int linkedpixdrawx = 0; - int linkedpixdrawy = 0; - frames++; - - int halfwidth = window.getWidth()/2; - int halfheight = window.getHeight()/2; - int dotw = (int)(2*(window.getWidth()/640.0)); - int doth = (int)(2*(window.getHeight()/480.0)); - - int sortErrors = 0; - - float strokew = 3f*(window.getWidth()/1920f); - g.setStroke(new BasicStroke(strokew)); - - //CHRISTMAS TREE? - if(XMASDRAW){ - - double trunksize = (double)ch*0.1; - int trunkstart = arrayController.length-(int)(trunksize/yscl); - - double ornamentsize = (double)ch*0.1; - int ornamentend = (int)(ornamentsize/yscl); - - int width = 0; - - for(int i = 0; i < arrayController.length; i++){ - //Check for sort errors in array - if(i>0 && arrayController.array[i]trunkstart) - g.setColor(new Color(102, 34, 0)); - else - g.setColor(new Color(0, 102, 0)); - - if(jtrunkstart){ //TRUNK - width = (int)(cw*0.2); - }else //TREE - width = (int)((((j-ornamentend)%85)*0.005+0.075)*cw); - - int step = (int)(i*yscl+yscl)-(int)(i*yscl); - g.fillRect(halfwidth-width/2, (int)(i*yscl)+32, width, step); - - } - } - //HOOP DRAWING (PYRAMID CIRCLES) - else if(PYRAMIDDRAW && CIRCLEDRAW){ - - g.setStroke(new BasicStroke(1.0f)); //significantly increased performance - - double maxdiam = (double)Math.min(cw, ch-32); - double diameter = maxdiam; - double diamstep = Math.min(xscl, yscl); - - for(int i = arrayController.length-1; i >= 0; i--){ - //Check for sort errors in array - if(i>0 && arrayController.array[i]0 && arrayController.array[i]0 && arrayController.array[i]0 && arrayController.array[i]0) - g.drawLine(linkedpixX, linkedpixY, linkedpixdrawx, linkedpixdrawy); - linkedpixdrawx = linkedpixX; - linkedpixdrawy = linkedpixY; - } - } - else{ - Polygon p = new Polygon(); - p.addPoint(halfwidth, halfheight); - p.addPoint(halfwidth+(int)(sinval*((window.getWidth()-64)/2.0*len)), halfheight-(int)(cosval*((window.getHeight()-96)/2.0*len))); - p.addPoint(halfwidth+(int)(Math.sin((i+1)*Math.PI/circamt)*((window.getWidth()-64)/2.0*len)), halfheight-(int)(Math.cos((i+1)*Math.PI/circamt)*((window.getHeight()-96)/2.0*len))); - g.fillPolygon(p); - } - //g.drawLine(halfwidth, halfheight, halfwidth+(int)(sinval*((window.getWidth()-64)/2.0*(array[i]/(double)array.length))), halfheight-(int)(cosval*((window.getHeight()-96)/2.0*(array[i]/(double)array.length)))); - } - //PIXELS ONLY - else if(PIXELDRAW){ - g.fillRect(halfwidth+(int)(sinval*((window.getWidth()-64)/2.0*(arrayController.array[i]/(double)arrayController.length))), halfheight-(int)(cosval*((window.getHeight()-96)/2.0*(arrayController.array[i]/(double)arrayController.length))), dotw, doth); - if(LINKEDPIXELDRAW){ - if(i>0) - g.drawLine(halfwidth+(int)(sinval*((window.getWidth()-64)/2.0*(arrayController.array[i]/(double)arrayController.length))), halfheight-(int)(cosval*((window.getHeight()-96)/2.0*(arrayController.array[i]/(double)arrayController.length))), linkedpixdrawx, linkedpixdrawy); - linkedpixdrawx = halfwidth+(int)(sinval*((window.getWidth()-64)/2.0*(arrayController.array[i]/(double)arrayController.length))); - linkedpixdrawy = halfheight-(int)(cosval*((window.getHeight()-96)/2.0*(arrayController.array[i]/(double)arrayController.length))); - } - } - //LENGTH AND COLOR - else{ - Polygon p = new Polygon(); - p.addPoint(halfwidth, halfheight); - p.addPoint(halfwidth+(int)(Math.sin((i)*Math.PI/circamt)*((window.getWidth()-64)/2.0*(arrayController.array[i]/(double)arrayController.length))), halfheight-(int)(Math.cos((i)*Math.PI/circamt)*((window.getHeight()-96)/2.0*(arrayController.array[i]/(double)arrayController.length)))); - p.addPoint(halfwidth+(int)(Math.sin((i+1)*Math.PI/circamt)*((window.getWidth()-64)/2.0*(arrayController.array[Math.min(i+1, arrayController.length-1)]/(double)arrayController.length))), halfheight-(int)(Math.cos((i+1)*Math.PI/circamt)*((window.getHeight()-96)/2.0*(arrayController.array[Math.min(i+1, arrayController.length-1)]/(double)arrayController.length)))); - g.fillPolygon(p); - //g.drawLine(halfwidth, halfheight, halfwidth+(int)(sinval*((window.getWidth()-64)/2.0*(array[i]/(double)array.length))), halfheight-(int)(cosval*((window.getHeight()-96)/2.0*(array[i]/(double)array.length)))); - } - } - else - for(int i = 0; i < arrayController.length; i++){ - if(i>0 && arrayController.array[i]0){ - if(COLORONLY){ - y = (int)(window.getHeight()-750*yscl); - g.fillRect(amt, y, width, Math.max((int)(750*yscl), 1)); - g.setColor(getRevColor()); - g.fillRect((int)(i*xscl), y, width, 6); - } - else if(PIXELDRAW){ - y = (int)(window.getHeight()-arrayController.array[i]*yscl); - g.fillRect(amt-dotw/2, y-doth/2, dotw, doth); - if(LINKEDPIXELDRAW){ - if(i>0) - g.drawLine(amt, y, linkedpixdrawx, linkedpixdrawy); - linkedpixdrawx = amt; - linkedpixdrawy = y; - } - } - else{ - y = (int)(window.getHeight()-arrayController.array[i]*yscl); - g.fillRect(amt, y, width, Math.max((int)(arrayController.array[i]*yscl), 1)); - g.setColor(getRevColor()); - //g.fillRect(amt, y, width, 6); FILAMENT - } - } - amt+=width; - } - - int coltmp = 255;//(int)Math.abs(Math.sin(frames*0.01)*255); - g.setColor(new Color(coltmp, coltmp, coltmp)); - Font f = g.getFont(); - g.setFont(fon); - g.drawString(heading, 10, (int)(cw/1280.0*20)+30); - g.drawString(formatNum(arrayController.comps)+" Comparison"+(arrayController.comps==1?"":"s"), 10, (int)(cw/1280.0*40)+30); - g.drawString(formatNum(arrayController.aa)+" Array Access"+(arrayController.aa==1?"":"es"), 10, (int)(cw/1280.0*60)+30); - int sortpercent = (int)((double)(arrayController.length-sortErrors)/(double)arrayController.length*100.0); - g.drawString(String.format("%d%% Sorted (%d Segment%c)", sortpercent, sortErrors+1, sortErrors==0?' ':'s'), 10, (int)(cw/1280.0*80)+30); - //g.drawString(String.format("%d Segment%c", sortErrors+1, sortErrors==0?' ':'s'), 10, (int)(cw/1280.0*100)+30); - double slpt = (double)sleeptime/1000000000.0; - if(System.currentTimeMillis()-lastrtupdate > rtupdatefreq){ - realt = (double)(running?(System.nanoTime()-starttime-sleeptime):(stoptime-starttime-sleeptime))/1000000.0; - lastrtupdate = System.currentTimeMillis(); - } - g.drawString(String.format("Real Time: %.2fms", realt), 10, (int)(cw/1280.0*100)+30); - g.drawString(String.format("Sleep Time: %.2fs", slpt), 10, (int)(cw/1280.0*120)+30); - g.setFont(f); - Graphics g2 = window.getGraphics(); - g2.setColor(Color.BLACK); - g2.drawImage(img, 0, 0, null); - } - } - - //0 = Solid, 1 = Rainbow, 2 = Segments - public Color getIntColor(int i, int segnum) { - if(COLORSTRAT == 1) - return Color.getHSBColor(((float)i/arrayController.length), 1.0F, 0.8F); - else if(COLORSTRAT == 2){ - return COLORSTRAT2cols.get(segnum%COLORSTRAT2cols.size()); - } - return COLORSTRAT0col; - } - public Color getRevColor(){ - return getIntColor((int)(Math.sin(frames/66.67)*arrayController.length), 0); - } - }.start(); - - uf.setVisible(false); - v = new ViewPrompt(window); - while(v.isVisible()) Thread.sleep(1); - uf.setVisible(true); - - //keep on keeping on - while(window.isActive())Thread.sleep(100); - } - - public static void refresharray() throws Exception { - clearmarked(); - if(running){ - stoptime = System.nanoTime(); - running = false; - Thread.sleep(1000); - } - - Thread.sleep(1000); - boolean solved = true; - for(int i = 0; i < arrayController.length; i++){ - if(arrayController.array[i]!=i) - solved = false; - arrayController.marked.set(0, i); - } - for(int i = 0; i < arrayController.length; i++) - arrayController.array[i] = i; - //System.out.println(solved); - arrayController.marked.set(0, -5); - heading = ""; - arrayController.aa = 0; - arrayController.comps = 0; - shuffle(arrayController.array); - clearmarked(); - Thread.sleep(500); - starttime = System.nanoTime(); - sleeptime = 0; - running = true; - } - - public static int getDigit(int a, int power, int radix){ - return (int) (a / Math.pow(radix, power)) % radix; - } - - public static int[] rianr(int [] arr) { - for (int i = 0; i < arr.length; i++) - arr[i] = i; - shuffle(arr); - return arr; - } - - public static void clearmarked(){ - arrayController.clearMarked(); - } - - public static void shuffle(int[] array) { - String tmp = heading; - heading = "Shuffling..."; - for(int i = 0; i < array.length; i++){ - swap(arrayController, i, (int)(Math.random()*array.length)); - arrayController.aa-=2; - if(SHUFFLEANIM) - sleep(1); - } - heading = tmp; - } - - public static int sleepTime(double d) { - return (int)(arrayController.length*d)/4; - } - - public synchronized static void RunAllSorts(){ - if(sortingThread != null) - while(sortingThread.isAlive()) try { - Thread.sleep(100); - } catch (InterruptedException ex) { - Logger.getLogger(ArrayVisualizer.class.getName()).log(Level.SEVERE, null, ex); - } - - SetSound(true); - sortingThread = new Thread(){ - @Override - public void run(){ - try{ - for (Sort sort : new Sort[]{ - new SelectionSort(), - new BubbleSort(), - new InsertionSort(), - new CocktailShaker(), - new ShellSort(), - new MergeSortOOP(), - new MergeSort(), - new WeaveMerge(), - new MaxHeapSort(), - new QuickSort(), - new CountingSort(), - new TimeSort(4), - new GravitySort(), - new RadixLSD(4), - new RadixMSD(4), - new BinaryQuickSort(), - new RadixLSDInPlace(2), - new RadixLSDInPlace(10)} - ) - { - chan.allNotesOff(); - refresharray(); - heading = sort.name(); - sort.sort(arrayController); - } - }catch (Exception ex){ - Logger.getLogger(ArrayVisualizer.class.getName()).log(Level.SEVERE, null, ex); - } - SetSound(false); - stoptime = System.nanoTime(); - running = false; - chan.allNotesOff(); - clearmarked(); - } - }; - sortingThread.start(); - } - - public static void ReportComparativeSort(int n){ - if(sortingThread != null && sortingThread.isAlive()) - return; - - final int num = n; - SetSound(true); - sortingThread = new Thread(){ - @Override - public void run(){ - try{ - refresharray(); - Sort sort; - switch (num) - { - case 0: - sort = new SelectionSort();break; - case 1: - sort = new BubbleSort();break; - case 2: - sort = new InsertionSort();break; - case 3: - sort = new DoubleSelection();break; - case 4: - sort = new CocktailShaker();break; - case 5: - sort = new QuickSort();break; - case 6: - sort = new MergeSort();break; - case 7: - sort = new MergeSortOOP();break; - case 8: - sort = new WeaveMerge();break; - case 9: - sort = new MaxHeapSort();break; - case 10: - sort = new ShellSort();break; - default: - sort = null; break; - } - if (sort != null) - { - heading = sort.name(); - sort.sort(arrayController); - } - }catch(Exception ex) - { - Logger.getLogger(ArrayVisualizer.class.getName()).log(Level.SEVERE, null, ex); - } - SetSound(false); - stoptime = System.nanoTime(); - running = false; - } - }; - sortingThread.start(); - } - - public static void ReportDistributiveSort(int n){ - if(sortingThread != null && sortingThread.isAlive()) - return; - int bas = 10; - if(n != 3 && n != 5 && n != 7) - if(n != 4) - try{bas = Integer.parseInt(JOptionPane.showInputDialog(null, "Enter Base for Sort"));}catch(Exception ex){ - Logger.getLogger(ArrayVisualizer.class.getName()).log(Level.SEVERE, null, ex); - } - else - try{bas = Integer.parseInt(JOptionPane.showInputDialog(null, "Enter Size of Partitions"));}catch(Exception ex){ - Logger.getLogger(ArrayVisualizer.class.getName()).log(Level.SEVERE, null, ex); - } - - final int base = Math.max(bas, 2); - final int num = n; - SetSound(true); - sortingThread = new Thread(){ - @Override - public void run(){ - try{ - refresharray(); - Sort sort; - switch (num) { - case 0: - sort = new RadixLSD(base);break; - case 1: - sort = new RadixMSD(base);break; - case 2: - sort = new RadixLSDInPlace(base);break; - case 3: - sort = new BinaryQuickSort();break; - case 4: - sort = new GravitySort();break; - case 5: - sort = new ShatterSorts(base);break; - case 6: - sort = new CountingSort();break; - case 7: - sort = new TimeSort(base);break; - case 8: - sort = new BogoSort();break; - default: - sort = null; break; - } - if (sort != null) - { - heading = sort.name(); - sort.sort(arrayController); - } - }catch(Exception ex){ - Logger.getLogger(ArrayVisualizer.class.getName()).log(Level.SEVERE, null, ex); - } - SetSound(false); - stoptime = System.nanoTime(); - running = false; - } - }; - sortingThread.start(); - } - - public static String formatNum(long a){ - if(a<0) - return "OVERFLOW"; - if(a>1000L*1000L*1000L*1000L) - return String.format("%.2fT", (double)a/1000000000000.0); - if(a>1000L*1000L*1000L) - return String.format("%.2fB", (double)a/1000000000.0); - if(a>1000L*1000L) - return String.format("%.2fM", (double)a/1000000.0); - else if(a>1000L) - return String.format("%.2fK", (double)a/1000.0); - else - return ""+a; - } -} diff --git a/src/array/visualizer/SortPrompt.java b/src/array/visualizer/SortPrompt.java deleted file mode 100644 index 5dd395f0..00000000 --- a/src/array/visualizer/SortPrompt.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package array.visualizer; - -import static array.visualizer.ArrayVisualizer.*; -import javax.swing.JFrame; - -/** - * - * @author S630690 - */ -public class SortPrompt extends javax.swing.JFrame implements AAFrame{ - - JFrame f; - /** - * Creates new form SortPrompt - */ - public SortPrompt(JFrame f) { - this.f = f; - setAlwaysOnTop(true); - setUndecorated(true); - initComponents(); - jList2.setListData(ComparativeSorts); - jList1.setListData(DistributiveSorts); - setLocation(f.getX()+(f.getWidth()-getWidth())/2, f.getY()+(f.getHeight()-getHeight())/2); - setVisible(true); - } - public void reposition(){ - setLocation(f.getX()+(f.getWidth()-getWidth())/2, f.getY()+(f.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("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - jLabel1 = new javax.swing.JLabel(); - jLabel2 = new javax.swing.JLabel(); - jScrollPane1 = new javax.swing.JScrollPane(); - jList2 = new javax.swing.JList(); - jScrollPane2 = new javax.swing.JScrollPane(); - jList1 = new javax.swing.JList(); - 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() { - String[] strings = { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5" }; - public int getSize() { return strings.length; } - public Object getElementAt(int i) { return strings[i]; } - }); - jList2.addListSelectionListener(new javax.swing.event.ListSelectionListener() { - public void valueChanged(javax.swing.event.ListSelectionEvent evt) { - jList2ValueChanged(evt); - } - }); - jScrollPane1.setViewportView(jList2); - - jScrollPane2.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - jScrollPane2.addMouseListener(new java.awt.event.MouseAdapter() { - public void mouseClicked(java.awt.event.MouseEvent evt) { - jScrollPane2MouseClicked(evt); - } - }); - - jList1.setModel(new javax.swing.AbstractListModel() { - String[] strings = { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5" }; - public int getSize() { return strings.length; } - public Object getElementAt(int i) { return strings[i]; } - }); - jList1.addListSelectionListener(new javax.swing.event.ListSelectionListener() { - public void valueChanged(javax.swing.event.ListSelectionEvent evt) { - jList1ValueChanged(evt); - } - }); - jScrollPane2.setViewportView(jList1); - - jButton1.setText("Run All"); - jButton1.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton1ActionPerformed(evt); - } - }); - - 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(23, 23, 23) - .addComponent(jLabel1) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jLabel2) - .addGap(32, 32, 32)) - .addGroup(layout.createSequentialGroup() - .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 113, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 113, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jButton1) - .addGap(82, 82, 82)) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jLabel1) - .addComponent(jLabel2)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) - .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 200, Short.MAX_VALUE) - .addComponent(jScrollPane1)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jButton1) - .addGap(0, 0, 0)) - ); - - pack(); - }// //GEN-END:initComponents - - private void jScrollPane2MouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jScrollPane2MouseClicked - // TODO add your handling code here: - }//GEN-LAST:event_jScrollPane2MouseClicked - - private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed - // TODO add your handling code here: - new Thread(){ - public void run(){ - RunAllSorts(); - } - }.start(); - 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 tmp = evt.getFirstIndex(); - new Thread(){ - public void run(){ - ReportDistributiveSort(tmp); - } - }.start(); - 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 tmp = evt.getFirstIndex(); - new Thread(){ - public void run(){ - ReportComparativeSort(tmp); - } - }.start(); - 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; - private javax.swing.JList jList1; - private javax.swing.JList jList2; - private javax.swing.JScrollPane jScrollPane1; - private javax.swing.JScrollPane jScrollPane2; - // End of variables declaration//GEN-END:variables -} diff --git a/src/array/visualizer/UtilFrame.java b/src/array/visualizer/UtilFrame.java deleted file mode 100644 index 641f8408..00000000 --- a/src/array/visualizer/UtilFrame.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package array.visualizer; - -import static array.visualizer.ArrayVisualizer.*; -import java.awt.Toolkit; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.JFrame; -import javax.swing.JOptionPane; - -/** - * - * @author S630690 - */ -public class UtilFrame extends javax.swing.JFrame { - - JFrame f; - AAFrame v; - /** - * Creates new form UtilFrame - */ - public UtilFrame(JFrame f) { - this.f = f; - setUndecorated(true); - initComponents(); - setLocation(f.getX()+f.getWidth(), f.getY()+29); - setAlwaysOnTop(true); - setVisible(true); - } - - public void reposition(){ - toFront(); - setLocation(Math.min((int)Toolkit.getDefaultToolkit().getScreenSize().getWidth()-getWidth(), f.getX()+f.getWidth()), f.getY()+29); - if(v!= null && v.isVisible()) - v.reposition(); - } - - /** - * 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("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - jCheckBox1 = new javax.swing.JCheckBox(); - jButton1 = new javax.swing.JButton(); - jButton2 = new javax.swing.JButton(); - jButton3 = new javax.swing.JButton(); - jCheckBox2 = new javax.swing.JCheckBox(); - jCheckBox3 = new javax.swing.JCheckBox(); - - jCheckBox1.setText("jCheckBox1"); - - setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE); - - jButton1.setText("Change Sort"); - jButton1.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton1ActionPerformed(evt); - } - }); - - jButton2.setText("Change View"); - jButton2.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton2ActionPerformed(evt); - } - }); - - jButton3.setText("Change Spd"); - jButton3.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton3ActionPerformed(evt); - } - }); - - jCheckBox2.setSelected(true); - jCheckBox2.setText("Shuffle Anim"); - jCheckBox2.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jCheckBox2ActionPerformed(evt); - } - }); - - jCheckBox3.setSelected(true); - jCheckBox3.setText("Linked Dots"); - jCheckBox3.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jCheckBox3ActionPerformed(evt); - } - }); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); - getContentPane().setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jCheckBox3) - .addComponent(jCheckBox2) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) - .addComponent(jButton3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jButton2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jButton1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) - .addGap(0, 8, Short.MAX_VALUE)) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addComponent(jButton1) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jButton2) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jButton3) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(jCheckBox2) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jCheckBox3) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - ); - - pack(); - }// //GEN-END:initComponents - - private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed - // TODO add your handling code here: - //CHANGE SORT - if(v != null && v.isVisible()){ - boolean tmp = v instanceof SortPrompt; - v.dispose(); - if(tmp) - return; - } - v = new SortPrompt(f); - }//GEN-LAST:event_jButton1ActionPerformed - - private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed - // TODO add your handling code here: - //CHANGE VIEW - if(v != null && v.isVisible()){ - boolean tmp = v instanceof ViewPrompt; - v.dispose(); - if(tmp) - return; - } - v = new ViewPrompt(f); - }//GEN-LAST:event_jButton2ActionPerformed - - private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton3ActionPerformed - // TODO add your handling code here: - try{ - SLEEPRATIO = Double.parseDouble(JOptionPane.showInputDialog(null, "Enter Speed Multiplier", SLEEPRATIO)); - }catch(Exception e) {} - }//GEN-LAST:event_jButton3ActionPerformed - - private void jCheckBox2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBox2ActionPerformed - SHUFFLEANIM = jCheckBox2.isSelected(); - }//GEN-LAST:event_jCheckBox2ActionPerformed - - private void jCheckBox3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBox3ActionPerformed - LINKEDPIXELDRAW = jCheckBox3.isSelected(); - }//GEN-LAST:event_jCheckBox3ActionPerformed - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JButton jButton1; - private javax.swing.JButton jButton2; - private javax.swing.JButton jButton3; - private javax.swing.JCheckBox jCheckBox1; - private javax.swing.JCheckBox jCheckBox2; - private javax.swing.JCheckBox jCheckBox3; - // End of variables declaration//GEN-END:variables -} diff --git a/src/array/visualizer/ViewPrompt.java b/src/array/visualizer/ViewPrompt.java deleted file mode 100644 index 80d42617..00000000 --- a/src/array/visualizer/ViewPrompt.java +++ /dev/null @@ -1,365 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package array.visualizer; - -import static array.visualizer.ArrayVisualizer.*; -import javax.swing.JFrame; - -/** - * - * @author S630690 - */ -public class ViewPrompt extends javax.swing.JFrame implements AAFrame { - - JFrame f; - /** - * Creates new form ViewPrompt - */ - public ViewPrompt(JFrame f) { - this.f = f; - setAlwaysOnTop(true); - setUndecorated(true); - initComponents(); - setLocation(f.getX()+(f.getWidth()-getWidth())/2, f.getY()+(f.getHeight()-getHeight())/2); - setVisible(true); - } - - public void reposition(){ - setLocation(f.getX()+(f.getWidth()-getWidth())/2, f.getY()+(f.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("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - jButton1 = new javax.swing.JButton(); - jButton2 = new javax.swing.JButton(); - jButton3 = new javax.swing.JButton(); - jButton4 = new javax.swing.JButton(); - jButton5 = new javax.swing.JButton(); - jButton6 = new javax.swing.JButton(); - jLabel1 = new javax.swing.JLabel(); - jButton7 = new javax.swing.JButton(); - jButton8 = new javax.swing.JButton(); - jButton9 = new javax.swing.JButton(); - jButton10 = new javax.swing.JButton(); - jButton11 = new javax.swing.JButton(); - jButton12 = new javax.swing.JButton(); - jButton13 = new javax.swing.JButton(); - jButton14 = new javax.swing.JButton(); - - setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); - setResizable(false); - - jButton1.setText("Classic"); - jButton1.setMaximumSize(new java.awt.Dimension(87, 23)); - jButton1.setMinimumSize(new java.awt.Dimension(87, 23)); - jButton1.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton1ActionPerformed(evt); - } - }); - - jButton2.setText("Color Circle"); - jButton2.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton2ActionPerformed(evt); - } - }); - - jButton3.setText("Spiral"); - jButton3.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton3ActionPerformed(evt); - } - }); - - jButton4.setText("Color Rect"); - jButton4.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton4ActionPerformed(evt); - } - }); - - jButton5.setText("Dots"); - jButton5.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton5ActionPerformed(evt); - } - }); - - jButton6.setText("Spiral Dots"); - jButton6.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton6ActionPerformed(evt); - } - }); - - jLabel1.setText("Select View Type"); - - jButton7.setText("D. Circle"); - jButton7.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton7ActionPerformed(evt); - } - }); - - jButton8.setText("D. C. Dots"); - jButton8.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton8ActionPerformed(evt); - } - }); - - jButton9.setText("TriMesh"); - jButton9.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton9ActionPerformed(evt); - } - }); - - jButton10.setText("VW Tris"); - jButton10.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton10ActionPerformed(evt); - } - }); - - jButton11.setText("V. Pyr"); - jButton11.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton11ActionPerformed(evt); - } - }); - - jButton12.setText("H. Pyr"); - jButton12.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton12ActionPerformed(evt); - } - }); - - jButton13.setText("Hoops"); - jButton13.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton13ActionPerformed(evt); - } - }); - - jButton14.setText("D. Hoops"); - jButton14.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton14ActionPerformed(evt); - } - }); - - 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(18, 18, 18) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) - .addComponent(jButton2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jButton1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jButton3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jButton7, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jButton11, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jButton9, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jButton13, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addGap(0, 0, Short.MAX_VALUE) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) - .addComponent(jButton4, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jButton5, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jButton6, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jButton10, javax.swing.GroupLayout.DEFAULT_SIZE, 87, Short.MAX_VALUE) - .addComponent(jButton12, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jButton8, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) - .addComponent(jButton14, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addContainerGap()) - .addGroup(layout.createSequentialGroup() - .addGap(65, 65, 65) - .addComponent(jLabel1) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addGap(7, 7, 7) - .addComponent(jLabel1) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jButton1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(jButton4)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jButton2) - .addComponent(jButton5)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jButton6) - .addComponent(jButton3)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jButton7) - .addComponent(jButton8)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jButton9) - .addComponent(jButton10)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jButton11) - .addComponent(jButton12)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jButton13) - .addComponent(jButton14)) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - ); - - pack(); - }// //GEN-END:initComponents - - private void setAllFieldsFalse(){ - CIRCLEDRAW = false; - COLORONLY = false; - PIXELDRAW = false; - DISPARITYDRAW = false; - MESHDRAW = false; - PYRAMIDDRAW = false; - DRAWFLIPPED = false; - } - - private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed - // TODO add your handling code here: - setAllFieldsFalse(); - dispose(); - }//GEN-LAST:event_jButton1ActionPerformed - - private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed - // TODO add your handling code here: - setAllFieldsFalse(); - CIRCLEDRAW = true; - COLORONLY = true; - dispose(); - }//GEN-LAST:event_jButton2ActionPerformed - - private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton3ActionPerformed - // TODO add your handling code here: - setAllFieldsFalse(); - CIRCLEDRAW = true; - dispose(); - }//GEN-LAST:event_jButton3ActionPerformed - - private void jButton4ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton4ActionPerformed - // TODO add your handling code here: - setAllFieldsFalse(); - COLORONLY = true; - dispose(); - }//GEN-LAST:event_jButton4ActionPerformed - - private void jButton5ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton5ActionPerformed - // TODO add your handling code here: - setAllFieldsFalse(); - PIXELDRAW = true; - dispose(); - }//GEN-LAST:event_jButton5ActionPerformed - - private void jButton6ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton6ActionPerformed - // TODO add your handling code here: - setAllFieldsFalse(); - CIRCLEDRAW = true; - PIXELDRAW = true; - dispose(); - }//GEN-LAST:event_jButton6ActionPerformed - - private void jButton7ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton7ActionPerformed - // TODO add your handling code here: - setAllFieldsFalse(); - CIRCLEDRAW = true; - DISPARITYDRAW = true; - dispose(); - }//GEN-LAST:event_jButton7ActionPerformed - - private void jButton8ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton8ActionPerformed - // TODO add your handling code here: - setAllFieldsFalse(); - CIRCLEDRAW = true; - PIXELDRAW = true; - DISPARITYDRAW = true; - dispose(); - }//GEN-LAST:event_jButton8ActionPerformed - - private void jButton9ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton9ActionPerformed - setAllFieldsFalse(); - MESHDRAW = true; - COLORONLY = true; - dispose(); - }//GEN-LAST:event_jButton9ActionPerformed - - private void jButton10ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton10ActionPerformed - setAllFieldsFalse(); - MESHDRAW = true; - dispose(); - }//GEN-LAST:event_jButton10ActionPerformed - - private void jButton11ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton11ActionPerformed - setAllFieldsFalse(); - PYRAMIDDRAW = true; - dispose(); - }//GEN-LAST:event_jButton11ActionPerformed - - private void jButton12ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton12ActionPerformed - setAllFieldsFalse(); - PYRAMIDDRAW = true; - DRAWFLIPPED = true; - dispose(); - }//GEN-LAST:event_jButton12ActionPerformed - - private void jButton13ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton13ActionPerformed - setAllFieldsFalse(); - PYRAMIDDRAW = true; - CIRCLEDRAW = true; - COLORONLY = true; - DRAWFLIPPED = true; - dispose(); - }//GEN-LAST:event_jButton13ActionPerformed - - private void jButton14ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton14ActionPerformed - setAllFieldsFalse(); - PYRAMIDDRAW = true; - CIRCLEDRAW = true; - DISPARITYDRAW = true; - DRAWFLIPPED = true; - dispose(); - }//GEN-LAST:event_jButton14ActionPerformed - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JButton jButton1; - private javax.swing.JButton jButton10; - private javax.swing.JButton jButton11; - private javax.swing.JButton jButton12; - private javax.swing.JButton jButton13; - private javax.swing.JButton jButton14; - private javax.swing.JButton jButton2; - private javax.swing.JButton jButton3; - private javax.swing.JButton jButton4; - private javax.swing.JButton jButton5; - private javax.swing.JButton jButton6; - private javax.swing.JButton jButton7; - private javax.swing.JButton jButton8; - private javax.swing.JButton jButton9; - private javax.swing.JLabel jLabel1; - // End of variables declaration//GEN-END:variables -} diff --git a/src/array/visualizer/sort/BinaryQuickSort.java b/src/array/visualizer/sort/BinaryQuickSort.java deleted file mode 100644 index a06f7522..00000000 --- a/src/array/visualizer/sort/BinaryQuickSort.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package array.visualizer.sort; - -import array.visualizer.ArrayController; - -import static array.visualizer.ArrayVisualizer.*; -import static array.visualizer.utils.Analysis.*; -import static array.visualizer.utils.Swaps.*; -import java.util.Queue; -import java.util.LinkedList; - -/** - * 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 class BinaryQuickSort implements Sort { - - public static boolean getBit(int n, int k) { - // Find boolean value of bit k in n - return ((n >> k) & 1) == 1; - } - - public static int analyzeBit(final ArrayController ac) { - // Find highest bit of highest value - int max = 0; - for(int i = 0; i < ac.length; i++){ - ac.marked.set(1, i); - ac.aa++; - sleep(1); - max = Math.max(max, ac.array[i]); - } - return 31 - Integer.numberOfLeadingZeros(max); - } - - 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 static void binaryQuickSortRecursive(final ArrayController ac, int p, int r, int bit) - { - if (p < r && bit >= 0) - { - int q=partition(ac, p, r, bit); - sleep(1); - binaryQuickSortRecursive(ac, p, q, bit-1); - binaryQuickSortRecursive(ac, q+1, r, bit-1); - } - } - - public static void binaryQuickSort(final ArrayController ac, 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(ac, task.p, task.r, task.bit); - sleep(1); - tasks.add(new Task(task.p, q, task.bit-1)); - tasks.add(new Task(q+1, task.r, task.bit-1)); - } - } - } - - public static int partition(final ArrayController ac, int p, int r, int bit) - { - int i = p - 1; - int j = r + 1; - - while (true) { - // Left is not set - i++; - while(i < r && !getBit(ac.array[i], bit)) { - i++; - ac.marked.set(1, i); - sleep(0.45); - ac.comps+=2; - } - // Right is set - j--; - while(j > p && getBit(ac.array[j], bit)) { - j--; - ac.marked.set(2, j); - sleep(0.45); - ac.comps+=2; - } - // If i is less than j, we swap, otherwise we are done - if (i < j) - swap(ac, i, j); - else - return j; - } - } - - @Override - public String name() - { - return "Binary Quicksort"; - } - - @Override - public void sort(ArrayController ac) - { - int msb = analyzeBit(ac); - binaryQuickSort(ac, 0, ac.length-1, msb); - // binaryQuickSortRecursive(ac, 0, ac.length-1, msb); - } -} diff --git a/src/array/visualizer/sort/BitonicSort.java b/src/array/visualizer/sort/BitonicSort.java deleted file mode 100644 index 578826b3..00000000 --- a/src/array/visualizer/sort/BitonicSort.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package array.visualizer.sort; - -import array.visualizer.ArrayController; - -/** - * - * @author S630690 - */ -public class BitonicSort implements Sort -{ - public static void bitonicSort(final ArrayController ac){ - bitonicMerge(ac, 0, ac.length, true); - } - - public static void bitonicMerge(final ArrayController ac, int start, int end, boolean dir){ - int mid = (start+end)/2; - if(start==mid) - return; - bitonicMerge(ac, start, mid, true); - bitonicMerge(ac, mid, end, false); - - int low = start; - int high = end; - - if(dir) - for(int i = 0; i < end-start; i++); - } - - @Override - public String name() - { - return "Bitonic Sort"; - } - - @Override - public void sort(ArrayController ac) - { - bitonicSort(ac); - } -} diff --git a/src/array/visualizer/sort/BogoSort.java b/src/array/visualizer/sort/BogoSort.java deleted file mode 100644 index 96145292..00000000 --- a/src/array/visualizer/sort/BogoSort.java +++ /dev/null @@ -1,46 +0,0 @@ -package array.visualizer.sort; - -import array.visualizer.ArrayController; - -import static array.visualizer.ArrayVisualizer.*; -import static array.visualizer.utils.Swaps.*; - -public class BogoSort implements Sort { - public static boolean bogoIsSorted(final ArrayController ac){ - for(int i = 1; i < ac.length; i++){ - ac.comps++; - ac.aa++; - ac.marked.set(1, i); - ac.marked.set(2, i-1); - sleep(1); - if(ac.array[i] 0; i--){ - for(int j = 0; j < i; j++){ - sleep(0.005); - if(ac.array[j]>ac.array[j+1]){ - ac.comps++; - swap(ac, j, j+1, 0.01); - }else{ - ac.marked.set(1, j+1); - ac.marked.set(2, -5); - } - } - //marked.set(0, i); - } - } - - @Override - public String name() - { - return "Bubble Sort"; - } - - @Override - public void sort(ArrayController ac) - { - bubbleSort(ac); - } -} diff --git a/src/array/visualizer/sort/CocktailShaker.java b/src/array/visualizer/sort/CocktailShaker.java deleted file mode 100644 index ac311d92..00000000 --- a/src/array/visualizer/sort/CocktailShaker.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package array.visualizer.sort; - -import array.visualizer.ArrayController; - -import static array.visualizer.utils.Swaps.*; - -/** - * - * @author S630690 - */ -public class CocktailShaker implements Sort { - public static void cocktailShakerSort(final ArrayController ac){ - int i = 0; - while(i< ac.length/2){ - for(int j = i; j < ac.length-i-1; j++){ - ac.comps++; - if(ac.array[j]>ac.array[j+1]) - swap(ac, j, j+1, 0.022); - } - for(int j = ac.length-i-1; j > i; j--){ - ac.comps++; - if(ac.array[j]ac.array[biggest]) - biggest = i; - if(ac.array[i]0&&ac.array[pos]<=ac.array[pos-1]){ - ac.comps+=2; - swap(ac, pos, pos-1, 0.02); - pos--; - } - } - } - - public static void insertionSort(final ArrayController ac, int start, int end, double slpamt) { - int pos; - for(int i = start; i < end; i++){ - pos = i; - ac.marked.set(1, i); - ac.marked.set(2, -5); - while(pos>start&&ac.array[pos]<=ac.array[pos-1]){ - ac.comps+=2; - swap(ac, pos, pos-1); - sleep(slpamt); - pos--; - } - } - } - - @Override - public String name() - { - return "Insertion Sort"; - } - - @Override - public void sort(ArrayController ac) - { - insertionSort(ac); - } -} diff --git a/src/array/visualizer/sort/MaxHeapSort.java b/src/array/visualizer/sort/MaxHeapSort.java deleted file mode 100644 index 899d4510..00000000 --- a/src/array/visualizer/sort/MaxHeapSort.java +++ /dev/null @@ -1,99 +0,0 @@ -package array.visualizer.sort; - -import array.visualizer.ArrayController; - -import static array.visualizer.utils.Swaps.*; - -public class MaxHeapSort implements Sort { - - static int SLP = 1; - - static void maxheapifyrec(final ArrayController ac, int pos, boolean max){ - if(pos>= ac.length) - return; - - int child1 = pos*2+1; - int child2 = pos*2+2; - - maxheapifyrec(ac, child1, max); - maxheapifyrec(ac, child2, max); - - if(child2>= ac.length){ - if(child1>= ac.length) - return; //Done, no children - ac.comps++; - if(ac.array[child1]>ac.array[pos]) - swap(ac, pos, child1, SLP); - return; - } - - //Find largest child - int lrg = child1; - ac.comps++; - if(ac.array[child2]>ac.array[child1]) - lrg = child2; - - //Swap with largest child - ac.comps++; - if(ac.array[lrg]>ac.array[pos]){ - swap(ac, pos, lrg, SLP); - percdwn(ac, lrg, true, ac.length); - return; - } - } - - static void percdwn(final ArrayController ac, int pos, boolean max, int len){ - int child1 = pos*2+1; - int child2 = pos*2+2; - - if(child2 >= len){ - if(child1 >= len) //Done - return; - else{ - //Single Child - ac.comps++; - if((max && (ac.array[child1]>ac.array[pos])) || (!max && (ac.array[child1]ac.array[child2]){ - //Ensure child1 is the smallest for easy programming - int tmp = child1; - child1 = child2; - child2 = tmp; - } - - ac.comps++; - if(max && (ac.array[child2]>ac.array[pos])){ - swap(ac, pos, child2, SLP); - percdwn(ac, child2, max, len); - } - else if (!max && (ac.array[child1] 0; i--){ - swap(ac, 0, i, SLP); - percdwn(ac, 0, true, i); - } - } - - @Override - public String name() - { - return "Max Heap Sort"; - } - - @Override - public void sort(ArrayController ac) - { - maxheapsort(ac); - } -} diff --git a/src/array/visualizer/sort/MergeSort.java b/src/array/visualizer/sort/MergeSort.java deleted file mode 100644 index 794a5488..00000000 --- a/src/array/visualizer/sort/MergeSort.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package array.visualizer.sort; - -import array.visualizer.ArrayController; -import array.visualizer.ArrayVisualizer; - -import static array.visualizer.utils.Swaps.*; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author S630690 - */ -public class MergeSort implements Sort { - static void merge(final ArrayController ac, int min, int max, int mid){ - try { - //radixLSDsortnd(2, min, max); - - - int i=min; - while(i<=mid){ - if(ac.array[i]>ac.array[mid+1]){ - ac.comps++; - swap(ac, i, mid+1, 1.5); - push(ac, mid+1, max); - } - i++; - } - - } catch (Exception ex) { - Logger.getLogger(ArrayVisualizer.class.getName()).log(Level.SEVERE, null, ex); - } - } - - static void push(final ArrayController ac, int s, int e){ - - for(int i=s;iac.array[i+1]){ - ac.comps++; - swap(ac, i, i+1, 0.0175); - } - } - - - } - - public static void mergeSort(final ArrayController ac, int min, int max){ - if(max-min==0){//only one element. - //no swap - } - else if(max-min==1){//only two elements and swaps them - if(ac.array[min]>ac.array[max]) - swap(ac, min, max); - } - else{ - int mid=((int) Math.floor((min+max)/2));//The midpoint - - mergeSort(ac, min, mid);//sort the left side - mergeSort(ac, mid+1, max);//sort the right side - merge(ac, min, max, mid);//combines them - } - } - - @Override - public String name() - { - return "Merge Sort In-Place"; - } - - @Override - public void sort(ArrayController ac) - { - mergeSort(ac, 0, ac.length - 1); - } -} diff --git a/src/array/visualizer/sort/MergeSortOOP.java b/src/array/visualizer/sort/MergeSortOOP.java deleted file mode 100644 index 6c814c8a..00000000 --- a/src/array/visualizer/sort/MergeSortOOP.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package array.visualizer.sort; - -import array.visualizer.ArrayController; - -import static array.visualizer.ArrayVisualizer.*; - -/** - * - * @author S630690 - */ -public class MergeSortOOP implements Sort { - public static void mergeSortOP(final ArrayController ac) { - int start = 0; - int end = ac.length; - int mid = (end+start)/2; - mergeOP(ac, start, mid, end); - } - - public static void mergeOP(final ArrayController ac, int start, int mid, int end){ - if(start==mid) - return; - mergeOP(ac, start, (mid+start)/2, mid); - mergeOP(ac, mid, (mid+end)/2, end); - - 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; - if(low < mid && high >= end){ - tmp[nxt]=ac.array[low]; - low++; - ac.comps+=2; - } - else if(low >= mid && high < end){ - tmp[nxt]=ac.array[high]; - high++; - ac.comps+=2; - } - else if(ac.array[low]=array.length/10) - sleep(0.65); - } - //System.arraycopy(tmp, 0, array, start, tmp.length); - ac.marked.set(2, -5); - for(int i = 0; i < tmp.length; i++){ - ac.array[start+i]=tmp[i]; - ac.aa++; - ac.marked.set(1, start+i); - if(end-start>= ac.length/100) - sleep(0.5); - } - } - - @Override - public String name() - { - return "Merge Sort"; - } - - @Override - public void sort(ArrayController ac) - { - mergeSortOP(ac); - } -} diff --git a/src/array/visualizer/sort/MinHeapSort.java b/src/array/visualizer/sort/MinHeapSort.java deleted file mode 100644 index 522c2710..00000000 --- a/src/array/visualizer/sort/MinHeapSort.java +++ /dev/null @@ -1,24 +0,0 @@ -package array.visualizer.sort; - -import array.visualizer.ArrayController; - -import static array.visualizer.utils.Swaps.swap; - -public class MinHeapSort implements Sort { - - static void minheapsort(ArrayController ac) - { - } - - @Override - public String name() - { - return "Min Heap Sort"; - } - - @Override - public void sort(ArrayController ac) - { - minheapsort(ac); - } -} diff --git a/src/array/visualizer/sort/QuickSort.java b/src/array/visualizer/sort/QuickSort.java deleted file mode 100644 index b52ce4f1..00000000 --- a/src/array/visualizer/sort/QuickSort.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package array.visualizer.sort; - -import array.visualizer.ArrayController; - -import static array.visualizer.ArrayVisualizer.*; -import static array.visualizer.utils.Swaps.*; - -/** - * - * @author S630690 - */ -public class QuickSort implements Sort { - public static void quickSort(final ArrayController ac, int p, int r) - { - if(pp && ac.array[j] > x){ - j--; - ac.marked.set(2, j); - sleep(0.45); - ac.comps+=2; - } - - if (i < j) - swap(ac, i, j); - else - return j; - } - } - - @Override - public String name() - { - return "Quick Sort"; - } - - @Override - public void sort(ArrayController ac) - { - quickSort(ac, 0, ac.length-1); - } -} diff --git a/src/array/visualizer/sort/RadixLSD.java b/src/array/visualizer/sort/RadixLSD.java deleted file mode 100644 index c554b8b2..00000000 --- a/src/array/visualizer/sort/RadixLSD.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package array.visualizer.sort; - -import array.visualizer.ArrayController; - -import static array.visualizer.ArrayVisualizer.*; -import static array.visualizer.utils.Analysis.*; -import static array.visualizer.utils.Transcriptions.*; -import java.util.ArrayList; - -/** - * - * @author S630690 - */ -public class RadixLSD implements Sort { - - private final int radix; - - public RadixLSD(int radix) - { - this.radix = radix; - } - - public static void radixLSDsort_(final ArrayController ac, int radix) { - clearmarked(); - int highestpower = analyze(ac, radix); - ArrayList[] registers = new ArrayList[radix]; - for(int i = 0; i < radix; i++) - registers[i] = new ArrayList(); - for(int p = 0; p <= highestpower; p++){ - for(int i = 0; i < ac.length; i++){ - ac.marked.set(1, i); - sleep(1); - registers[getDigit(ac.array[i], p, radix)].add(ac.array[i]); - } - //transcribe(registers, array); - transcribe(registers, ac); - } - } - - public static void radixLSDsort(final ArrayController ac, int radix){ - clearmarked(); - int highestpower = analyze(ac, radix); - ArrayList[] registers = new ArrayList[radix]; - for(int i = 0; i < radix; i++) - registers[i] = new ArrayList(); - for(int p = 0; p <= highestpower; p++){ - for(int i = 0; i < ac.length; i++){ - ac.aa++; - ac.marked.set(1, i); - if(i%2==0) - sleep(1); - registers[getDigit(ac.array[i], p, radix)].add(ac.array[i]); - } - fancyTranscribe(ac, registers); - } - } - - public static void radixLSDsortnd(final ArrayController ac, int radix, int min, int max) { - clearmarked(); - int highestpower = analyze(ac, radix); - ArrayList[] registers = new ArrayList[radix]; - for(int i = 0; i < radix; i++) - registers[i] = new ArrayList(); - for(int p = 0; p <= highestpower; p++){ - for(int i = min; i < max; i++){ - registers[getDigit(ac.array[i], p, radix)].add(ac.array[i]); - } - //transcribe(registers, array); - transcribend(ac, registers, min); - } - } - - @Override - public String name() - { - return "Radix LSD Sort (Base " + radix + ")"; - } - - @Override - public void sort(ArrayController ac) - { - radixLSDsort(ac, radix); - } -} diff --git a/src/array/visualizer/sort/RadixLSDInPlace.java b/src/array/visualizer/sort/RadixLSDInPlace.java deleted file mode 100644 index 0ccf72e9..00000000 --- a/src/array/visualizer/sort/RadixLSDInPlace.java +++ /dev/null @@ -1,54 +0,0 @@ -package array.visualizer.sort; - -import array.visualizer.ArrayController; - -import static array.visualizer.utils.Analysis.*; -import static array.visualizer.ArrayVisualizer.*; -import static array.visualizer.utils.Swaps.*; - -public class RadixLSDInPlace implements Sort { - private final int radix; - - public RadixLSDInPlace(int radix) - { - this.radix = radix; - } - - public static void inPlaceRadixLSDSort(final ArrayController ac, int radix){ - int pos = 0; - int[] vregs = new int[radix-1]; - int maxpower = analyze(ac, radix); - double smul = Math.sqrt(radix); - for(int p = 0; p <= maxpower; p++){ - for(int i = 0; i < vregs.length; i++) - vregs[i]= ac.length-1; - pos = 0; - for(int i = 0; i < ac.length; i++){ - int digit = getDigit(ac.array[pos], p, radix); - if(digit==0) { - pos++; - ac.marked.set(0, pos); - } else { - for(int j = 0; j 0; j--) - vregs[j-1]--; - } - } - - } - } - - @Override - public String name() - { - return "Radix LSD In-Place Sort (Base " + radix + ")"; - } - - @Override - public void sort(ArrayController ac) - { - inPlaceRadixLSDSort(ac, radix); - } -} diff --git a/src/array/visualizer/sort/RadixMSD.java b/src/array/visualizer/sort/RadixMSD.java deleted file mode 100644 index 0542569f..00000000 --- a/src/array/visualizer/sort/RadixMSD.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package array.visualizer.sort; - -import array.visualizer.ArrayController; - -import static array.visualizer.ArrayVisualizer.*; -import static array.visualizer.utils.Transcriptions.*; -import static array.visualizer.utils.Analysis.*; -import java.util.ArrayList; - -/** - * - * @author S630690 - */ -public class RadixMSD implements Sort { - private final int radix; - - public RadixMSD(int radix) - { - this.radix = radix; - } - - public static void radixMSDSort(final ArrayController ac, int radix) { - clearmarked(); - int highestpower = analyze(ac, radix); - int[] tmp = new int[ac.length]; - System.arraycopy(ac.array, 0, tmp, 0, ac.length); - radixMSDRec(ac, 0, ac.length, radix, highestpower); - } - - public static void radixMSDRec(final ArrayController ac, int min, int max, int radix, int pow){ - if(min >= max || pow < 0) - return; - ac.marked.set(2, max); - ac.marked.set(3, min); - ArrayList[] registers = new ArrayList[radix]; - for(int i = 0; i < radix; i++) - registers[i] = new ArrayList(); - for(int i = min; i < max; i++){ - ac.marked.set(1, i); - registers[getDigit(ac.array[i], pow, radix)].add(ac.array[i]); - ac.aa++; - } - transcribermsd(ac, registers, min); - - int sum = 0; - for(int i = 0; i < registers.length; i++){ - radixMSDRec(ac, sum+min, sum+min+registers[i].size(), radix, pow-1); - sum+=registers[i].size(); - registers[i].clear(); - } - } - - @Override - public String name() - { - return "Radix MSD Sort (Base " + radix + ")"; - } - - @Override - public void sort(ArrayController ac) - { - radixMSDSort(ac, radix); - } -} diff --git a/src/array/visualizer/sort/SelectionSort.java b/src/array/visualizer/sort/SelectionSort.java deleted file mode 100644 index 74610794..00000000 --- a/src/array/visualizer/sort/SelectionSort.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package array.visualizer.sort; - -import array.visualizer.ArrayController; - -import static array.visualizer.ArrayVisualizer.*; -import static array.visualizer.utils.Swaps.*; - -/** - * - * @author S630690 - */ -public class SelectionSort implements Sort { - - public static void selectionSort(final ArrayController ac) { - for (int i = 0; i < ac.length - 1; i++) { - int lowestindex = i; - for (int j = i + 1; j < ac.length; j++) { - if (ac.array[j] < ac.array[lowestindex]){ - lowestindex = j; - } - sleep(0.01); - ac.comps++; - } - swap(ac, i, lowestindex); - } - } - - - @Override - public String name() - { - return "Selection Sort"; - } - - @Override - public void sort(ArrayController ac) - { - selectionSort(ac); - } -} diff --git a/src/array/visualizer/sort/ShatterSorts.java b/src/array/visualizer/sort/ShatterSorts.java deleted file mode 100644 index 80962613..00000000 --- a/src/array/visualizer/sort/ShatterSorts.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package array.visualizer.sort; - -import array.visualizer.ArrayController; - -import static array.visualizer.ArrayVisualizer.*; -import static array.visualizer.utils.Transcriptions.*; -import java.util.ArrayList; - -/** - * - * @author S630690 - */ -public class ShatterSorts implements Sort { - private final int base; - - public ShatterSorts(int base) - { - this.base = base; - } - - public static void shatterPartition(final ArrayController ac, int num) { - int shatters = (int)Math.ceil(ac.length/(double)num); - ArrayList[] registers = new ArrayList[shatters]; - for(int i = 0; i < shatters; i++) - registers[i] = new ArrayList(); - for(int i = 0; i < ac.length; i++){ - registers[ac.array[i]/num].add(ac.array[i]); - ac.aa++; - } - transcribe(registers, ac); - } - - public static void shatterSort(final ArrayController ac, int num) { - int shatters = (int)Math.ceil(ac.length/(double)num); - shatterPartition(ac, num); - int[] tmp = new int[num]; - for(int i = 0; i < shatters; i++){ - for(int j = 0; j < num; j++){ - if(i*num+j>= ac.length) - tmp[j] = -1; - else - tmp[j]=ac.array[i*num+j]; - ac.aa++; - } - for(int j = 0; j < tmp.length; j++){ - int tmpj = tmp[j]; - if(i*num+(tmpj%num)>= ac.length || tmpj == -1) - break; - ac.array[i*num+(tmpj%num)]=tmpj; - ac.aa++; - ac.marked.set(1, i*num+(tmpj%num)); - sleep(sleepTime(0.02)); - } - } - } - - public static void simpleShatterSort(final ArrayController ac, int num, int rate) { - for(int i = num; i > 1; i = i/rate) - shatterPartition(ac, i); - shatterPartition(ac, 1); - } - - @Override - public String name() - { - return "Shatter Sort"; - } - - @Override - public void sort(ArrayController ac) - { - shatterSort(ac, base); - } -} diff --git a/src/array/visualizer/sort/ShellSort.java b/src/array/visualizer/sort/ShellSort.java deleted file mode 100644 index 8c91544a..00000000 --- a/src/array/visualizer/sort/ShellSort.java +++ /dev/null @@ -1,50 +0,0 @@ -package array.visualizer.sort; - -import array.visualizer.ArrayController; - -import static array.visualizer.ArrayVisualizer.*; -import static array.visualizer.utils.Swaps.*; - -public class ShellSort implements Sort { - public static void shellSort(final ArrayController ac, int gap, int divrate){ - double sleepamt = 1d; - while(gap>0){ - for(int j = 0; j <= gap-1; j++){ - for(int i = j+gap; i < ac.length; i+=gap){ - int pos = i; - int prev = pos-gap; - while(prev>=0){ - if(ac.array[pos] < ac.array[prev]){ - ac.comps++; - swap(ac, pos, prev); - sleep(sleepamt); - }else{ - ac.aa+=2; - break; - } - pos = prev; - prev = pos-gap; - } - } - } - - if(gap==1) //Done - break; - - gap = Math.max(gap/divrate, 1); //Ensure that we do gap 1 - //sleepamt /= divrate; - } - } - - @Override - public String name() - { - return "Shell Sort"; - } - - @Override - public void sort(ArrayController ac) - { - shellSort(ac, ac.length, 2); - } -} diff --git a/src/array/visualizer/sort/Sort.java b/src/array/visualizer/sort/Sort.java deleted file mode 100644 index dc0ff2e5..00000000 --- a/src/array/visualizer/sort/Sort.java +++ /dev/null @@ -1,9 +0,0 @@ -package array.visualizer.sort; - -import array.visualizer.ArrayController; - -public interface Sort -{ - String name(); - void sort(ArrayController ac); -} diff --git a/src/array/visualizer/sort/TimeSort.java b/src/array/visualizer/sort/TimeSort.java deleted file mode 100644 index 89820eb0..00000000 --- a/src/array/visualizer/sort/TimeSort.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package array.visualizer.sort; - - -import array.visualizer.ArrayController; -import array.visualizer.ArrayVisualizer; - -import static array.visualizer.ArrayVisualizer.*; -import static array.visualizer.sort.InsertionSort.*; -import java.util.ArrayList; -import java.util.logging.Level; -import java.util.logging.Logger; -/** - * - * @author S630690 - */ -public class TimeSort implements Sort { - private final int magnitude; - - public TimeSort(int magnitude) - { - this.magnitude = magnitude; - } - - public static void timeSort(final ArrayController ac, int magnitude) - { - final int A = magnitude; - next = 0; - ArrayList threads = new ArrayList(); - final int[] tmp = ac.array.clone(); - for(int i = 0; i < ac.length; i++){ - ac.marked.set(0, i); - sleep(1); - final int c = i; - threads.add(new Thread(){ - public void run() { - int a = tmp[c]; - try { - sleep(a*A); - } catch (InterruptedException ex) { - Logger.getLogger(ArrayVisualizer.class.getName()).log(Level.SEVERE, null, ex); - } - report(ac, a); - } - }); - } - for(Thread t : threads) - t.start(); - sleep(ac.length * A); - insertionSort(ac, 0, ac.length, 0.2d); - - } - static volatile int next = 0; - public static synchronized void report(final ArrayController ac, int a){ - ac.marked.set(0, next); - ac.array[next] = a; - ac.aa++; - next++; - } - - @Override - public String name() - { - return "Time+Insertion Sort (Mul " + magnitude + ")"; - } - - @Override - public void sort(ArrayController ac) - { - timeSort(ac, magnitude); - } -} diff --git a/src/array/visualizer/sort/WeaveMerge.java b/src/array/visualizer/sort/WeaveMerge.java deleted file mode 100644 index ddf69e27..00000000 --- a/src/array/visualizer/sort/WeaveMerge.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package array.visualizer.sort; - -import array.visualizer.ArrayController; -import array.visualizer.ArrayVisualizer; - -import static array.visualizer.ArrayVisualizer.*; -import static array.visualizer.sort.InsertionSort.*; -import static array.visualizer.utils.Swaps.*; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author S630690 - */ -public class WeaveMerge implements Sort { - static void weaveMerge(final ArrayController ac, int min, int max, int mid){ - try { - //radixLSDsortnd(2, min, max); - - int i=1; - int target = (mid-min); - while(i<=target){ - //swapUpTo(mid+(i-min), min+(i-min)*2, 0.01); - swapUpTo(ac, mid+i, min+i*2-1, 0.01); - i++; - sleep(1); - } - insertionSort(ac, min, max+1, 0.15); - //sleep(100); - - } catch (Exception ex) { - Logger.getLogger(ArrayVisualizer.class.getName()).log(Level.SEVERE, null, ex); - } - } - - static void push(final ArrayController ac, int s, int e){ - - for(int i=s;iac.array[i+1]){ - ac.comps++; - swap(ac, i, i+1); - } - } - } - - public static void weaveMergeSort(final ArrayController ac, int min, int max){ - if(max-min==0){//only one element. - //no swap - } - else if(max-min==1){//only two elements and swaps them - if(ac.array[min]>ac.array[max]) - swap(ac, min, max); - } - else{ - int mid=((int) Math.floor((min+max)/2));//The midpoint - - weaveMergeSort(ac, min, mid);//sort the left side - weaveMergeSort(ac, mid+1, max);//sort the right side - weaveMerge(ac, min, max, mid);//combines them - } - } - - @Override - public String name() - { - return "Merge+Insertion Sort"; - } - - @Override - public void sort(ArrayController ac) - { - weaveMergeSort(ac, 0, ac.length - 1); - } -} diff --git a/src/array/visualizer/utils/Analysis.java b/src/array/visualizer/utils/Analysis.java deleted file mode 100644 index fc3b1544..00000000 --- a/src/array/visualizer/utils/Analysis.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package array.visualizer.utils; - -import array.visualizer.ArrayController; - -import static array.visualizer.ArrayVisualizer.*; - -/** - * - * @author S630690 - */ -public class Analysis { - public static int analyze(final ArrayController ac, int base) { - int a = 0; - for(int i = 0; i < ac.length; i++){ - ac.marked.set(1, i); - ac.aa++; - sleep(1); - if((int)(Math.log(ac.array[i])/Math.log(base))>a){ - a=(int)(Math.log(ac.array[i])/Math.log(base)); - } - } - return a; - } - - public static int analyze_sneaky(int[] array, int base) { - int a = 0; - for(int i = 0; i < array.length; i++) - if((int)(Math.log(array[i])/Math.log(base))>a){ - a=(int)(Math.log(array[i])/Math.log(base)); - } - return a; - } - - public static int analyzemax(final ArrayController ac) { - int a = 0; - for(int i = 0; i < ac.length; i++){ - if(ac.array[i]>a) - a=ac.array[i]; - ac.marked.set(1, i); - ac.aa++; - sleep(1.0); - } - return a; - } -} diff --git a/src/array/visualizer/utils/Searches.java b/src/array/visualizer/utils/Searches.java deleted file mode 100644 index e1432eac..00000000 --- a/src/array/visualizer/utils/Searches.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package array.visualizer.utils; - -import array.visualizer.ArrayController; - -import static array.visualizer.ArrayVisualizer.*; - -/** - * - * @author S630690 - */ -public class Searches { - public static void linearSearch(final ArrayController ac, int find) throws Exception { - for(int i = 0; i < ac.length; i++){ - ac.aa++; - ac.comps++; - if(ac.array[i]==find) - break; - ac.marked.set(0, i); - sleep(5); - } - sleep(1000); - } - - public static void binarySearch(final ArrayController ac, int find) throws Exception { - int at = ac.length/2; - int change = ac.length/4; - while(ac.array[at]!=find && change > 0){ - ac.marked.set(0, ac.array[at]); - ac.comps+=2; - ac.aa++; - Thread.sleep(1000); - if(ac.array[at] 0) - for(int i = pos; i < to; i++) - swap(ac, i, i+1, pause); - else - for(int i = pos; i > to; i--) - swap(ac, i, i-1, pause); - } - - public static void swapUpToNM(final ArrayController ac, int pos, int to, double pause){ - if(to - pos > 0) - for(int i = pos; i < to; i++) - swapnm(ac, i, i+1, pause); - else - for(int i = pos; i > to; i--) - swapnm(ac, i, i-1, pause); - } - - public static void swapUp(final ArrayController ac, int pos, double pause) { - for(int i = pos; i < ac.length; i++) - swap(ac, i, i+1, pause); - } -} diff --git a/src/array/visualizer/utils/Transcriptions.java b/src/array/visualizer/utils/Transcriptions.java deleted file mode 100644 index 834befba..00000000 --- a/src/array/visualizer/utils/Transcriptions.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package array.visualizer.utils; - -import array.visualizer.ArrayController; - -import static array.visualizer.ArrayVisualizer.*; - -import java.util.ArrayList; - -/** - * - * @author S630690 - */ -public class Transcriptions { - public static void transcribe(ArrayList[] registers, final ArrayController ac) { - int total = 0; - for(int ai = 0; ai < registers.length; ai++){ - for(int i = 0; i < registers[ai].size(); i++){ - sleep(1); - ac.array[total] = registers[ai].get(i); - ac.marked.set(1, total); - total++; - ac.aa++; - } - registers[ai].clear(); - } - } - - public static void transcribenm(ArrayList[] registers, int[] array) { - int total = 0; - for(int ai = 0; ai < registers.length; ai++){ - for(int i = 0; i < registers[ai].size(); i++){ - array[total] = registers[ai].get(i); - total++; - } - registers[ai].clear(); - } - } - - public static void transcribend(final ArrayController ac, ArrayList[] registers, int min) { - int total = 0; - for(int ai = 0; ai < registers.length; ai++){ - for(int i = 0; i < registers[ai].size(); i++){ - sleep((min+i)%5/4); - ac.array[total+min] = registers[ai].get(i); - ac.marked.set(1, total+min); - total++; - ac.aa++; - } - registers[ai].clear(); - } - } - - public static void transcribermsd(final ArrayController ac, ArrayList[] registers, int min) { - int total = 0; - for(ArrayList ai : registers) - total+=ai.size(); - int tmp = 0; - for(int ai = registers.length-1; ai >= 0; ai--){ - for(int i = registers[ai].size()-1; i >= 0; i--){ - sleep(1+2/registers[ai].size()); - ac.array[total+min-tmp-1] = registers[ai].get(i); - ac.marked.set(1, total+min-tmp-1); - tmp++; - ac.aa++; - } - } - } - - public static void transcribe(ArrayList[] registers, final ArrayController ac, int start) { - int total = start; - for(int ai = 0; ai < registers.length; ai++){ - for(int i = 0; i < registers[ai].size(); i++){ - sleep(1); - ac.array[total] = registers[ai].get(i); - ac.marked.set(1, total); - total++; - ac.aa++; - } - registers[ai].clear(); - } - } - - public static void fancyTranscribe(final ArrayController ac, ArrayList[] registers) { - int[] tmp = new int[ac.length]; - boolean[] tmpwrite = new boolean[ac.length]; - int radix = registers.length; - transcribenm(registers, tmp); - for(int i = 0; i < tmp.length; i++){ - int register = i%radix; - if(register == 0) - sleep(radix);//radix - int pos = (int)(((double)register*((double)tmp.length/radix))+((double)i/radix)); - if(tmpwrite[pos]==false){ - ac.array[pos]=tmp[pos]; - ac.aa++; - tmpwrite[pos] = true; - } - ac.marked.set(register, pos); - } - for(int i = 0; i < tmpwrite.length; i++) - if(tmpwrite[i]==false){ - ac.array[i]=tmp[i]; - ac.aa++; - } - clearmarked(); - } -} diff --git a/src/frames/ArrayFrame.java b/src/frames/ArrayFrame.java new file mode 100644 index 00000000..2ed88a0c --- /dev/null +++ b/src/frames/ArrayFrame.java @@ -0,0 +1,163 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package frames; + +import java.awt.Toolkit; +import java.util.Hashtable; + +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.SwingConstants; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import main.ArrayManager; +import main.ArrayVisualizer; +import templates.Frame; +import utils.Highlights; +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. + * + */ + +/** + * + * @author S630690 + */ + +final public class ArrayFrame extends javax.swing.JFrame { + final private static long serialVersionUID = 1L; + + private int[] array; + + private ArrayManager ArrayManager; + private ArrayVisualizer ArrayVisualizer; + private Frame abstractFrame; + private Highlights Highlights; + private JFrame Frame; + private UtilFrame UtilFrame; + + public ArrayFrame(int[] array, ArrayVisualizer arrayVisualizer) { + this.array = array; + + this.ArrayVisualizer = arrayVisualizer; + this.ArrayManager = ArrayVisualizer.getArrayManager(); + + this.Highlights = ArrayVisualizer.getHighlights(); + this.Frame = ArrayVisualizer.getMainWindow(); + this.UtilFrame = ArrayVisualizer.getUtilFrame(); + + setUndecorated(true); + initComponents(); + setLocation(Math.min((int) Toolkit.getDefaultToolkit().getScreenSize().getWidth() - getWidth(), Frame.getX() + Frame.getWidth()), Frame.getY() + 29); + setAlwaysOnTop(false); + setVisible(true); + } + + public void reposition(){ + toFront(); + setLocation(Math.min((int) Toolkit.getDefaultToolkit().getScreenSize().getWidth() - getWidth() - UtilFrame.getWidth(), Frame.getX() + Frame.getWidth()), Frame.getY() + 29); + if(this.abstractFrame != null && abstractFrame.isVisible()) + abstractFrame.reposition(); + } + + // //GEN-BEGIN:initComponents + private void initComponents() { + + this.jLabel1 = new javax.swing.JLabel(); + this.jSlider = new javax.swing.JSlider(SwingConstants.VERTICAL, 1, 14, 11); + + jLabel1.setText("Array Size"); + + setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE); + + Hashtable labels = new Hashtable<>(); + labels.put(1, new JLabel("2")); + labels.put(2, new JLabel("4")); + labels.put(3, new JLabel("8")); + labels.put(4, new JLabel("16")); + labels.put(5, new JLabel("32")); + labels.put(6, new JLabel("64")); + labels.put(7, new JLabel("128")); + labels.put(8, new JLabel("256")); + labels.put(9, new JLabel("512")); + labels.put(10, new JLabel("1024")); + labels.put(11, new JLabel("2048")); + labels.put(12, new JLabel("4096")); + labels.put(13, new JLabel("8192")); + labels.put(14, new JLabel("16384")); + + jSlider.setMajorTickSpacing(1); + jSlider.setLabelTable(labels); + jSlider.setPaintLabels(true); + jSlider.setPaintTicks(true); + jSlider.setSnapToTicks(true); + jSlider.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent event) { + if(ArrayManager.isLengthMutable()) { + ArrayVisualizer.setCurrentLength((int) Math.pow(2, jSlider.getValue())); + //ArrayVisualizer.setEqualItems((int) Math.pow(2, jSlider.getValue())); + ArrayManager.initializeArray(array); + } + else jSlider.setValue(ArrayVisualizer.getLogBaseTwoOfLength()); + if(ArrayVisualizer.getVisualStyles() == visuals.VisualStyles.CIRCULAR && jSlider.getValue() == 1) jSlider.setValue(2); + + Highlights.clearAllMarks(); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER, true) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER, true) + .addComponent(this.jLabel1) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, true) + .addComponent(this.jSlider) + .addGap(0, 10, Short.MAX_VALUE)))) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, true) + .addGroup(layout.createSequentialGroup() + .addGap(5, 5, 5) + .addComponent(this.jLabel1) + .addGap(5, 5, 5) + .addComponent(this.jSlider, UtilFrame.getHeight() - 26, UtilFrame.getHeight() - 26, UtilFrame.getHeight() - 26)) + ); + + pack(); + }// //GEN-END:initComponents + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel jLabel1; + private javax.swing.JSlider jSlider; + // End of variables declaration//GEN-END:variables +} \ No newline at end of file diff --git a/src/frames/UtilFrame.java b/src/frames/UtilFrame.java new file mode 100644 index 00000000..bd62ec7c --- /dev/null +++ b/src/frames/UtilFrame.java @@ -0,0 +1,491 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package frames; + +import java.awt.Toolkit; +import java.util.Hashtable; + +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.SwingConstants; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import main.ArrayManager; +import main.ArrayVisualizer; +import prompts.ShufflePrompt; +import prompts.SortPrompt; +import prompts.ViewPrompt; +import templates.Frame; +import utils.Delays; +import utils.Highlights; +import utils.Sounds; +import utils.Timer; + +/* + * +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 UtilFrame extends javax.swing.JFrame { + final private static long serialVersionUID = 1L; + + private int[] array; + + private ArrayManager ArrayManager; + private ArrayVisualizer ArrayVisualizer; + private Delays Delays; + private Frame abstractFrame; + private Highlights Highlights; + private JFrame Frame; + private Timer RealTimer; + private Sounds Sounds; + + public UtilFrame(int[] array, ArrayVisualizer arrayVisualizer) { + this.array = array; + + this.ArrayVisualizer = arrayVisualizer; + this.ArrayManager = ArrayVisualizer.getArrayManager(); + + this.Delays = ArrayVisualizer.getDelays(); + this.Frame = ArrayVisualizer.getMainWindow(); + this.Highlights = ArrayVisualizer.getHighlights(); + this.RealTimer = ArrayVisualizer.getTimer(); + this.Sounds = ArrayVisualizer.getSounds(); + + setUndecorated(true); + initComponents(); + setLocation(Math.min((int) Toolkit.getDefaultToolkit().getScreenSize().getWidth() - getWidth(), Frame.getX() + Frame.getWidth()), Frame.getY() + 29); + setAlwaysOnTop(false); + setVisible(true); + } + + public void reposition(ArrayFrame af){ + toFront(); + setLocation(Math.min((int) Toolkit.getDefaultToolkit().getScreenSize().getWidth() - getWidth(), Frame.getX() + Frame.getWidth() + af.getWidth()), Frame.getY() + 29); + if(this.abstractFrame != null && abstractFrame.isVisible()) + abstractFrame.reposition(); + } + + // //GEN-BEGIN:initComponents + private void initComponents() { + + this.jLabel1 = new javax.swing.JLabel(); + this.jButton1 = new javax.swing.JButton(); + this.jButton2 = new javax.swing.JButton(); + this.jButton3 = new javax.swing.JButton(); + this.jCheckBox1 = new javax.swing.JCheckBox(); + this.jCheckBox2 = new javax.swing.JCheckBox(); + this.jButton4 = new javax.swing.JButton(); + this.jCheckBox3 = new javax.swing.JCheckBox(); + this.jCheckBox4 = new javax.swing.JCheckBox(); + this.jButton5 = new javax.swing.JButton(); + this.jCheckBox5 = new javax.swing.JCheckBox(); + this.jButton6 = new javax.swing.JButton(); + this.jCheckBox6 = new javax.swing.JCheckBox(); + this.jCheckBox7 = new javax.swing.JCheckBox(); + this.jCheckBox8 = new javax.swing.JCheckBox(); + this.jSlider = new javax.swing.JSlider(SwingConstants.VERTICAL, 1, 12, 11); + + jLabel1.setText("Settings"); + + setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE); + + Hashtable labels = new Hashtable<>(); + labels.put(1, new JLabel("2")); + labels.put(2, new JLabel("4")); + labels.put(3, new JLabel("8")); + labels.put(4, new JLabel("16")); + labels.put(5, new JLabel("32")); + labels.put(6, new JLabel("64")); + labels.put(7, new JLabel("128")); + labels.put(8, new JLabel("256")); + labels.put(9, new JLabel("512")); + labels.put(10, new JLabel("1024")); + labels.put(11, new JLabel("2048")); + labels.put(12, new JLabel("4096")); + + jSlider.setMajorTickSpacing(1); + jSlider.setLabelTable(labels); + jSlider.setPaintLabels(true); + jSlider.setPaintTicks(true); + jSlider.setSnapToTicks(true); + jSlider.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent event) { + ArrayVisualizer.setCurrentLength((int) Math.pow(2, jSlider.getValue())); + } + }); + + jButton1ResetText(); + jButton1.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton1ActionPerformed(); + } + }); + + jButton2ResetText(); + jButton2.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton2ActionPerformed(); + } + }); + + jButton3.setText("Change Speed"); + jButton3.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton3ActionPerformed(); + } + }); + + jButton4.setText("Skip Sort"); + jButton4.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton4ActionPerformed(); + } + }); + + jCheckBox1.setSelected(true); + jCheckBox1.setText("Show Shuffle"); + jCheckBox1.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(java.awt.event.ActionEvent evt) { + jCheckBox1ActionPerformed(); + } + }); + + jCheckBox2.setSelected(false); + jCheckBox2.setText("Linked Dots"); + jCheckBox2.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(java.awt.event.ActionEvent evt) { + jCheckBox2ActionPerformed(); + } + }); + + jCheckBox3.setSelected(true); + jCheckBox3.setText("End Sweep Anim"); + jCheckBox3.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(java.awt.event.ActionEvent evt) { + jCheckBox3ActionPerformed(); + } + }); + + jCheckBox4.setSelected(true); + jCheckBox4.setText("Calc Real Time"); + jCheckBox4.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(java.awt.event.ActionEvent evt) { + jCheckBox4ActionPerformed(); + } + }); + + jButton5.setText("Clear Stats"); + jButton5.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton5ActionPerformed(); + } + }); + + jCheckBox5.setSelected(false); + jCheckBox5.setText("Softer Sounds"); + jCheckBox5.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(java.awt.event.ActionEvent evt) { + jCheckBox5ActionPerformed(); + } + }); + + jButton6ResetText(); + jButton6.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton6ActionPerformed(); + } + }); + + jCheckBox6.setSelected(true); + jCheckBox6.setText("Display Stats"); + jCheckBox6.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(java.awt.event.ActionEvent evt) { + jCheckBox6ActionPerformed(); + } + }); + + jCheckBox7.setSelected(true); + jCheckBox7.setText("Enable Sounds"); + jCheckBox7.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(java.awt.event.ActionEvent evt) { + jCheckBox7ActionPerformed(); + } + }); + + jCheckBox8.setSelected(false); + jCheckBox8.setText("Enable Color"); + jCheckBox8.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(java.awt.event.ActionEvent evt) { + jCheckBox8ActionPerformed(); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER, true) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER, true) + .addComponent(this.jLabel1) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, true) + .addComponent(this.jCheckBox1) + .addComponent(this.jCheckBox2) + .addComponent(this.jCheckBox3) + .addComponent(this.jCheckBox4) + .addComponent(this.jCheckBox6) + .addComponent(this.jCheckBox7) + .addComponent(this.jCheckBox8) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, true) + .addComponent(this.jCheckBox5) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER) + .addComponent(this.jButton6, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(this.jButton5, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(this.jButton4, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(this.jButton3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(this.jButton2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(this.jButton1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))) + .addGap(0, 10, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, true) + .addGroup(layout.createSequentialGroup() + .addGap(5, 5, 5) + .addComponent(this.jLabel1) + .addGap(7, 7, 7) + .addComponent(this.jButton2) + .addGap(5, 5, 5) + .addComponent(this.jCheckBox2) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(this.jCheckBox8) + .addGap(7, 7, 7) + .addComponent(this.jButton3) + .addGap(12, 12, 12) + .addComponent(this.jButton1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(this.jButton4) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(this.jButton6) + .addGap(7, 7, 7) + .addComponent(this.jCheckBox1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(this.jCheckBox7) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(this.jCheckBox5) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(this.jCheckBox3) + .addGap(8, 8, 8) + .addComponent(this.jButton5) + .addGap(5, 5, 5) + .addComponent(this.jCheckBox6) + .addComponent(this.jCheckBox4)) + ); + + pack(); + }// //GEN-END:initComponents + + private void jButton1ActionPerformed() {//GEN-FIRST:event_jButton1ActionPerformed + //CHANGE SORT + if(this.abstractFrame != null && abstractFrame.isVisible()){ + boolean tmp = this.abstractFrame instanceof SortPrompt; + abstractFrame.dispose(); + jButton1ResetText(); + if(tmp) + return; + } + this.abstractFrame = new SortPrompt(this.array, this.ArrayVisualizer, this.Frame, this); + jButton1.setText("Close"); + jButton2ResetText(); + jButton6ResetText(); + }//GEN-LAST:event_jButton1ActionPerformed + + public void jButton1ResetText() { + jButton1.setText("Choose Sort"); + } + + private void jButton2ActionPerformed() {//GEN-FIRST:event_jButton2ActionPerformed + // TODO add your handling code here: + //CHANGE VIEW + if(this.abstractFrame != null && abstractFrame.isVisible()){ + boolean tmp = this.abstractFrame instanceof ViewPrompt; + jButton2ResetText(); + abstractFrame.dispose(); + if(tmp) + return; + } + this.abstractFrame = new ViewPrompt(this.ArrayVisualizer, this.Frame, this); + jButton2.setText("Close"); + jButton1ResetText(); + jButton6ResetText(); + }//GEN-LAST:event_jButton2ActionPerformed + + public void jButton2ResetText() { + jButton2.setText("Visual Style"); + } + + private void jButton3ActionPerformed() {//GEN-FIRST:event_jButton3ActionPerformed + boolean speedPromptAllowed; + + if(this.abstractFrame == null) { + speedPromptAllowed = true; + } + else if(!this.abstractFrame.isVisible()) { + speedPromptAllowed = true; + } + else { + speedPromptAllowed = false; + } + + if(speedPromptAllowed) { + boolean showPrompt = true; + while(showPrompt) { + try { + double oldRatio = Delays.getSleepRatio(); + String userInput = JOptionPane.showInputDialog(null, "Modify the visual's speed below (Ex. 10 = Ten times faster)", oldRatio); + if(userInput == null) { + showPrompt = false; + } + else { + double newRatio = Double.parseDouble(userInput); + if(newRatio == 0) throw new Exception("Divide by zero"); + Delays.setSleepRatio(newRatio); + Delays.updateCurrentDelay(oldRatio, Delays.getSleepRatio()); + showPrompt = false; + } + } + catch(Exception e) { + showPrompt = true; + JOptionPane.showMessageDialog(null, "Not a number! (" + e.getMessage() + ")", "Error", JOptionPane.ERROR_MESSAGE); + } + } + } + }//GEN-LAST:event_jButton3ActionPerformed + + private void jCheckBox1ActionPerformed() {//GEN-FIRST:event_jCheckBox2ActionPerformed + ArrayVisualizer.toggleShuffleAnimation(jCheckBox1.isSelected()); + }//GEN-LAST:event_jCheckBox1ActionPerformed + + private void jCheckBox2ActionPerformed() {//GEN-FIRST:event_jCheckBox3ActionPerformed + ArrayVisualizer.toggleLinkedDots(jCheckBox2.isSelected()); + }//GEN-LAST:event_jCheckBox2ActionPerformed + + private void jCheckBox3ActionPerformed() {//GEN-FIRST:event_jCheckBox3ActionPerformed + Highlights.toggleFancyFinishes(jCheckBox3.isSelected()); + }//GEN-LAST:event_jCheckBox3ActionPerformed + + private void jButton4ActionPerformed() {//GEN-FIRST:event_jButton4ActionPerformed + Delays.changeSkipped(true); + }//GEN-LAST:event_jButton4ActionPerformed + + private void jCheckBox4ActionPerformed() {//GEN-FIRST:event_jButton4ActionPerformed + RealTimer.toggleRealTimer(jCheckBox4.isSelected()); + }//GEN-LAST:event_jCheckBox4ActionPerformed + + private void jButton5ActionPerformed() {//GEN-FIRST:event_jButton4ActionPerformed + ArrayVisualizer.resetAllStatistics(); + }//GEN-LAST:event_jButton6ActionPerformed + + private void jCheckBox5ActionPerformed() {//GEN-FIRST:event_jButton4ActionPerformed + if(jCheckBox5.isSelected()) { + Sounds.toggleSofterSounds(true); + } + else { + Sounds.toggleSofterSounds(false); + } + }//GEN-LAST:event_jCheckBox5ActionPerformed + + private void jButton6ActionPerformed() {//GEN-FIRST:event_jButton2ActionPerformed + //CHANGE SIZE + if(this.abstractFrame != null && abstractFrame.isVisible()){ + boolean tmp = this.abstractFrame instanceof ShufflePrompt; + abstractFrame.dispose(); + jButton6ResetText(); + if(tmp) + return; + } + this.abstractFrame = new ShufflePrompt(this.ArrayManager, this.Frame, this); + jButton6.setText("Close"); + jButton1ResetText(); + jButton2ResetText(); + }//GEN-LAST:event_jButton7ActionPerformed + + public void jButton6ResetText() { + jButton6.setText("Choose Shuffle"); + } + + private void jCheckBox6ActionPerformed() {//GEN-FIRST:event_jButton4ActionPerformed + ArrayVisualizer.toggleStatistics(jCheckBox6.isSelected()); + }//GEN-LAST:event_jCheckBox6ActionPerformed + + private void jCheckBox7ActionPerformed() {//GEN-FIRST:event_jButton4ActionPerformed + Sounds.toggleSounds(jCheckBox7.isSelected()); + }//GEN-LAST:event_jCheckBox7ActionPerformed + + private void jCheckBox8ActionPerformed() {//GEN-FIRST:event_jButton4ActionPerformed + ArrayVisualizer.toggleColor(jCheckBox8.isSelected()); + }//GEN-LAST:event_jCheckBox8ActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel jLabel1; + private javax.swing.JButton jButton1; + private javax.swing.JButton jButton2; + private javax.swing.JButton jButton3; + private javax.swing.JButton jButton4; + private javax.swing.JButton jButton5; + private javax.swing.JButton jButton6; + private javax.swing.JCheckBox jCheckBox1; + private javax.swing.JCheckBox jCheckBox2; + private javax.swing.JCheckBox jCheckBox3; + private javax.swing.JCheckBox jCheckBox4; + private javax.swing.JCheckBox jCheckBox5; + private javax.swing.JCheckBox jCheckBox6; + private javax.swing.JCheckBox jCheckBox7; + private javax.swing.JCheckBox jCheckBox8; + private javax.swing.JSlider jSlider; + // End of variables declaration//GEN-END:variables +} \ No newline at end of file diff --git a/src/main/ArrayManager.java b/src/main/ArrayManager.java new file mode 100644 index 00000000..dd66e1bb --- /dev/null +++ b/src/main/ArrayManager.java @@ -0,0 +1,164 @@ +package main; + +import javax.swing.JOptionPane; + +import templates.JErrorPane; +import utils.Delays; +import utils.Highlights; +import utils.Shuffles; +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 ArrayManager { + private int[] presortedArray; + private utils.Shuffles[] shuffleTypes; + private String[] shuffleIDs = {"Randomly", "Backwards", "Few Unique", "Almost Sorted", "Already Sorted"}; + + private volatile boolean MUTABLE; + + private ArrayVisualizer ArrayVisualizer; + private Delays Delays; + private Highlights Highlights; + private Shuffles Shuffles; + private Writes Writes; + + public ArrayManager(ArrayVisualizer arrayVisualizer) { + this.ArrayVisualizer = arrayVisualizer; + this.presortedArray = new int[ArrayVisualizer.getMaximumLength()]; + + this.Shuffles = utils.Shuffles.RANDOM; + this.shuffleTypes = utils.Shuffles.values(); + + this.Delays = ArrayVisualizer.getDelays(); + this.Highlights = ArrayVisualizer.getHighlights(); + this.Writes = ArrayVisualizer.getWrites(); + + this.MUTABLE = true; + } + + public boolean isLengthMutable() { + return this.MUTABLE; + } + public void toggleMutableLength(boolean Bool) { + this.MUTABLE = Bool; + } + + //TODO: Fix minimum to zero + public void initializeArray(int[] array) { + int equalFactor = ArrayVisualizer.getEqualItems(); + for (int i = 0; i < array.length; i++) { + array[i] = (i / equalFactor) * equalFactor; + } + } + + public void initializePresortedArray() { + for (int i = 0; i < this.presortedArray.length; i++) { + this.presortedArray[i] = i; + } + + for(int i = 0; i < Math.max((this.presortedArray.length / 10), 1); i++){ + Writes.swap(this.presortedArray, (int) (Math.random() * this.presortedArray.length), (int) (Math.random() * this.presortedArray.length), 0, true, false); + } + } + + public String[] getShuffleIDs() { + return this.shuffleIDs; + } + public Shuffles[] getShuffles() { + return this.shuffleTypes; + } + public Shuffles getShuffle() { + return this.Shuffles; + } + public void setShuffle(Shuffles choice) { + this.Shuffles = choice; + } + + public void shuffleArray(int[] array, int currentLen, ArrayVisualizer ArrayVisualizer) { + this.initializeArray(array); + + String tmp = ArrayVisualizer.getHeading(); + ArrayVisualizer.setHeading("Shuffling..."); + + double speed = Delays.getSleepRatio(); + + if(ArrayVisualizer.getSortingThread() != null && ArrayVisualizer.getSortingThread().isAlive()) { + double sleepRatio; + + switch(ArrayVisualizer.getLogBaseTwoOfLength()) { + case 14: sleepRatio = 16d; break; + case 13: sleepRatio = 8d; break; + case 12: sleepRatio = 4d; break; + case 11: sleepRatio = 2d; break; + case 10: sleepRatio = 3/2d; break; + case 9: sleepRatio = 1d; break; + case 8: sleepRatio = 3/4d; break; + case 7: sleepRatio = 1/2d; break; + case 6: sleepRatio = 1/3d; break; + case 5: + case 4: sleepRatio = 1/4d; break; + case 3: + case 2: + default: sleepRatio = 1/8d; + } + + Delays.setSleepRatio(sleepRatio); + } + + Shuffles.shuffleArray(array, this.ArrayVisualizer, Delays, Highlights, Writes); + + Delays.setSleepRatio(speed); + + Highlights.clearAllMarks(); + ArrayVisualizer.setHeading(tmp); + } + + public void refreshArray(int[] array, int currentLen, ArrayVisualizer ArrayVisualizer) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + JErrorPane.invokeErrorMessage(e); + } + + ArrayVisualizer.resetAllStatistics(); + Highlights.clearAllMarks(); + + ArrayVisualizer.setHeading(""); + this.shuffleArray(array, currentLen, ArrayVisualizer); + + Highlights.clearAllMarks(); + + try { + Thread.sleep(500); + } catch (InterruptedException e) { + JErrorPane.invokeErrorMessage(e); + } + + ArrayVisualizer.resetAllStatistics(); + } +} \ No newline at end of file diff --git a/src/main/ArrayVisualizer.java b/src/main/ArrayVisualizer.java new file mode 100644 index 00000000..1f0f745a --- /dev/null +++ b/src/main/ArrayVisualizer.java @@ -0,0 +1,727 @@ +package main; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Stroke; +import java.awt.Toolkit; +import java.math.RoundingMode; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.NumberFormat; +import java.util.Locale; + +import javax.swing.JFrame; +import javax.swing.JOptionPane; + +import frames.ArrayFrame; +import frames.UtilFrame; +import templates.Visual; +import utils.Delays; +import utils.Highlights; +import utils.Reads; +import utils.Renderer; +import utils.Sounds; +import utils.Timer; +import utils.Writes; +import visuals.Bars; +import visuals.Circular; +import visuals.Hoops; +import visuals.Mesh; +import visuals.Pixels; +import visuals.VisualStyles; + +/* TO-DO LIST: + * - JAVA DOCS FOR EVERYTHING + * - FINALIZE CLASSES/METHODS (already finished?) + * - CLEAN UP GETTERS AND SETTERS + * - CLEAN UP METHOD PARAMETERS + * - SHOW/HIDE BASIC SORTS, ADVANCED SORTS, AND OBSCURE SORTS + * - GIVE COMPARE METHODS DELAY AND MARK ARGUMENTS + * - Implement: + * - - Entering in your own set of data + * - - Pass ArrayVisualizer as "observer object" to sorts + * - - Pass formatter/symbols from ArrayVisualizer into utils + * - Fix: + * - - Circular pointer + * - - Combsort / Radix Sorts not complying with "Skip Sort" + * - - "Skip Sort" / changing array size saving previous speed + * - - .dls file in soundfont dir + * - - 1080p in OBS(?) + * - Create: + * - - Better code design for ViewPrompt + * - - options to choose comb gap + * - - options to choose shell gap + * - - options to choose timsort minrun + * - - subheadings for sort customizations (e.g. number of buckets for all bucket sorts) + * - - toggle between pointer and black bar + * - - SINE WAVE VISUAL!!! + * - Make: + * - - "Many Similar" distribution ((i / 5) * 5, as an example) + * - - Better names/clean up code in frames/prompts + * - - Better names for variables + * - - Analysis color for all visuals + * - - Pipe Organ (ascend -> descend) distribution + * - - option for pre-shuffled array + * - - option to stop run all + * - - sort list categories + * - - run all sorts in specific category + * - - option for custom parts for intro sorts + * - - option for simple shatter rate??? + * - Finish: + * - - SkaSort + * - - HolyGrailSort + * - - Run All Sorts in Category + * - Cleanup: + * - - Treesort + */ + +/* + * +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. + * + */ + +/* + * +The MIT License (MIT) + +Copyright (c) 2019 Luke Hutchison + +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 ArrayVisualizer { + final JFrame window = new JFrame(); + + final boolean OBS = false; // Change to true if you want 1080p for recording with OBS + + final private int MIN_ARRAY_VAL = 2; + final private int MAX_ARRAY_VAL = 16384; + + final int[] array = new int[this.MAX_ARRAY_VAL]; + + private String[][] ComparisonSorts; // First row of Comparison/DistributionSorts arrays consists of class names + private String[][] DistributionSorts; // Second row consists of user-friendly names + private String[] InvalidSorts; + + private volatile int currentLen; + private volatile int equalItems; + + private ArrayManager ArrayManager; + private SortAnalyzer SortAnalyzer; + + private UtilFrame UtilFrame; + private ArrayFrame ArrayFrame; + + private Visual[] visualClasses; + + private Thread sortingThread; + private Thread visualsThread; + + private String category; + private String heading; + private Font typeFace; + private DecimalFormat formatter; + private DecimalFormatSymbols symbols; + + private volatile int currentGap; + + private boolean SHUFFLEANIM; + + private volatile boolean ANALYZE; + + private volatile boolean POINTER; + + private volatile boolean TEXTDRAW; + private volatile boolean COLOR; + private volatile boolean DISPARITYDRAW; + private volatile boolean LINEDRAW; + private volatile boolean PIXELDRAW; + private volatile boolean RAINBOW; + private volatile boolean SPIRALDRAW; + private volatile boolean WAVEDRAW; + + private volatile int cx; + private volatile int cy; + private volatile int ch; + private volatile int cw; + + private Image img; + private Graphics2D mainRender; + private Graphics2D extraRender; + private Stroke thickStroke; + + private Delays Delays; + private Highlights Highlights; + private Reads Reads; + private Renderer Renderer; + private Sounds Sounds; + private Timer Timer; + private VisualStyles VisualStyles; + private Writes Writes; + + public ArrayVisualizer() { + this.currentLen = 2048; + this.equalItems = 1; + + this.Highlights = new Highlights(this, this.MAX_ARRAY_VAL); + this.Sounds = new Sounds(this.array, this); + this.Delays = new Delays(this); + this.Timer = new Timer(); + this.Reads = new Reads(this); + this.Renderer = new Renderer(this); + this.Writes = new Writes(this); + + this.ArrayManager = new ArrayManager(this); + this.SortAnalyzer = new SortAnalyzer(this); + + this.SortAnalyzer.analyzeSorts(); + this.ComparisonSorts = this.SortAnalyzer.getComparisonSorts(); + this.DistributionSorts = this.SortAnalyzer.getDistributionSorts(); + this.InvalidSorts = this.SortAnalyzer.getInvalidSorts(); + + this.category = ""; + this.heading = ""; + this.typeFace = new Font("Times New Roman", Font.PLAIN, (int) (640 / (1280.0 * 25))); + this.formatter = (DecimalFormat) NumberFormat.getInstance(Locale.US); + this.symbols = this.formatter.getDecimalFormatSymbols(); + + this.formatter.setRoundingMode(RoundingMode.HALF_UP); + + this.UtilFrame = new UtilFrame(this.array, this); + this.ArrayFrame = new ArrayFrame(this.array, this); + + this.UtilFrame.reposition(this.ArrayFrame); + + this.SHUFFLEANIM = true; + this.ANALYZE = false; + this.POINTER = false; + this.TEXTDRAW = true; + + this.COLOR = false; + this.DISPARITYDRAW = false; + this.LINEDRAW = false; + this.PIXELDRAW = false; + this.RAINBOW = false; + this.SPIRALDRAW = false; + + this.cx = 0; + this.cy = 0; + this.ch = 0; + this.cw = 0; + + this.ArrayManager.initializeArray(this.array); + + //TODO: Overhaul visual code to properly reflect Swing (JavaFX?) style and conventions + + //DRAW THREAD + this.visualsThread = new Thread() { + @Override + public void run() { + utils.Renderer.initializeVisuals(ArrayVisualizer.this); + + Graphics background = ArrayVisualizer.this.window.getGraphics(); + background.setColor(Color.BLACK); + + ArrayVisualizer.this.visualClasses = new Visual[5]; + ArrayVisualizer.this.visualClasses[0] = new Bars(ArrayVisualizer.this); + ArrayVisualizer.this.visualClasses[1] = new Circular(ArrayVisualizer.this); + ArrayVisualizer.this.visualClasses[2] = new Hoops(ArrayVisualizer.this); + ArrayVisualizer.this.visualClasses[3] = new Mesh(ArrayVisualizer.this); + ArrayVisualizer.this.visualClasses[4] = new Pixels(ArrayVisualizer.this); + + while(true) { + ArrayVisualizer.this.Renderer.updateVisuals(ArrayVisualizer.this); + ArrayVisualizer.this.Renderer.drawVisual(ArrayVisualizer.this.VisualStyles, ArrayVisualizer.this.array, ArrayVisualizer.this, ArrayVisualizer.this.Highlights); + + if(ArrayVisualizer.this.TEXTDRAW) { + Font f = ArrayVisualizer.this.mainRender.getFont(); + ArrayVisualizer.this.mainRender.setFont(ArrayVisualizer.this.typeFace); + ArrayVisualizer.this.mainRender.setColor(Color.BLACK); + ArrayVisualizer.this.mainRender.drawString(ArrayVisualizer.this.category + ": " + ArrayVisualizer.this.heading, 17, (int)(ArrayVisualizer.this.cw/1280.0*30)+32); + ArrayVisualizer.this.mainRender.drawString(ArrayVisualizer.this.formatter.format(ArrayVisualizer.this.currentLen) + " Numbers", 17, (int)(ArrayVisualizer.this.cw/1280.0*55)+32); + ArrayVisualizer.this.mainRender.drawString(String.format("Delay: " + ArrayVisualizer.this.Delays.displayCurrentDelay() + "ms"), 17, (int)(ArrayVisualizer.this.cw/1280.0*95)+32); + ArrayVisualizer.this.mainRender.drawString(String.format("Visual Time: " + ArrayVisualizer.this.Timer.getVisualTime()), 17, (int)(ArrayVisualizer.this.cw/1280.0*120)+32); + ArrayVisualizer.this.mainRender.drawString(String.format("Sort Time: " + ArrayVisualizer.this.Timer.getRealTime()), 17, (int)(ArrayVisualizer.this.cw/1280.0*145)+32); + ArrayVisualizer.this.mainRender.drawString(ArrayVisualizer.this.Reads.displayComparisons(), 17, (int)(ArrayVisualizer.this.cw/1280.0*185)+32); + ArrayVisualizer.this.mainRender.drawString(ArrayVisualizer.this.Writes.getSwaps(), 17, (int)(ArrayVisualizer.this.cw/1280.0*210)+32); + ArrayVisualizer.this.mainRender.drawString(ArrayVisualizer.this.Writes.getReversals(), 17, (int)(ArrayVisualizer.this.cw/1280.0*235)+32); + ArrayVisualizer.this.mainRender.drawString(ArrayVisualizer.this.Writes.getWrites(), 17, (int)(ArrayVisualizer.this.cw/1280.0*275)+32); + ArrayVisualizer.this.mainRender.drawString(ArrayVisualizer.this.Writes.getTempWrites(), 17, (int)(ArrayVisualizer.this.cw/1280.0*300)+32); + ArrayVisualizer.this.mainRender.setColor(Color.WHITE); + ArrayVisualizer.this.mainRender.drawString(ArrayVisualizer.this.category + ": " + ArrayVisualizer.this.heading, 15, (int)(ArrayVisualizer.this.cw/1280.0*30)+30); + ArrayVisualizer.this.mainRender.drawString(ArrayVisualizer.this.formatter.format(ArrayVisualizer.this.currentLen) + " Numbers", 15, (int)(ArrayVisualizer.this.cw/1280.0*55)+30); + ArrayVisualizer.this.mainRender.drawString(String.format("Delay: " + ArrayVisualizer.this.Delays.displayCurrentDelay() + "ms"), 15, (int)(ArrayVisualizer.this.cw/1280.0*95)+30); + ArrayVisualizer.this.mainRender.drawString(String.format("Visual Time: " + ArrayVisualizer.this.Timer.getVisualTime()), 15, (int)(ArrayVisualizer.this.cw/1280.0*120)+30); + ArrayVisualizer.this.mainRender.drawString(String.format("Sort Time: " + ArrayVisualizer.this.Timer.getRealTime()), 15, (int)(ArrayVisualizer.this.cw/1280.0*145)+30); + ArrayVisualizer.this.mainRender.drawString(ArrayVisualizer.this.Reads.displayComparisons(), 15, (int)(ArrayVisualizer.this.cw/1280.0*185)+30); + ArrayVisualizer.this.mainRender.drawString(ArrayVisualizer.this.Writes.getSwaps(), 15, (int)(ArrayVisualizer.this.cw/1280.0*210)+30); + ArrayVisualizer.this.mainRender.drawString(ArrayVisualizer.this.Writes.getReversals(), 15, (int)(ArrayVisualizer.this.cw/1280.0*235)+30); + ArrayVisualizer.this.mainRender.drawString(ArrayVisualizer.this.Writes.getWrites(), 15, (int)(ArrayVisualizer.this.cw/1280.0*275)+30); + ArrayVisualizer.this.mainRender.drawString(ArrayVisualizer.this.Writes.getTempWrites(), 15, (int)(ArrayVisualizer.this.cw/1280.0*300)+30); + ArrayVisualizer.this.mainRender.setFont(f); + } + background.drawImage(ArrayVisualizer.this.img, 0, 0, null); + }}}; + + this.Sounds.startAudioThread(); + this.drawWindows(); + } + + public ArrayManager getArrayManager() { + return this.ArrayManager; + } + public Delays getDelays() { + return this.Delays; + } + public Highlights getHighlights() { + return this.Highlights; + } + public Reads getReads() { + return this.Reads; + } + public Renderer getRender() { + return this.Renderer; + } + public Sounds getSounds() { + return this.Sounds; + } + public Timer getTimer() { + return this.Timer; + } + public VisualStyles getVisualStyles() { + return this.VisualStyles; + } + public Writes getWrites() { + return this.Writes; + } + + public Visual[] getVisuals() { + return this.visualClasses; + } + + public UtilFrame getUtilFrame() { + return this.UtilFrame; + } + + public String[][] getComparisonSorts() { + return this.ComparisonSorts; + } + public String[][] getDistributionSorts() { + return this.DistributionSorts; + } + + public Thread getSortingThread() { + return this.sortingThread; + } + public void setSortingThread(Thread thread) { + this.sortingThread = thread; + } + public void runSortingThread() { + this.sortingThread.start(); + } + + public int getMinimumLength() { + return this.MIN_ARRAY_VAL; + } + public int getMaximumLength() { + return this.MAX_ARRAY_VAL; + } + + public void resetAllStatistics() { + this.Reads.resetStatistics(); + this.Writes.resetStatistics(); + this.Timer.manualSetTime(0); + } + + // These next five methods should be part of ArrayManager + public int getCurrentLength() { + return this.currentLen; + } + public void setCurrentLength(int newLength) { + this.currentLen = newLength; + } + + public int getEqualItems() { + return this.equalItems; + } + public void setEqualItems(int newCount) { + this.equalItems = newCount; + } + + public int getLogBaseTwoOfLength() { + return (int) (Math.log(this.currentLen) / Math.log(2)); + } + + public boolean shuffleEnabled() { + return this.SHUFFLEANIM; + } + public void toggleShuffleAnimation(boolean Bool) { + this.SHUFFLEANIM = Bool; + } + + public String getHeading() { + return this.heading; + } + public void setHeading(String text) { + this.heading = text; + } + public void setCategory(String text) { + this.category = text; + } + + public boolean pointerActive() { + return this.POINTER; + } + + public JFrame getMainWindow() { + return this.window; + } + + public void setWindowHeight() { + this.ch = this.window.getHeight(); + } + public void setWindowWidth() { + this.cw = this.window.getWidth(); + } + + // TODO: + // CURRENT HEIGHT/WIDTH/X/Y SHOULD CORRESPOND TO "C" VARIABLES + // AND WINDOW HEIGHT/WIDTH/X/Y SHOULD CORRESPOND TO WINDOW FIELD + + public int currentHeight() { + return this.window.getHeight(); + } + public int currentWidth() { + return this.window.getWidth(); + } + public int currentX() { + return this.window.getX(); + } + public int currentY() { + return this.window.getY(); + } + + public int windowHeight() { + return this.ch; + } + public int windowWidth() { + return this.cw; + } + public int windowHalfHeight() { + return (this.ch / 2); + } + public int windowHalfWidth() { + return (this.cw / 2); + } + public int windowXCoordinate() { + return this.cx; + } + public int windowYCoordinate() { + return this.cy; + } + + public void createVolatileImage() { + this.img = this.window.createVolatileImage(this.cw, this.ch); + } + public void setThickStroke(Stroke stroke) { + this.thickStroke = stroke; + } + public Stroke getThickStroke() { + return this.thickStroke; + } + public Stroke getDefaultStroke() { + return new BasicStroke(3f * (this.currentWidth() / 1280f)); + } + public Graphics2D getMainRender() { + return this.mainRender; + } + public Graphics2D getExtraRender() { + return this.extraRender; + } + public void setMainRender() { + this.mainRender = (Graphics2D) this.img.getGraphics(); + } + public void setExtraRender() { + this.extraRender = (Graphics2D) this.img.getGraphics(); + } + public void updateVisuals() { + for(Visual visual : this.visualClasses) { + visual.updateRender(this); + } + } + public void resetMainStroke() { + this.mainRender.setStroke(this.getDefaultStroke()); + } + + public void renderBackground() { + this.mainRender.setColor(new Color(0, 0, 0)); // Pure black + this.mainRender.fillRect(0, 0, this.img.getWidth(null), this.img.getHeight(null)); + } + + public void updateCoordinates() { + this.cx = this.window.getX(); + this.cy = this.window.getY(); + } + public void updateDimensions() { + this.cw = this.window.getWidth(); + this.ch = this.window.getHeight(); + } + public void updateFontSize() { + this.typeFace = new Font("Times New Roman",Font.PLAIN,(int)(this.cw/1280.0*25)); + } + + public void toggleAnalysis(boolean Bool) { + this.ANALYZE = Bool; + } + public boolean analysisEnabled() { + return this.ANALYZE; + } + + public int halfCircle() { + return (this.currentLen / 2); + } + + //TODO: This method is *way* too long. Break it apart. + public synchronized void verifySortAndSweep() { + this.Highlights.toggleFancyFinish(true); + this.Highlights.resetFancyFinish(); + + this.Delays.setSleepRatio(1); + + double sleepRatio = 0; + + switch(this.getLogBaseTwoOfLength()) { + case 14: sleepRatio = 0.25; break; + case 13: sleepRatio = 0.5; break; + case 12: sleepRatio = 1; break; + case 11: sleepRatio = 2; break; + case 10: sleepRatio = 4; break; + case 9: sleepRatio = 6; break; + case 8: sleepRatio = 8; break; + case 7: sleepRatio = 16; break; + case 6: sleepRatio = 24; break; + case 5: + case 4: sleepRatio = 32; break; + case 3: + case 2: + default: sleepRatio = 64; + } + + long tempComps = this.Reads.getComparisons(); + this.Reads.setComparisons(0); + + String temp = this.heading; + this.heading = "Verifying sort..."; + + for(int i = 0; i < this.currentLen + this.getLogBaseTwoOfLength(); i++) { + if(i < this.currentLen) this.Highlights.markArray(1, i); + this.Highlights.incrementFancyFinishPosition(); + + if(i < this.currentLen - 1) { + if(this.Reads.compare(this.array[i], this.array[i + 1]) == 1) { + this.Highlights.clearMark(1); + + this.Sounds.toggleSound(false); + this.Highlights.toggleFancyFinish(false); + + for(int j = i + 1; j < this.currentLen; j++) { + this.Highlights.markArray(j, j); + this.Delays.sleep(sleepRatio / this.getLogBaseTwoOfLength()); + } + + JOptionPane.showMessageDialog(this.window, "The sort was unsuccessful;\nIndices " + i + " and " + (i + 1) + " are out of order!", "Error", JOptionPane.OK_OPTION, null); + + this.Highlights.clearAllMarks(); + + i = this.currentLen + this.getLogBaseTwoOfLength(); + + this.Sounds.toggleSound(true); + } + } + + if(this.Highlights.fancyFinishEnabled()) { + this.Delays.sleep(sleepRatio / this.getLogBaseTwoOfLength()); + } + } + this.Highlights.clearMark(1); + + this.heading = temp; + this.Reads.setComparisons(tempComps); + + if(this.Highlights.fancyFinishActive()) { + this.Highlights.toggleFancyFinish(false); + } + this.Highlights.resetFancyFinish(); + } + + public void endSort() { + this.Timer.disableRealTimer(); + this.Highlights.clearAllMarks(); + + double speed = this.Delays.getSleepRatio(); + this.verifySortAndSweep(); + this.Delays.setSleepRatio(speed); + this.Delays.changeSkipped(false); + + this.Highlights.clearAllMarks(); + } + + public void togglePointer(boolean Bool) { + this.POINTER = Bool; + } + public void toggleDistance(boolean Bool) { + this.DISPARITYDRAW = Bool; + } + public void togglePixels(boolean Bool) { + this.PIXELDRAW = Bool; + } + public void toggleRainbow(boolean Bool) { + this.RAINBOW = Bool; + } + public void toggleSpiral(boolean Bool) { + this.SPIRALDRAW = Bool; + } + public void toggleLinkedDots(boolean Bool) { + this.LINEDRAW = Bool; + } + public void toggleStatistics(boolean Bool) { + this.TEXTDRAW = Bool; + } + public void toggleColor(boolean Bool) { + this.COLOR = Bool; + } + public void toggleWave(boolean Bool) { + this.WAVEDRAW = Bool; + } + + public void setVisual(VisualStyles choice) { + this.VisualStyles = choice; + } + + public int getCurrentGap() { + return this.currentGap; + } + public void setCurrentGap(int gap) { + this.currentGap = gap; + } + + public void repositionFrames() { + this.ArrayFrame.reposition(); + this.UtilFrame.reposition(this.ArrayFrame); + } + + public boolean rainbowEnabled() { + return this.RAINBOW; + } + public boolean colorEnabled() { + return this.COLOR; + } + public boolean spiralEnabled() { + return this.SPIRALDRAW; + } + public boolean distanceEnabled() { + return this.DISPARITYDRAW; + } + public boolean pixelsEnabled() { + return this.PIXELDRAW; + } + public boolean linesEnabled() { + return this.LINEDRAW; + } + public boolean waveEnabled() { + return this.WAVEDRAW; + } + + public DecimalFormat getNumberFormat() { + return this.formatter; + } + + private void drawWindows() { + this.VisualStyles = visuals.VisualStyles.BARS; + this.category = "Select a Sort"; + + // For recording with OBS (don't ask where these numbers came from. I don't get it myself) + if(this.OBS) { + int x = (int) (1920 * (double) (1920 / 1916)); + int y = (int) (1080 * (double) (1080 / 1020)); + this.window.setSize(new Dimension(x, y)); + } + else { // Consider changing back to discrete 16:9 dimension + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + this.window.setSize((int) (screenSize.getWidth() / 2d), (int) (screenSize.getHeight() / 2d)); + } + + this.window.setLocation(0, 0); + this.window.setVisible(true); + this.window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + this.window.setTitle("w0rthy's Array Visualizer - " + (this.ComparisonSorts[0].length + this.DistributionSorts[0].length) + " Sorting Algorithms with 12 Different Visual Styles"); + this.window.setBackground(Color.BLACK); + + this.symbols.setGroupingSeparator(','); + this.formatter.setDecimalFormatSymbols(this.symbols); + + //TODO: Consider removing insets from window size + this.cw = this.window.getWidth(); + this.ch = this.window.getHeight(); + + this.visualsThread.start(); + + this.UtilFrame.setVisible(true); + this.ArrayFrame.setVisible(true); + + if(this.InvalidSorts != null) { + String output = ""; + for(int i = 0; i < this.InvalidSorts.length; i++) { + output += this.InvalidSorts[i] + "\n"; + } + JOptionPane.showMessageDialog(this.window, "The following algorithms were not loaded due to errors:\n" + output, "Warning", JOptionPane.WARNING_MESSAGE); + } + } + + public static void main(String[] args) { + new ArrayVisualizer(); + } +} \ No newline at end of file diff --git a/src/main/SortAnalyzer.java b/src/main/SortAnalyzer.java new file mode 100644 index 00000000..e3b99824 --- /dev/null +++ b/src/main/SortAnalyzer.java @@ -0,0 +1,181 @@ +package main; + +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JOptionPane; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.ScanResult; +import templates.JErrorPane; +import templates.Sort; +import utils.Delays; +import utils.Highlights; +import utils.Reads; +import utils.Writes; + +/* + * +The MIT License (MIT) + +Copyright (c) 2019 Luke Hutchison + +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 SortAnalyzer { + private ArrayList comparisonSorts; + private ArrayList distributionSorts; + private ArrayList invalidSorts; + + private Delays Delays; + private Highlights Highlights; + private Reads Reads; + private Writes Writes; + + public SortAnalyzer(ArrayVisualizer ArrayVisualizer) { + this.comparisonSorts = new ArrayList<>(); + this.distributionSorts = new ArrayList<>(); + this.invalidSorts = new ArrayList<>(); + + this.Delays = ArrayVisualizer.getDelays(); + this.Highlights = ArrayVisualizer.getHighlights(); + this.Reads = ArrayVisualizer.getReads(); + this.Writes = ArrayVisualizer.getWrites(); + } + + public void analyzeSorts() { + try (ScanResult scanResult = new ClassGraph().whitelistPackages("sorts").scan()) { + List 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