package nxm.sys.libm;

/**
  Class to create a pseudo-random number.<br>
  <br>
  Note that at the start of each JVM session, the initial seed value is set to
  12345678 and this seed is used for the random number generation.
  This consistent non-random setting of seed allows for consistent random data
  generation for testing across repeated sessions. {@link #setSeed(int)} MUST
  must be used if a truly random seed is required for each session.

  @author Jeff Schoen
*/
public class Random extends Operator {

  /** constants: T26 == 2^26 */
  private static final double A=67081293.0, B=14181771.0, T26=67108864.0;
  private static final double BI = B/T26;
  private static int seed = 123456789;

  /** Random Default Constructor */
  public Random() { super(); }
  /** Random Constructor
      @param flags any processing flags to use
   */
  public Random (int flags) { super(flags); }
  /** Random Constructor
      @param scale the scale factor to use
   */
  public Random (double scale) { super(scale); }
  /** Random Constructor
      @param flags any processing flags to use
      @param scale the scale factor to use
   */
  public Random (int flags, double scale) { super(flags,scale); }

  public int getInputs() { return 0; }
  public int getOutputs() { return 1; }

  public int validate (char type, int n, int mode) {
    switch (mode) {
      case S: case C: case V: break;
      default: n=NOSUPPORT;
    } return n;
  }

  public int process (double[][] v, int n, int mode, double scale) {
    switch (mode) {
      case S: get(v[0],n,scale); break;
      case C: get(v[0],n*2,scale); break;
      case V: get(v[0],n*3,scale); break;
      default: return NOSUPPORT;
    }
    return n;
  }

  public int process (double[][] v, int n, int mode) {
    switch (mode) {
      case S: get(v[0],n,scale); break;
      case C: get(v[0],n*2,scale); break;
      case V: get(v[0],n*3,scale); break;
      default: return NOSUPPORT;
    }
    return n;
  }

  /** Sets the seed for the pseudo-random number generator
      @param a     array to apply the operator to
      @param size  the size of the array
      @param scale the scale factor to apply
   */
  public static void get (double[] a, int size, double scale) {
    double sis=((double)seed)/T26;
    for (int i=0; i<size;) {
      sis = sis*A + BI;
      sis = sis - (double)(int)sis;
      a[i++] = (sis*scale);
    }
    seed = (int)(sis*T26);
  }

  public int process (float[][] v, int n, int mode, double scale) {
    switch (mode) {
      case S: get(v[0],n,scale); break;
      case C: get(v[0],n*2,scale); break;
      case V: get(v[0],n*3,scale); break;
      default: return NOSUPPORT;
    }
    return n;
  }

  public int process (float[][] v, int n, int mode) {
    switch (mode) {
      case S: get(v[0],n,scale); break;
      case C: get(v[0],n*2,scale); break;
      case V: get(v[0],n*3,scale); break;
      default: return NOSUPPORT;
    }
    return n;
  }

  /** Sets the seed for the pseudo-random number generator
      @param a     array to apply the operator to
      @param size  the size of the array
      @param scale the scale factor to apply
   */
  public static void get (float[] a, int size, double scale) {
    double sis=((double)seed)/T26;
    for (int i=0; i<size;) {
      sis = sis*A + BI;
      sis = sis - (double)(int)sis;
      a[i++] = (float)(sis*scale);
    }
    seed = (int)(sis*T26);
  }

  /** Get the current seed
   *  @return current seed for the pseudo-random number generator.
   *  @since NeXtMidas 3.1.1
   */
  public static int getSeed () { return seed; }

  /** Set new seed for the pseudo-random number generator.
      @param newSeed new seed value. Valid range is [0, 2^26)
      @since NeXtMidas 3.1.1
   */
  public static void setSeed (int newSeed) {
    if (newSeed >= T26) throw new IllegalArgumentException("new seed ["+newSeed+"] must be less than 2^26");
    seed = newSeed;
  }
}
