// Distributions. //
// Fill an array with values of 100 heights (from 1 to 3 meters).
//     values of 0-20 represent heights from 1.0 to 3.0 meters
// Display the  array horizontally.
// Generate a distribution table (histogram), y=height, x=0-100% 
// Flip the table, and display it with y=%, x=height

int many=100;
int a[] = new int[many];
int lo=10, hi=30, range=hi-lo;
int d[] = new int[range+1];          // Distribution
String typename="";
int type=0;
float sigma=1;
//
boolean debug=false, help=false, pause=false, sorted=false;
String author="bam@professorBAM.com";
String title=  "DISTRIBUTION:  ";
String hints;
int left=10, xHist=left+405;

void setup() {
  size(800, 500);
  reset();
  String s="";
  s += "r to randomize\n";
  s += "s to sort\n";
  s += "t to change distribution type\n";
  s += "? for help\n";
  s += "q to quit\n";
  hints=  s;
}

void draw() {
  background(255);
  titles();
  if (help) { 
    showHelp(100, 100); 
    return;
  }
  if (pause) { 
    text( "PAUSED", 50, 50); 
    return;
  }
  //  
  show( left, 200, a, many );           // Display the values
  showhist( xHist, 200, d, range );  // Display the histogram
  showflip( 400, 80, d, range );        // Flip the histogram
}

// Display array values as vertical bars at (xx,yy)
void show( int xx, int yy, int a[], int many ) {
  int x=xx, y=yy+range*10;

  for (int i=0; i<many; ++i) {
    int h=  10*(lo+a[i]);           // Height= 100cm + 10*a[i]
    fill(0, 0, 255);
    rect( x+1, y, 2, -h );          // DISPLAY THE BLUE BAR (with red head)
    fill(255, 0, 0);
    ellipse( x+2, y-h, 5, 5 );
    fill(0, 150, 0);
    if (i%10<1) text( h, x+2, y-h-10 );    // show every 10th height
    //--  if (i%10<1) text( a[i], x+2, y-h-30 );    // show every 10th value
    x += 4;
    // Space the bars 4 pixels apart.
  }
  if (! sorted)   text( "Press the 's' key, to sort the array.", xx+50, y+10 );
}
// Display histogram as horizontal bars at (xx,yy)
void showhist( int xx, int yy, int d[], int range ) {
  int x=xx, y=yy+range*10;
  int count, sum=0, sumpct=0;
  float pct;
  int leftbar=x+45, leftpct=x+300;
  //
  for (int i=0; i<range; ++i) {
    int h=  10*(lo+i);            // Height= 100cm + 10*i
    fill(0, 150, 0); 
    textSize(12);
    text( h, x+2, y-h );
    //
    count=  d[i];                // How many instances of this value?
    sum += count;
    fill(255, 0, 255);
    textSize(10);
    text( "("+count+")", x+27, y-h );
    //
    rect( leftbar, y-h, count*5, 2 );
    pct= 100 * float(count) / float(many);
    sumpct += pct;
    textSize(12);
    fill(0, 0, 255);
    text( int(pct) + "%", leftpct, y-h );
  }
  textSize(8);
  text( sum, x+25, y-90);
  text( sumpct+"%", leftpct, y-90);
}

// Display histogram as vertical bars at (xx,yy)
void showflip( int xx, int yy, int d[], int range ) {
  int x=xx, y=yy+range*10, bottom=x;
  int count;
  float pct;
  float spacing=1.5;
  //
  for (int i=0; i<range; ++i) {
    int h=  10*(lo+i);            // Height= 100cm + 10*i
    fill(0, 150, 0); 
    textSize(12);
    if (h%50<1) text( h, y+h*spacing-10, x+10 );
    if (i>=range-1) text( h, y+h*spacing-10, x+10 );
    //
    count=  d[i];                // How many instances of this value?
    fill(0);
    textSize(8);
    text( count, y+h*spacing, x+20 );
    rect( y+h*spacing, bottom, 2, -count*10 );        // Vertical bar!
    pct= 100 * float(count) / float(many);
    textSize(8);
    fill(0, 0, 255);
    text( int(pct) + "%", y+h*spacing, x+30 );
  }
}

