public class Sorting {
  
  /**
   * Display the contents of the list.
   */
  public static void displayList(int[] list) {
    for(int i = 0; i < list.length; i++) {
      System.out.print(list[i] + " ");
    }
    System.out.println();
  }

  /**
   * Sort the items in an array in increasing
   * order.
   * 
   * Bubble sort:
   * - traverse the list comparing neighbouring
   *   elements
   * - move the smaller element into the lower
   * position
   * - repeat list.length times
   */
  public static void bubbleSort(int[] list) {
    int swaps = 0;
    int comps = 0;
    
    for(int j = 0; j < list.length; j++) {
      for(int i = 0; i < list.length - 1; i++) {
        
        // compare adjacent elements
        if(list[i] > list[i+1]) {
          
          // swap
          int temp = list[i];
          list[i] = list[i+1];
          list[i+1] = temp;
          swaps++;
        }
        comps++;
      }
    }
    displayList(list);
    
    System.out.println("Swaps: " + swaps 
                         + " Comparisons: " + comps); 
  }
  
  /* Bubble sort (improved!):
   * - stop when the list is sorted
   * - don't compare with elements that are
   * definitely in their sorted position
   */
  public static void bubbleSort2(int[] list) {
    boolean didSwap = true;
    int j = 0;
    int swaps = 0;
    int comps = 0;
    
    while(didSwap) {
      didSwap = false;
      for(int i = 0; i < list.length - 1 - j; i++) {
        
        // compare adjacent elements
        if(list[i] > list[i+1]) {
          didSwap = true;
          // swap
          int temp = list[i];
          list[i] = list[i+1];
          list[i+1] = temp;
          swaps++;
        }
        comps++;
      }
      j++;
    }
    displayList(list);
    System.out.println("Swaps: " + swaps 
                         + " Comparisons: " + comps); 

  }

  /** 
   * Insertion sort:
   * - pick an element from the unsort section of
   * the list
   * - insert it into its proper position in the
   * sorted part of the list
   * - repeat for all elements
   */
  public static void insertionSort(int[] list) {
    int swaps = 0;
    int comps = 0;
    
    for(int i = 0; i < list.length; i++) {
      int j = i;
      
      // compare element i with the elements
      // in the sorted part of the list [0..i-1]
      // and insert it into the sorted part of the list
      while(j != 0 && list[j] < list[j-1] ){
      
        // swap element at j with element at j-1
        int temp = list[j];
        list[j] = list[j-1];
        list[j-1] = temp;
        j--;
        swaps++;
        comps++;
      }
      comps++;
    }
     System.out.println("Swaps: " + swaps 
                         + " Comparisons: " + comps); 

  }
  
  /**
   * Selection sort:
   * - find the smallest element, put it in position 0
   * - find the 2nd smallest, put it in position 1
   * ...
   * - find the nth smallest, put it in position n-1
   */
  public static void selectionSort(int[] list) {
    int swaps = 0;
    int comps = 0;
    
    for(int i = 0; i < list.length; i++) {
      int smallestIndex = i;
      
      // compare i with unsorted elements:
      // [i to list.length-1]
      for(int j = i+1; j < list.length; j++) {
        comps++;
        if(list[smallestIndex] > list[j]) {
          smallestIndex = j;
        }
      }
      
      // put the element at smallestIndex into
      // its proper position in the sorted section
      int temp = list[i];
      list[i] = list[smallestIndex];
      list[smallestIndex] = temp;
      swaps++;
    }
    displayList(list);
    System.out.println("Swaps: " + swaps 
                         + " Comparisons: " + comps); 

  }
  
  /** 
   * Search:
   * find element x in a sorted list.
   */ 
  
  /** 
   * Linear search
   */
  public static int linearSearch(int[] list, int x) {
    
    for(int i = 0; i < list.length; i++) {
      if(list[i] == x) {
        return i;
      }
    }
    return -1;
  }
  
  /**
   * Binary search
   */
  public static int binarySearch(int[] list, int x) {
    int i = 0;
    int j = list.length - 1;
    
    // list[i..j] is the unknown section
    // done when list [i..j] is empty
    while(i != j + 1) {
      int m = (i + j) / 2;
      if(list[m] <= x) {
        i = m + 1;
      } else {
        j = m - 1;
      }
    }
    if(j >= 0 && j < list.length && list[j] == x) {
      return j;
    } else {
      return -1;
    }
  }
}

