// file:    bucketsort.java
// author:  Robert Keller
// purpose: illustrating a very fast sorting program for natural numbers in
//          a reasonably small range only
//
//          bucketsort works as follows: The minimum and maximum of the 
//          range of numbers is found.  Then an array of "buckets" is 
//          allocated for each integral value between the minimum and
//          maximum inclusive.  The number of each value in the original
//          array is counted by one pass over the latter, using the datum
//          as an index into the bucket array.
//
//          This method is O(N) where N is the number of elements to be sorted;
//          a prime example of the use of the linear-addressing principle.
//
//          If the range of numbers is to large, the bucket array can't be
//          allocated and the method will fail.

import java.lang.*;
import java.io.*;
import java.util.*;

class bucketsort
  {
  /**
    *  Calling bucketsort constructor on array of floats sorts the array.
    *  Parameter N is the number of elements to be sorted.
   **/

  bucketsort(int array[], int N)
    {
    if( N <= 0 )
      return;                                   // Case of empty array

    int min = array[0];
    int max = min;
    for( int i = 1; i < N; i++ )                // Find the minimum and maximum
      if( array[i] > max )
        max = array[i];
      else if( array[i] < min )
        min = array[i];

    int bucket[] = new int[max-min+1];          // Create buckets
    
    for( int i = 0; i < N; i++ )                // "Fill" buckets
      bucket[array[i]-min]++;                   // by counting each datum

    int i = 0;                                  
    for( int b = 0; b < bucket.length; b++ )    // "Empty" buckets
      for( int j = 0; j < bucket[b]; j++ )      // back into array
        array[i++] = b+min;                     // by creating one per count
    }


  /**
    *  test program for bucketsort; reads arbitrarily-many numbers 
    *  from standard input, sorts them, then writes them to standard output.
   **/

  public static void main(String[] args)
    {
    StreamTokenizer in = new StreamTokenizer(System.in);

    int Size = 1;                       // initial allocation
    int N = 0;                          // number of elements in array
    int array[] = new int[Size];        // initial array allocation

    try
      {
      // while more numbers in file
      while( in.nextToken() != java.io.StreamTokenizer.TT_EOF ) 
        {
        if( N == Size )                 // if the array is full
          {
          array = reallocate(array);    // int the array size
          Size *= 2;    
          }
        array[N++] = (int)in.nval;      // put item in array
        }
      }
    catch(IOException e)
      {
      System.err.println("*** IOException caught");
      }

    Date startTime = new Date();

    System.err.println("Sorting started");

    new bucketsort(array, N);		// calling constructor sorts

    Date endTime = new Date();

    long time = endTime.getTime() - startTime.getTime();

    System.err.println("Sorting finished in " + time + " ms");

    for( int i = 0; i < N; i++ )
      {
      System.out.print(array[i] + " ");
      }    

    System.out.println();

    System.err.println("Sorting " + N + " elements using bucketsort took " + 
                        time + " ms");
    }


  /**
    *  reallocate allocates a new array int the size of the original
    *  and copies the original into it.  The new array is returned.
   **/

  static int[] reallocate(int array[])
    {
    int[] newArray = new int[2*array.length];
    for( int i = 0; i < array.length; i++ )             // copy old array
      newArray[i] = array[i];    
    return newArray;
    }
  }