// Build the histogram //
void histogram( int a[], int many, int d[], int range ) {
  for (int i=0; i<range; ++i) { 
    d[i]=0;
  }
  // Bump correct bin for each height 
  for (int i=0; i<many; ++i) {
    int j= a[i];
    j=  j<0 ? 0 : j ;
    j=  j<range ? j : range-1 ;
    ++d[j];
  }
}

void bigsort( int a[], int many ) {
  // Sort int array (moving biggest to end, then shrinking array)
  for (int m=many; m>1; --m) {
    int w=  wherebig( a, m );     // find biggest
    swap( a, w, m-1 );            // move it to end
    // Shrink array until only one
  }
  sorted=  true;
}
int wherebig( int a[], int m ) {
  // Returns index of biggest
  int w=0;                     // assume big is first
  for (int i=1; i<m; ++i) {    // Start with next element of array
    if (a[i] < a[w]) w=i;      // Found bigger
  }
  return w;                    // Return index of biggest
}
void swap( int a[], int j, int k ) {
  // Swap a[j] with a[k]
  int tmp=a[j];
  a[j]=  a[k];
  a[k]=  tmp;
}



//// FILL ARRAY WITH DIFFERENT DISTRIBUTIONS ////
String[] dtypes= { 
  "Uniform", "Gaussian", "Poisson", "Pseudo", "Expo"
};
// +++ change to 2=G, use 0,1,2,3 for sigma
int types=dtypes.length;
void reset() {
  type=  type%types;
  typename=  dtypes[type];
  //--  bug( "type="+type +":  " +typename );
  if (type==0) uniform( a, many, 0, range );
  else if (type==1) gaussian( sigma, a, many, 0, range );
  else if (type==2) poisson( a, many, 0, range );
  else if (type==3) pseudo( a, many, 0, range );
  else if (type==4) expo( a, many, 0, range );
  else { 
    typename="ERROR";
  }
  //--??--  else uniform( a, many, 0, range );
  histogram( a, many, d, range );      // Generate histogram d from a
  sorted=false;
}
// Change distribution type, then reset.
void reset( int t ) { 
  type=t; 
  reset();
}
// Change distribution type, then reset.
void reset( int t, float sig ) { 
  type=t; 
  sigma=sig;
  reset();
}

// Fill int array with random values, from lo to hi.
void uniform( int a[], int many, int lo, int hi ) {
  bug( typename );
  for (int i=0; i<many; ++i) {
    a[i]=  int( random(lo, hi) );
  }
}
void gaussian( float sigma, int a[], int many, int lo, int hi ) {
  int skip=5;
  float spread=sigma;
  float g;
  bug( typename + sigma );
  for (int i=0; i<many; ++i) {
    g=  randomGaussian();
    a[i]=  (hi-lo)/2 + int( g * spread);
  }
}
void poisson( int a[], int many, int lo, int hi ) {
  // STUB +++ Poisson //
  bug( typename );
  for (int i=0; i<many; ++i) {
    a[i]=  int( lo + float(poi(1) *(hi-lo)) / sigma );
  }
}
static final int poi(final double lambda) {
  final double L = Math.exp(-lambda);
  double p = 1;
  int k = -1;

  do ++k; 
  while ( (p *= Math.random ()) > L);
  return k;
}
void pseudo( int a[], int many, int lo, int hi ) {
  // STUB +++ // (Not really Poisson, but skewed low. //
  bug( typename );
  for (int i=0; i<many; ++i) {
    float center=  range * 0.25;
    float low=  random(0, center);
    float high=  random(center, range);
    a[i]=  int( low + 3*high) / 4;            // (Not really Poisson, but skewed low.
  }
}
void expo( int a[], int many, int lo, int hi ) {
  // STUB +++ Square //
  bug( typename );
  for (int i=0; i<many; ++i) {
    double power=  sigma;
    double q=  int( random(lo, hi) );
    double qq=  Math.pow(q, power), rr=  Math.pow(range, power), fraction= qq / rr;            
    double value=  range * fraction;
    int v=  (int) value;
    // Fraction of the maximum rr;
    a[i]=  v;      // (Should be 1..20);
  }
}



