/** * Copyright (C) 1997 William Braynen * Contact information can be found at www.samdurak.com * * This applet illustrates the operation of a number of sorting * algorithms and the concept of invariants. As it sorts a list * of integers, it uses a different color to plot the part of the * list affected by the invariant. An invariant is a condition * that does not change during a program segment's execution. * * 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 2 * 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Written by : William Braynen * Last modified : May 10, 1998 * Class name : InvariantsApplet (formerly called ArrayApplet) * * Purpose: * Demonstrates the contents of an array (representing a list of * numbers) as it's being sorted by graphing the index of the array * on the x-axis and the value contained at that index position on * the y-axis. This is aimed at, hopefully, demonstaring the invariants * of various sorting algorithms visually. * * NOTE: * The array and graph used are instances of an ArrayI and Graph * object classes respectively. The size of the x and y axis of the * graph is equal to the size of the array. * * Modification Notes : Rex Posadas * Modification Goals : 1 - to be able to toggle between graphing the * sorting routines backwards and forwards. * 2 - create a visual interface which allows the * user to toggle between graphing backwards * and forwards. * Current Status : 1 - all sorting routines can work backwards. * 2 - direction of graphing is directed by * display() which uses global variable * Forward (toggled in ItemChanged()) * 3 - checkbox(S.S.L.) is changed to * checkbox(reverse) -- visual interface. * Added Methods : 1 - newdisplay(): graphs unsorted list. * This method is used by the "new" button */ import java.applet.Applet; import java.awt.*; import java.awt.event.*; import java.util.Vector; public class InvariantsApplet extends Applet implements ActionListener, ItemListener { private ArrayI array1, // sorted by Sorting Routines. array2, // original - always in its original UNSORTED state. arraytemp; private Graph graph; private Vector vector; private final int SIZE = 200; private int index = 0; // number of elements in vector private boolean DEBUG = false; // this is to use array2 to be able to sort // THE SAME array more than once. private boolean Forward = true; // sort forward private boolean InsertionSort = true; Thread thread1, // bubble thread2, // insertion thread3, // quick thread4; // selection ThreadObject1 threadObject1; // bubble ThreadObject2 threadObject2; // insertion ThreadObject3 threadObject3; // quick ThreadObject4 threadObject4; // selection Image image; Graphics graphics; // User Interface: private Panel1 panel1; // `New` button with options private Panel2 panel2; // `Sort` button with options of the sorting algorithm to be used private Checkbox checkbox_backgroundLine; private Checkbox checkbox_reverse; private Choice speedMenu; // speed adjustment private Label label1, // "Speed:" - caption for the scrollbar spacer1, spacer2, spacer3; private static final Color BACKGROUND_COLOR = Color.lightGray; // controlPanelColor.darker(); public static final Color CONTROL_PANEL_COLOR = BACKGROUND_COLOR; // must be public to be accessable from the panel classes private static final Color BUTTON_COLOR = (Color.pink).darker(); private static final Color LETTER_COLOR = Color.black; private static final Color BUTTON_LETTER_COLOR = Color.white; // private static final int DELAY_INCREMENTS = 50; // DELAY_MAX / DELAY_INCREMENTS = number of setting private static final int DELAY_MAX = 300; public void init() { GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints constraints = new GridBagConstraints(); setLayout( gridbag ); constraints.fill = GridBagConstraints.NONE; constraints.anchor = GridBagConstraints.CENTER; constraints.gridx = 0; constraints.gridy = 0; spacer1 = new Label(); spacer1.setBackground( BACKGROUND_COLOR ); gridbag.setConstraints( spacer1, constraints ); add( spacer1 ); constraints.gridx = 0; constraints.gridy = 1; panel1 = new Panel1(); panel1.setForeground( LETTER_COLOR ); panel1.button_new.setBackground( BUTTON_COLOR ); panel1.button_new.setForeground( BUTTON_LETTER_COLOR ); gridbag.setConstraints( panel1, constraints ); add( panel1 ); panel1.button_new.addActionListener( this ); constraints.gridx = 0; constraints.gridy = 2; spacer2 = new Label(); spacer2.setBackground( BACKGROUND_COLOR ); gridbag.setConstraints( spacer2, constraints ); add( spacer2 ); constraints.gridx = 0; constraints.gridy = 3; panel2 = new Panel2(); panel2.setForeground( LETTER_COLOR ); panel2.button_sort.setBackground( BUTTON_COLOR ); panel2.button_sort.setForeground( BUTTON_LETTER_COLOR ); gridbag.setConstraints( panel2, constraints ); add( panel2 ); panel2.button_sort.addActionListener( this ); constraints.fill = GridBagConstraints.NONE; constraints.anchor = GridBagConstraints.NORTHWEST; constraints.gridx = 1; constraints.gridy = 1; constraints.gridwidth = 4; constraints.gridheight = 4; constraints.weightx = 4; constraints.weighty = 4; graph = new Graph(); graph.setSize( SIZE ); graph.resize( SIZE+1, SIZE+1 ); image = createImage( SIZE+1, SIZE+1 ); graph.setImage( image ); graph.setGraphics( image.getGraphics() ); graph.displayGrid(); gridbag.setConstraints( graph, constraints ); add( graph ); constraints.anchor = GridBagConstraints.CENTER; constraints.gridx = 0; constraints.gridy = 6; constraints.gridwidth = 1; constraints.gridheight = 1; constraints.weightx = 1; constraints.weighty = 1; checkbox_reverse = new Checkbox("Reverse"); checkbox_reverse.setBackground( BACKGROUND_COLOR ); checkbox_reverse.setForeground( LETTER_COLOR ); gridbag.setConstraints( checkbox_reverse, constraints ); add( checkbox_reverse ); checkbox_reverse.addItemListener( this ); /* Choice Menu (speed): * fast => speedDelay = 10; * medium => speedDelay = approx.= DELAY_MAX / 2; * slow => speedDelay = DELAY_MAX; */ constraints.anchor = GridBagConstraints.CENTER; constraints.gridx = 1; constraints.gridy = 6; constraints.gridwidth = 1; constraints.gridheight = 1; constraints.weightx = 1; constraints.weighty = 1; speedMenu = new Choice(); speedMenu.addItem( "Fast" ); speedMenu.addItem( "Medium" ); speedMenu.addItem( "Slow" ); speedMenu.addItemListener( this ); gridbag.setConstraints( speedMenu, constraints ); add( speedMenu ); constraints.anchor = GridBagConstraints.EAST; constraints.gridx = 2; constraints.gridy = 6; constraints.gridwidth = 1; constraints.gridheight = 1; constraints.weightx = 1; constraints.weighty = 1; checkbox_backgroundLine = new Checkbox("Sorted List"); checkbox_backgroundLine.setBackground( BACKGROUND_COLOR ); checkbox_backgroundLine.setForeground( LETTER_COLOR ); gridbag.setConstraints( checkbox_backgroundLine, constraints ); add( checkbox_backgroundLine ); checkbox_backgroundLine.addItemListener( this ); /* label1 = new Label( "Speed" ); * label1.setBackground( BACKGROUND_COLOR ); * label1.setForeground( LETTER_COLOR ); * gridbag.setConstraints( label1, constraints ); * add( label1 ); */ constraints.gridx = 4; constraints.gridy = 6; constraints.gridwidth = 1; constraints.gridheight = 1; constraints.weightx = 1; constraints.weighty = 1; spacer3 = new Label(); spacer3.setBackground( BACKGROUND_COLOR ); gridbag.setConstraints( spacer3, constraints ); add( spacer3 ); threadObject1 = new ThreadObject1( this ); threadObject2 = new ThreadObject2( this ); threadObject3 = new ThreadObject3( this ); threadObject4 = new ThreadObject4( this ); vector = new Vector( 500 ); array1 = new ArrayI( SIZE ); if (DEBUG) array2 = new ArrayI( SIZE ); arraytemp = new ArrayI( SIZE ); // temporary array if (panel1.checkbox_random.getState()) this.array1.fillRandom(); else if (panel1.checkbox_shuffled.getState()) this.array1.fillPerm(); vector.addElement( array1 ); if (DEBUG) array2.copyOf( array1 ); newdisplay(); } // end init public void paint( Graphics g ) { g.setColor( BACKGROUND_COLOR ); g.fillRect( 0, 0, 450, 320 ); } // end paint public void itemStateChanged( ItemEvent e) { if (e.getItemSelectable() == checkbox_reverse) { Forward = !Forward; if (panel2.button_sort.getLabel() != "Pause") { if (Forward) panel2.button_sort.setLabel( "Sort" ); else panel2.button_sort.setLabel( "Unsort" ); } // end if } // end if else if (e.getItemSelectable() == checkbox_backgroundLine && !anyAlive()) newdisplay(); } // end itemStateChanged public void actionPerformed( ActionEvent e ) { String buttonLabel = new String( e.getActionCommand() ); if (buttonLabel.equals( "Sort" ) || buttonLabel.equals( "Unsort" )) { panel2.button_sort.setLabel( "Pause" ); if (anyAlive()) resume(); else { if (DEBUG) array1.copyOf( array2 ); if (panel2.checkbox_bubble.getState()) { stopAll(); thread1 = new Thread( threadObject1 ); thread1.start(); display(); panel2.checkbox_insertion.setEnabled( false ); panel2.checkbox_quick.setEnabled ( false ); panel2.checkbox_selection.setEnabled( false ); } // end if else if (panel2.checkbox_insertion.getState()) { stopAll(); thread2 = new Thread( threadObject2 ); thread2.start(); display(); panel2.checkbox_bubble.setEnabled ( false ); panel2.checkbox_quick.setEnabled ( false ); panel2.checkbox_selection.setEnabled( false ); } // end else if else if (panel2.checkbox_quick.getState()) { stopAll(); thread3 = new Thread( threadObject3 ); thread3.start(); display(); panel2.checkbox_bubble.setEnabled ( false ); panel2.checkbox_insertion.setEnabled( false ); panel2.checkbox_selection.setEnabled( false ); } // end else if else if (panel2.checkbox_selection.getState()) { stopAll(); thread4 = new Thread( threadObject4 ); thread4.start(); display(); panel2.checkbox_bubble.setEnabled ( false ); panel2.checkbox_insertion.setEnabled( false ); panel2.checkbox_quick.setEnabled ( false ); } // end else if panel2.button_sort.setLabel( "Pause" ); } // end if (!anyAlive()) } // end if (buttonLabel.equals( "Sort" )) if (buttonLabel.equals( "Pause" )) { if (Forward) panel2.button_sort.setLabel( "Sort" ); else panel2.button_sort.setLabel( "Unsort" ); pause(); } // end if (buttonLabel.equals( "Pause" )) if (buttonLabel.equals( "New" )) { stopAll(); if (panel1.checkbox_random.getState()) array1.fillRandom(); else array1.fillPerm(); newdisplay(); panel2.checkbox_bubble.setEnabled ( true ); panel2.checkbox_insertion.setEnabled( true ); panel2.checkbox_quick.setEnabled ( true ); panel2.checkbox_selection.setEnabled( true ); if (DEBUG) array2.copyOf( array1 ); } // end if if (checkbox_reverse.getState()) Forward = false; // go backwards if reverse checkbox is filled else Forward = true; // go forwards otherwise } // end actionPerformed // auxiliary method for `actionPerformed` private void resume() { if (thread1 != null) { if (thread1.isAlive()) thread1.resume(); } // end if if (thread2 != null) { if (thread2.isAlive()) thread2.resume(); } // end if if (thread3 != null) { if (thread3.isAlive()) thread3.resume(); } // end if if (thread4 != null) { if (thread4.isAlive()) thread4.resume(); } // end if } // end resume // auxiliary method for `actionPerformed` private void pause() { if (thread1 != null) { if (thread1.isAlive()) thread1.suspend(); } // end if if (thread2 != null) { if (thread2.isAlive()) thread2.suspend(); } // end if if (thread3 != null) { if (thread3.isAlive()) thread3.suspend(); } // end if if (thread4 != null) { if (thread4.isAlive()) thread4.suspend(); } // end if } // end pause private int getDelayTime() { int i = speedMenu.getSelectedIndex(); int speedDelay = 0; if (i == 0) speedDelay = 10; else if (i == 1) speedDelay = (DELAY_MAX / 2) - 100; else if (i == 2) speedDelay = DELAY_MAX; return speedDelay; } // end getSleepTime private void stopAll() { if (thread1 != null) { if (thread1.isAlive()) { thread1.stop(); array1.fillRandom(); } thread1 = null; } // end if if (thread2 != null) { if (thread2.isAlive()) { thread2.stop(); array1.fillRandom(); } thread2 = null; } // end if if (thread3 != null) { if (thread3.isAlive()) { thread3.stop(); array1.fillRandom(); } thread3 = null; } // end if if (thread4 != null) { if (thread4.isAlive()) { thread4.stop(); array1.fillRandom(); } thread4 = null; } // end if if ((panel2.button_sort.getLabel()).equals( "Pause" )) { if (Forward) panel2.button_sort.setLabel( "Sort" ); else panel2.button_sort.setLabel( "Unsort" ); } // end if } // end stopAll private boolean anyAlive() { if (thread1 != null) { if (thread1.isAlive()) return true; } // end if else if (thread2 != null) { if (thread2.isAlive()) return true; } // end else if else if (thread3 != null) { if (thread3.isAlive()) return true; } // end else if else if (thread4 != null) { if (thread4.isAlive()) return true; } // end else if return false; } // end anyAlive public Thread aliveThread() { if (thread1 != null) { if (thread1.isAlive()) return thread1; } // end if else if (thread2 != null) { if (thread2.isAlive()) return thread2; } // end else if else if (thread3 != null) { if (thread3.isAlive()) return thread3; } // end else if else if (thread4 != null) { if (thread4.isAlive()) return thread4; } // end else if return thread1; } // end aliveThread /* Used only to display the first element of the vector */ public void newdisplay() { setBackground( Color.white ); graph.displayGrid(); /* * Every time the new button in pressed the vector is initialized as to prepare * for a new sorting routine. */ vector.removeAllElements(); // clear the vector for the next sorting routine vector.addElement( array1 );// add unsorted data to vector for "new" button index = 0; // set vector count back to 0 ArrayI a = (ArrayI)vector.elementAt(0); if (checkbox_backgroundLine.getState() == true) graph.plot( a, 4, true ); graph.plot( a ); graph.display(); }// newdisplay /*----- displaying routine -----*/ public void display() { int i = 1 ; if (!Forward) i = index - 1; while (i > 0 && i < index) { setBackground( Color.white ); graph.displayGrid(); ArrayI a = (ArrayI)vector.elementAt(i); if (checkbox_backgroundLine.getState() == true) graph.plot( a, 4, true ); if (InsertionSort && i < index - 1) graph.plot( a, a.getInsertionSortIndex() ); else if (InsertionSort && i == index - 1) graph.plot( a, 2, 0 ); else graph.plot( a ); graph.display(); if (Forward) i++; // increment to next vector index to go forwards else i--; try{aliveThread().sleep( getDelayTime() );} catch(InterruptedException e) {} } // end while /* PREVIOUS WORKING VERSION OF THE ABOVE while LOOP: * ( j u s t i n c a s e the above new version "stops" working... ) * NOTE: Variable "Forward" is not an `int` any longer - it's a `boolean`... * while (i > 0 || i< index) { * setBackground( Color.white ); * graph.displayGrid(); * graph.plot((ArrayI)vector.elementAt(i)); //plot array into buffer * graph.display(); * if (Forward == 1) i++; // increment to next vector index to go forwards * else if (Forward == 0 && i > 1) i--; // decrement to previous vector index to go backwards * else i= index + 1; // leave the loop * } // end: while (i >= 0 || i< index) */ vector.removeAllElements(); // clear the vector for the next sorting routine vector.addElement( array1 );// add unsorted data to vector for "new" button index = 0; // set vector count back to 0 } // end display /*-------------------------------------------------------------- * display() method is included in the thread as to allow the * use of the pause button *--------------------------------------------------------------*/ public void selectionSort() { array1 = selectionSort( array1 ); InsertionSort = false; display(); panel2.button_sort.setLabel( "Sort" ); panel2.checkbox_bubble.setEnabled ( true ); panel2.checkbox_insertion.setEnabled( true ); panel2.checkbox_quick.setEnabled ( true ); panel2.checkbox_selection.setEnabled( true ); } // end selectionSort public void bubbleSort() { array1 = bubbleSort( array1 ); InsertionSort = false; display(); panel2.button_sort.setLabel( "Sort" ); panel2.checkbox_bubble.setEnabled ( true ); panel2.checkbox_insertion.setEnabled( true ); panel2.checkbox_quick.setEnabled ( true ); panel2.checkbox_selection.setEnabled( true ); } // end bubbleSort public void insertionSort() { array1 = insertionSort( array1 ); InsertionSort = true; display(); panel2.button_sort.setLabel( "Sort" ); panel2.checkbox_bubble.setEnabled ( true ); panel2.checkbox_insertion.setEnabled( true ); panel2.checkbox_quick.setEnabled ( true ); panel2.checkbox_selection.setEnabled( true ); } // end insertionSort public void quickSort() { quickSort( 1, (this.array1).last() ); InsertionSort = false; display(); panel2.button_sort.setLabel( "Sort" ); panel2.checkbox_bubble.setEnabled ( true ); panel2.checkbox_insertion.setEnabled( true ); panel2.checkbox_quick.setEnabled ( true ); panel2.checkbox_selection.setEnabled( true ); } // end quickSort /*-------------------------sorting routines------------------------------*/ public ArrayI bubbleSort( ArrayI array, boolean show ) { int t; for (int i = array.last(); i > 0; i--) { for (int j = 2; j < i + 1; j++) { if (array.getItem(j-1) > array.getItem(j)) array.swap(j-1,j); } // end for arraytemp = new ArrayI( SIZE); // temporary array arraytemp.copyOf( array ); vector.addElement( (ArrayI)arraytemp); // add changed array to vector index++; // increment number of elements in vector } // end for return array; } // end bubbleSort public ArrayI bubbleSort( ArrayI array ) { return (this.bubbleSort( array, true )); } // end bubbleSort // initially: // left = 1 // right = array1.last() public void quickSort( int left, int right ) { int pivot_pointer = left; int pivot = this.array1.getItem( pivot_pointer ); int pointer1 = left; int pointer2 = right; // deBugger: // if (left >= right) (this.getGraphics()).drawString( "LEFT = RIGHT", 300, 300 ); if (left < right) { while (pointer1 < pointer2 ) { while ((this.array1.getItem(pointer2) > pivot) && (pointer2 > pointer1)) { pointer2--; } // end while while ((this.array1.getItem(pointer1) <= pivot) && (pointer2 > pointer1)) { pointer1++; } // end while if (pointer1 != pointer2) this.array1.swap(pointer1, pointer2); /*----------------*/ arraytemp = new ArrayI( SIZE ); // temporary array arraytemp.copyOf( array1 ); vector.addElement( (ArrayI)arraytemp); // add changed array to vector index++; // increment number of elements in vector /*----------------*/ } // end while this.array1.swap( pointer1, pivot_pointer ); pivot_pointer = pointer1; quickSort( left, pivot_pointer-1 ); quickSort( pivot_pointer+1, right ); } // end if } // end quickSort public ArrayI insertionSort( ArrayI array ) { int j, value; for (int i = 2; i <= array.last(); i++) { value = array.getItem(i); j = i; while ((j > 1) && (array.getItem(j-1) > value)) { array.setItem(j, array.getItem(j-1)); j--; } // end while array.setItem(j, value); /* ---------- */ arraytemp = new ArrayI( SIZE); // temporary array arraytemp.copyOf( array ); arraytemp.setInsertionSortIndex( i ); vector.addElement( (ArrayI)arraytemp); // add changed array to vector index++; /* ---------- */ } // end for /* arraytemp.setInsertionSortIndex( 0 ); * display(); */ return array; } // end insertionSort public ArrayI selectionSort( ArrayI array ) { int min, temp; for (int i = 1; i < array.last(); i++) { min = i; for (int j = i + 1; j <= array.last(); j++) { if (array.getItem( j ) < array.getItem( min )) { min = j; } // end if } // end for temp = array.getItem( min ); array.setItem( min, array.getItem( i )); array.setItem( i, temp ); /* ---------- */ arraytemp = new ArrayI( SIZE); // temporary array arraytemp.copyOf( array ); vector.addElement( (ArrayI)arraytemp); // add changed array to vector index++; // increment number of elements in vector /* ---------- */ } // end for return array; } // end selectionSort } // end InvariantsApplet class class Panel1 extends Panel { Button button_new; CheckboxGroup group1; Checkbox checkbox_random, checkbox_shuffled; Panel1() { setLayout( new GridLayout( 3, 1 )); setBackground( InvariantsApplet.CONTROL_PANEL_COLOR ); group1 = new CheckboxGroup(); button_new = new Button( "New" ); add( button_new ); checkbox_random = new Checkbox( "Random", true, group1 ); add( checkbox_random ); checkbox_shuffled = new Checkbox( "Shuffled", false, group1 ); add( checkbox_shuffled ); } // end constructor } // end class Panel1 class Panel2 extends Panel { Button button_sort; CheckboxGroup group2; Checkbox checkbox_bubble, checkbox_insertion, checkbox_quick, checkbox_selection; Panel2() { setLayout( new GridLayout( 5, 1 )); setBackground( InvariantsApplet.CONTROL_PANEL_COLOR ); group2 = new CheckboxGroup(); button_sort = new Button( "Sort" ); add( button_sort ); checkbox_selection = new Checkbox( "Selection", false, group2 ); add( checkbox_selection ); checkbox_bubble = new Checkbox( "Bubble", true, group2 ); add( checkbox_bubble ); checkbox_insertion = new Checkbox( "Insertion", false, group2 ); add( checkbox_insertion ); checkbox_quick = new Checkbox( "Quick", false, group2 ); add( checkbox_quick ); } // end constructor } // end class Panel2