//  RandomGenerator.java
//
//  Author:
//       Antonio J. Nebro <antonio@lcc.uma.es>
//       Juan J. Durillo <durillo@lcc.uma.es>
//
//  Copyright (c) 2011 Antonio J. Nebro, Juan J. Durillo
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
// 
//  You should have received a copy of the GNU Lesser General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.

package jmetal.util;

import java.util.Random;

 /**
 * This code has been taken from deb NSGA-II implementation
 * The code is available to download from
 * http://www.iitk.ac.in/kangal/codes.shtml
 *
 */

public class RandomGenerator implements IRandomGenerator{
    
  /* Definition of random number generation routines */
  double seed;
  double [] oldrand = new double[55];
  int jrand;

  /**
   * Constructor
   */
  public RandomGenerator(){
    this.seed = (new Random(System.nanoTime())).nextDouble();
    this.randomize();
  } // RandomGenerator

  /* Get seed number for random and start it up */
  void randomize(){
    for(int j1=0; j1<=54; j1++){
      oldrand[j1] = 0.0;
    }
    jrand=0;
    warmup_random (seed);
  } // randomize

  /* Get randomize off and running */
  void warmup_random (double seed){
    int j1, ii;
    double new_random, prev_random;
    oldrand[54] = seed;
    new_random = 0.000000001;
    prev_random = seed;
    for(j1=1; j1<=54; j1++){
      ii = (21*j1)%54;
      oldrand[ii] = new_random;
      new_random = prev_random-new_random;
      if(new_random<0.0){
        new_random += 1.0;
      }
      prev_random = oldrand[ii];
    }
    advance_random ();
    advance_random ();
    advance_random ();
    jrand = 0;
  } // warmup_random

  /* Create next batch of 55 random numbers */
  void advance_random (){
    int j1;
    double new_random;
    for(j1=0; j1<24; j1++){
      new_random = oldrand[j1]-oldrand[j1+31];
      if(new_random<0.0){
        new_random = new_random+1.0;
      }
      oldrand[j1] = new_random;
    }
    for(j1=24; j1<55; j1++){
      new_random = oldrand[j1]-oldrand[j1-24];
      if(new_random<0.0){
        new_random = new_random+1.0;
      }
      oldrand[j1] = new_random;
    }
  } //advance_ramdom

  /* Fetch a single random number between 0.0 and 1.0 */
  double randomperc(){
    jrand++;
    if(jrand>=55){
      jrand = 1;
      advance_random();
    }
    return((double)oldrand[jrand]);
  } //randomPerc

  /* Fetch a single integer between 0 and upperbound */
  synchronized public int nextInt(int upperBound) {
  	return rndInt(0, upperBound) ;
  }

  /* Fetch a single double between 0.0 and 1.0 */
  synchronized public double nextDouble() {
  	return randomperc() ;
  }
  
  /* Fetch a single random integer between low and high including the bounds */
  synchronized public int rndInt (int low, int high){
    int res;
    if (low >= high){
      res = low;
    } else {
      res = low + (int)(randomperc()*(high-low+1));
      if (res > high){
        res = high;
      }
    }
    return (res);
  } // rnd

  /* Fetch a single random real number between low and high including the */
  /* bounds */
  synchronized public double rndReal (double low, double high){
    return (low + (high-low)*randomperc());
  } //rndreal
} // RandomGenerator