//// EVENT HANDLERS ////

void keyPressed() {
  if (key == 'q') exit();
  if (key == 'r') reset();
  if (key == 's') bigsort( a, many );
  if (key == 't') { reset( ++type ); }
  //
  if (key == 'U') { 
    reset( 0 );
  }  // Uniform distribution.
  if (key == 'N') { reset(1); }
  if (key == 'G') { reset(1,2); }   // Gaussian (Normal)
  if (key == 'P') { reset(2,3); }   // Poisson
  if (key == 'Q') { reset(3); }     // Pseudo
  if (key == 'E') { reset(2,2); }   // Exponential
  //
  if (key == '0') { reset(1,0); }
  if (key == '1') { reset(1,1); }
  if (key == '2') { reset(1,2); }
  if (key == '3') { reset(1,3); }
  if (key == '4') { reset(1,4); }
  if (key == '5') { reset(1, 0.5); }  
  if (key == '6') { reset(1, 2.0/3.0); }  
  if (key == '7') { reset(1, 0.75); }  
  if (key == '8') { reset(1, 0.875); }  
  if (key == '9') { reset(1, 0.99); }  
  //
  if (key == '?') { help = ! help; }
  if (key == 'd') { debug = ! debug; }
  if (key == 'p') { pause = ! pause; }
}

void bug( String s) { if (debug) println( s ); }

void showHelp( int x, int y ) {
  String s="";
  s += "r to randomize\n";
  s += "s to sort\n";
  s += "t to change distribution type\n";
  s += "? for help\n";
  s += "p to pause\n";
  s += "q to quit\n";
  hints=  s;
  //
  int h=12, next=0;
  text( "Array is filled with 100 random values (0 to 20)\n", x, y+h*next++ );     
  text( "    (representing heights ranging from 1 to 3 meters).\n", x, y+h*next++ );     
  ++next;
  text( "Display the array  horizontally.\n", x, y+h*next++ );
  text( "Generate a distribution table (histogram):  y=height, x=0-100% \n", x, y+h*next++ );
  text( "Flip the table, and display it with y=%, x=height \n", x, y+h*next++ );
  text( "Press the 's' key, to sort the array.", x, y+h*next++ );
  ++next;
  text( "Distribution types:\n", x, y+h*next++ );
  text( "    Uniform", x, y+h*next++ );
  text( "    Gaussian (Normal) -- depends upon value of sigma.", x, y+h*next++ );
  text( "    Poisson", x, y+h*next++ );
  text( "    Pseudo", x, y+h*next++ );
  text( "    Exponential", x, y+h*next++ );
  ++next;
  text( "Sigma values are selected by keys:  0, 1, 2, 3, 4, or ...\n", x, y+h*next++ );
  text( "    5 for 0.50.", x, y+h*next++ );
  text( "    6 for 2/3", x, y+h*next++ );
  text( "    7 for 0.75", x, y+h*next++ );
  text( "    8 for 0.875", x, y+h*next++ );
  text( "    9 for 0.99", x, y+h*next++ );
  ++next;
  text( "Press ? key again, to resume.", x, y+h*next++ );
}
void titles() {
  fill(0);
  textSize(24);
  String t=  typename;
  if (type==1) t += "  (" + sigma + " §)";
  text( "DISTRIBUTION:  "+ t, width/3, 20 );
  textSize(12);
  text( "(heights range from 100-300 cm)", width/3, 35 );
  //-- text( " s to sort\n r to randomize\n q to quit", width*3/4, 30 );
  text(hints, width*3/4, 40 );
  fill(255,0,0);
  if (debug) text( "DEBUG MODE ON", width/2-20, height-20 );
}

