You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by to...@apache.org on 2003/05/18 02:58:52 UTC

cvs commit: jakarta-commons-sandbox/math/src/test/org/apache/commons/math RandomDataTest.java MathTestSuite.java

tobrien     2003/05/17 17:58:52

  Modified:    math/src/test/org/apache/commons/math MathTestSuite.java
  Added:       math/src/java/org/apache/commons/math RandomData.java
                        RandomDataImpl.java
               math/src/test/org/apache/commons/math RandomDataTest.java
  Log:
  Phil Steitz wrote:
  
  This commit contains the suite of random data generation utilities that
  I originally
  proposed as extensions to lang.math.  There is some functional overlap
  with lang.math, but the contract and intention of this implementation is
  different in several significant ways.
  
  * the lang implementation maintains "immutability" of the underlying
     random number generator (emulating Math). The RandomData
     implementation allows users to reseed the random number generator(s)
     (this is in effect possible in the recent extensions to lang.math by
     passing in a user-supplied random as an actual parameter to the
     next() methods) Users can also reset the PRNG algorithm and provider
     used by the "secure" methods.
  
  * RandomData includes "secure" methods (delegating to SecureRandom)
  
  * RandomData will generate random deviates from exponential and poisson,
     as well as Gaussian and Uniform distributions.  These are useful in
     simulation applications.
  
  * Overlapping somewhat with lang.StringUtils, RandomData will generate
     random hex strings.  There is a nextSecureHexString method that will
     (I claim :-) generate cryptographically secure string identifiers. I
     would appreciate feedback on this algorithm, which I have seen used
     elsewhere (similar to what tomcat does to generate session ids); but
     not documented as a standard.
  
  PR: Bugzilla 20013
  Obtained from: Phil S.
  Submitted by: Phil S.
  Reviewed by: Tim O.
  
  Revision  Changes    Path
  1.1                  jakarta-commons-sandbox/math/src/java/org/apache/commons/math/RandomData.java
  
  Index: RandomData.java
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2003 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  package org.apache.commons.math;
  
  /**
   * Random data generation utilities
   * @author Phil Steitz
   * @version $Revision: 1.1 $ $Date: 2003/05/18 00:58:51 $
   */
  public interface RandomData {      
      /**
       * Generates a random string of hex characters of length
       * <code>len</code>.<br>
       * The generated string will be random, but not cryptographically 
       * secure. To generate cryptographically secure strings, use 
       * <code>nextSecureHexString</code>
       * @param len the length of the string to be generated
       * @throws IllegalArgumentException if <code>len</code> is not positive.
       */
      public String nextHexString(int len);  
      
      /**
       * Generates a uniformly distributed random integer between 
       * <code>lower</code> and <code>upper</code> (endpoints included).<br>
       * The generated integer will be random, but not cryptographically secure.
       * To generate cryptographically secure integer sequences, use 
       * <code>nextSecureInt</code>.
       * @param lower lower bound for generated integer
       * @param upper upper bound for generated integer
       * @exception IllegalArgumentException thrown if
       * <code>lower</code> is not strictly less than <code>upper</code>.
       * @return a random integer greater than or equal to <code>lower</code> 
       * and less than or equal to <code>upper</code>.
       */
      public int nextInt(int lower, int upper);  
      
      /**
       * Generates a uniformly distributed random long integer between <
       * code>lower</code> and <code>upper</code> (endpoints included).
       * The generated long integer values will be random, but not 
       * cryptographically secure.<br> 
       * To generate cryptographically secure sequences of longs, use 
       * <code>nextSecureLong</code>
       * @param lower lower bound for generated integer
       * @param upper upper bound for generated integer
       * @exception IllegalArgumentException Thrown if lower > upper
       * @return a random integer greater than or equal to <code>lower</code>
       * and less than or equal to <code>upper</code>.
       */
      public long nextLong(long lower, long upper);  
      
      /**
       * Generates a random string of hex characters from a secure random sequence.
       * If cryptographic security is not required, 
       * use <code>nextHexString()</code>.
       * @param len length of return string
       * @exception IllegalArgumentException thrown if len <= 0 
       * @return the random hex string
       */
      public String nextSecureHexString(int len);  
      
      /**
       * Generates a uniformly distributed random integer between 
       * <code>lower</code> and <code>upper</code> (endpoints included) 
       * from a secure random sequence.<br>
       * The generated sequence will be cryptographically secure.<br>
       * If cryptographic security is not required, <code>nextInt</code>
       * should be used.<br>
       * <strong>Definition</strong>(secure random sequence):
       * http://www.wikipedia.org/wiki/Cryptographically_secure_pseudo-random_number_generator<br>
       * @param lower lower bound for generated integer
       * @param upper upper bound for generated integer
       * @exception IllegalArgumentException thrown if
       * <code>lower</code> is not strictly less than <code>upper</code>.
       * @return a random integer greater than or equal to <code>lower</code>
       * and less than or equal to <code>upper</code>.
       */
      public int nextSecureInt(int lower, int upper);  
      
      /**
       * Generates a random long integer between <code>lower</code>
       * and <code>upper</code> (endpoints included).<br>
       * The generated long sequence will be cryptographically secure.<br>
       * If cryptographic security is not required,
       * use <code>nextLong</code><br>
       * <strong>Definition</strong>:
       * <a href=http://www.wikipedia.org/wiki/Cryptographically_secure_pseudo-random_number_generator>
       * Secure Random Sequence</a>
       * @param lower lower bound for generated integer
       * @param upper upper bound for generated integer
       * @exception IllegalArgumentException thrown if
       * <code>lower</code> is not strictly less than <code>upper</code>.
       * @return a long integer greater than or equal to <code>lower</code>
       * and less than or equal to <code>upper</code>.
       */
      public long nextSecureLong(long lower, long upper);  
      
      /** 
       * Generates a random value from the Poisson distribution with 
       * the given mean.<br>
       * <strong>Definition</strong>: 
       * <a href=http://www.itl.nist.gov/div898/handbook/eda/section3/eda366j.htm>
       * Poisson Distribution</a><br>
       * <strong>Preconditions</strong>: <ul>
       * <li>The specified mean <i>must</i> be positive </li>
       * </ul>
       * @param mean Mean of the distribution
       * @returns long
       * @throws IllegalArgumentException if mean <= 0
       */
      public long nextPoisson(double mean);  
      
      /** 
       * Generates a random value from the
       * Normal (a.k.a. Gaussian) distribution with the given mean
       * and standard deviation.<br>
       * <strong>Definition</strong>: 
       * <a href=http://www.itl.nist.gov/div898/handbook/eda/section3/eda3661.htm>
       * Normal Distribution</a><br>
       * <strong>Preconditions</strong>: <ul>
       * <li>The specified standard deviation <i>must</i> be positive </li>
       * </ul>
       * @param mu Mean of the distribution
       * @param sigma Standard deviation of the distribution
       * @return random value from Gaussian distribution with mean = mu,
       * standard deviation = sigma
       * @throws IllegalArgumentExcption if sigma <= 0
       */
      public double nextGaussian(double mu,double sigma);  
      
      /**
       * Generates a random value from the exponential distribution
       * with expected value = <code>mean</code><br>
       * <strong>Definition</strong>: 
       * <a href=http://www.itl.nist.gov/div898/handbook/eda/section3/eda3667.htm>
       * Exponential Distribution</a><br>
       * <strong>Preconditions</strong>: <ul>
       * <li>The specified mean <i>must</i> be non-negative</li>
       * </ul>
       * @param mu Mean of the distribution
       * @return random value from exponential distribution
       */
      public double nextExponential(double mean);   
      
      /**
       * Generates a uniformly distributed random value from the opem interval
       * (<code>lower</code>,<code>upper</code>) (i.e., endpoints excluded)
       * <strong>Definition</strong>: 
       * <a href=http://www.itl.nist.gov/div898/handbook/eda/section3/eda3662.htm>
       * Uniform Distribution</a> <code>lower</code> and <code>upper - lower</code>
       * are the 
       * <a href = http://www.itl.nist.gov/div898/handbook/eda/section3/eda364.htm>
       * location and scale parameters</a>, respectively<br>
       * @param lower lower endpoint of the interval of support
       * @param upper upper endpoint of the interval of support
       * @return uniformly distributed random value between lower
       * and upper (exclusive)
       * @exception IllegalArgumentException thrown if
       * <code>lower</code> is not strictly less than <code>upper</code>.
       */
      public double nextUniform(double lower, double upper);   
  }
  
  
  
  1.1                  jakarta-commons-sandbox/math/src/java/org/apache/commons/math/RandomDataImpl.java
  
  Index: RandomDataImpl.java
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2003 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  package org.apache.commons.math;
  
  import java.security.MessageDigest;
  import java.security.SecureRandom;
  import java.security.NoSuchAlgorithmException;
  import java.security.NoSuchProviderException;
  import java.util.Random;
  
  /**
   * Implements the <code>RandomData</code> interface using 
   * <code>java.util.Random</code> and 
   * <code>java.util.Random.SecureRandom</code> instances to generate data.
   * Supports reseeding the underlying 
   * <a href=http://www.wikipedia.org/wiki/Pseudo-random_number_generator>PRNG</a>. 
   * The <code>SecurityProvider</code> and <code>Algorithm</code>
   * used by the <code>SecureRandom</code> instance can also be reset.<p>
   * For details on the PRNGs, see the JDK documentation for 
   * <code>java.util.Random</code> and 
   * <code>java.util.Random.SecureRandom</code></p><p>
   * <strong>Usage Notes</strong>: <ul>
   * <li>Instance variables are used to maintain <code>Random</code> and 
   * <code>SecureRandom</code> instances used in data generation. Therefore,
   * to generate a random sequence of values or strings, you should use just
   * <strong>one</strong> <code>RandomDataImpl</code> instance repeatedly.</li>
   * <li>The "secure" methods are *much* slower.  These should be used only when
   * a <a href=http://www.wikipedia.org/wiki/Cryptographically_secure_pseudo-random_number_generator>
   * Secure Random Sequence</a> is required.</li>
   *<li>When a new <code>RandomDataImpl</code> is created, the underlying random
   * number generators are <strong>not</strong> intialized.  The first call to a
   * data generation method, or to a <code>reSeed()</code> method instantiates
   * the appropriate generator.  If you do not explicitly seed the generator, it
   * is by default seeded with the current time in milliseconds</li>
   * <li>The <code>reSeed</code> and <code>reSeedSecure</code> methods delegate to
   * the corresponding methods on the underlying <code>Random</code> and <code>
   * SecureRandom</code> instances.  Therefore, the contracts of these methods
   * are as defined in the JDK documentation.  In particular, <code>reSeed(long)
   * </code> fully resets the initial state of the non-secure random number 
   * generator (so that reseeding with a specific value always results in the
   * same subsequent random sequence); whereas reSeedSecure(long) does <strong> not 
   * </strong> reinitialize the secure random number generator (so secure sequences
   * started with calls to reseedSecure(long) won't be identical).</li></ul>
   *</p>
   * 
   * @author Phil Steitz
   * @version $Revision: 1.1 $ $Date: 2003/05/18 00:58:51 $
   */
  public class RandomDataImpl implements RandomData{
      
      /** underlying random number generator */
      private Random rand = null;
      
      /** underlying secure random number generator */
      private SecureRandom secRand = null;
      
      public RandomDataImpl(){
      }
            
      /**
       * Generates a random string of hex characters
       * If cryptographic security is required, use 
       * <code>nextSecureHexString()</code>.<br>
       * <strong>Algorithm Description:</strong> hex strings are generated 
       * using a 2-step process. <ol>
       * <li>len/2+1 binary bytes are generated using the underlying Random</li>
       * <li>Each binary byte is translated into 2 hex digits</li></ol>
       * @param len length of return string
       * @exception IllegalArgumentException thrown if len <= 0 
       * @return the random hex string
       */
      public String nextHexString(int len) {
          if (len <= 0) {
              throw new IllegalArgumentException("length must be positive");
          }
              
          //Get a random number generator
          Random ran = getRan();
          
          //Initialize output buffer
          StringBuffer outBuffer = new StringBuffer();
              
          //Get int(len/2)+1 random bytes
          byte[] randomBytes = new byte[(len / 2) + 1];
          ran.nextBytes(randomBytes);
   
          //Convert each byte to 2 hex digits
          for (int i = 0; i < randomBytes.length; i++) {
              Integer c = new Integer(randomBytes[i]);
                  
              /* Add 128 to byte value to make interval 0-255 before
               * doing hex conversion.
               * This guarantees <= 2 hex digits from toHexString()
               * toHexString would otherwise add 2^32 to negative arguments.
               */
               String hex = Integer.toHexString(c.intValue()+128);
                  
               // Make sure we add 2 hex digits for each byte
               if (hex.length() == 1) hex = "0" + hex;
               outBuffer.append(hex);
          }
          return outBuffer.toString().substring(0, len);
      }
      
       
      public int nextInt(int lower, int upper) {
          if (lower >= upper) {
              throw new IllegalArgumentException
                  ("incorrect bounds for rendomInt");
          }
          Random rand = getRan();
          return lower + (int)(Math.random() * (upper-lower+1));
      }
      
      public long nextLong(long lower, long upper) {
          if (lower >= upper) {
              throw new IllegalArgumentException
                  ("upper bound must be >= lower bound");
          }
          Random rand = getRan();
          return lower + (long)(rand.nextDouble() * (upper-lower+1));
      }
      
       /**
       * Generates a random string of hex characters from a secure random sequence.
       * If cryptographic security is not required, 
       * use <code>nextHexString()</code>.<br>
       * <strong>Algorithm Description:</strong> hex strings are generated in 40-byte
       * segments using a 3-step process. <ol>
       * <li>20 random bytes are generated using the underlying SecureRandom</li>
       * <li>SHA-1 hash is applied to yield a 20-byte binary digest</li>
       * <li>Each byte of the binary digest is converted to 2 hex digits</li></ol><p>
       * TODO: find external reference or provide justification for the claim that this
       * yields a cryptographically secure sequence of hex strings.</p>
       * @param len length of return string
       * @exception IllegalArgumentException thrown if len <= 0 
       * @return the random hex string
       */
      public String nextSecureHexString(int len) {
          if (len <= 0) {
              throw new IllegalArgumentException("length must be positive");
          }
         
         // Get SecureRandom and setup Digest provider
         SecureRandom secRan = getSecRan();
         MessageDigest alg = null;
         try {
              alg = MessageDigest.getInstance("SHA-1");
         } catch (NoSuchAlgorithmException ex) {
             return null; // gulp FIXME? -- this *should* never fail. OK to swallow????
         }
         alg.reset(); 
         
         //Compute number of iterations required (40 bytes each)
         int numIter = (len / 40) + 1;
         
         StringBuffer outBuffer = new StringBuffer();
         for (int iter = 1; iter < numIter + 1; iter++) {
              byte[] randomBytes = new byte[40];
              secRan.nextBytes(randomBytes);
              alg.update(randomBytes);
      
              //Compute hash -- will create 20-byte binary hash
              byte hash[] = alg.digest();
              
              //Loop over the hash, converting each byte to 2 hex digits
              for (int i = 0; i < hash.length; i++) {
                  Integer c = new Integer(hash[i]);
          
                  /* Add 128 to byte value to make interval 0-255
                   * This guarantees <= 2 hex digits from toHexString()
                   * toHexString would otherwise add 2^32 to negative 
                   * arguments
                   */
                  String hex = Integer.toHexString(c.intValue()+128);
                      
                 //Keep strings uniform length -- guarantees 40 bytes
                 if (hex.length() == 1) hex = "0" + hex;
                 outBuffer.append(hex);
              }
          }
          return outBuffer.toString().substring(0, len);
      }
      
      public int nextSecureInt(int lower, int upper) {
            if (lower >= upper) {
                throw new IllegalArgumentException
                  ("lower bound must be <= upper bound");
            }
            SecureRandom sec = getSecRan();
            return lower + (int)(sec.nextDouble() * (upper-lower+1));
      }
      
      
      public long nextSecureLong(long lower, long upper) {
          if (lower >= upper) {
              throw new IllegalArgumentException
              ("lower bound must be <= upper bound");
          }
          SecureRandom sec = getSecRan();
          return lower + (long)(sec.nextDouble() * (upper-lower+1));
      }
      
      /** 
       * Generates a random value from the Poisson distribution with 
       * the given mean.<br>
       * <strong>Definition</strong>: 
       * <a href=http://www.itl.nist.gov/div898/handbook/eda/section3/eda366j.htm>
       * Poisson Distribution</a><br>
       * <strong>Algorithm Description</strong>:
       * Uses simulation of a Poisson process using Uniform deviates, as described 
       * <a href = http://dmawww.epfl.ch/benarous/Pmmi/interactive/rng7.htm>
       * here</a>
       * @param mean Mean of the distribution
       * @returns long
       * @throws IllegalArgumentException if mean <= 0
       */
      public long nextPoisson(double mean) {
          double p = Math.exp(-mean);
          long n = 0;
          double r = 1.0d;
          Random rand = getRan();
          if (mean <= 0) {
              throw new IllegalArgumentException("Poisson mean must be > 0");
          }
          while (true) {
              double rnd = rand.nextDouble();
              r = r * rnd;
              if (r >= p) {
                  n++;
              } else {
                  return n;
              }
          }
      }
      
      public double nextGaussian(double mu,double sigma) {
          if (sigma <= 0) {
              throw new IllegalArgumentException("Gaussian std dev must be > 0");
          }
          Random rand = getRan();
          return sigma*rand.nextGaussian() + mu;
      }
      
      /**
       * Generates a random value from the exponential distribution
       * with expected value = <code>mean</code><br>
       * <strong>Definition</strong>: 
       * <a href=http://www.itl.nist.gov/div898/handbook/eda/section3/eda3667.htm>
       * Exponential Distribution</a><br>
       * <strong>Preconditions</strong>: <ul>
       * <li>The specified mean <i>must</i> be non-negative</li>
       * </ul>
       * <strong>Algorithm Description</strong>:  Uses the 
       * <a href=http://www.jesus.ox.ac.uk/~clifford/a5/chap1/node5.html> 
       * Inversion Method</a> to generate exponential from uniform deviates.
       * @param mu Mean of the distribution
       * @return random value from exponential distribution
       */
      public double nextExponential(double mean)  {
          if (mean < 0.0) throw new IllegalArgumentException
              ("Exponential mean must be >= 0");
          Random rand = getRan();
          double unif = rand.nextDouble();
          while (unif == 0.0d) {
              unif = rand.nextDouble();
          }
          return -mean*Math.log(unif);
      }
      
      /**
       * Generates a uniformly distributed random value from the open interval
       * (<code>lower</code>,<code>upper</code>) (i.e., endpoints excluded)
       * <strong>Definition</strong>: 
       * <a href=http://www.itl.nist.gov/div898/handbook/eda/section3/eda3662.htm>
       * Uniform Distribution</a> <code>lower</code> and <code>upper - lower</code>
       * are the 
       * <a href = http://www.itl.nist.gov/div898/handbook/eda/section3/eda364.htm>
       * location and scale parameters</a>, respectively<br>
       * <strong>Algorithm Description</strong>: scales the output of 
       * Random.nextDouble(), but rejects 0 values (i.e., will generate another
       * random double if Random.nextDouble() returns 0).  This is necessary to
       * provide a symmetric output interval (both endpoints excluded).
       * @param lower lower endpoint of the interval of support
       * @param upper upper endpoint of the interval of support
       * @return uniformly distributed random value between lower
       * and upper (exclusive)
       * @exception IllegalArgumentException thrown if
       * <code>lower</code> is not strictly less than <code>upper</code>.
       */
      public double nextUniform(double lower, double upper) {
          if (lower >= upper) {
              throw new IllegalArgumentException
              ("lower bound must be <= upper bound");
          }
          Random rand = getRan();
          double result = lower + rand.nextDouble()*(upper-lower);
          while (result == lower) {
                result = lower + rand.nextDouble()*(upper-lower);
          }
          return result;   
      }
      
      /** 
       * Returns the static Random used to generate random data.<br>
       * Creates and initializes if null
       * @return the static Random used to generate random data
       */
      private Random getRan() {
          if (rand == null) {
              rand = new Random();
              rand.setSeed(System.currentTimeMillis());
          }
          return rand;
      }
      
      /** 
       * Returns the static SecureRandom used to generate secure random data.<br>
       * Creates and initializes if null.
       * @return the static SecureRandom used to generate secure random data
       */
      private SecureRandom getSecRan() {
          if (secRand == null) {
              secRand = new SecureRandom();
              secRand.setSeed(System.currentTimeMillis());
          }
          return secRand;
      }
      
      /**
       * Reseeds the random number generator with the supplied seed.  Will
       * create and initialize if null.
       * @param seed the seed value to use
       */
      public void reSeed(long seed) {
          if (rand == null) {
              rand = new Random();
          }
          rand.setSeed(seed);
      }
      
      /**
       * Reseeds the secure random number generator with the current time
       * in milliseconds.  Will create and initialize if null.
       */
      public void reSeedSecure() {
          if (rand == null) {
              rand = new Random();
          }
          rand.setSeed(System.currentTimeMillis());
      }
      
      /**
       * Reseeds the secure random number generator with the supplied seed.
       * Will create and initialize if null.
       * @param seed the seed value to use
       */
      public void reSeedSecure(long seed) {
          if (secRand == null) {
              secRand = new SecureRandom();
          }
          secRand.setSeed(seed);
      }
      
      /**
       * Reseeds the random number generator with the current time
       * in milliseconds
       */
      public void reSeed() {
          if (rand == null) {
              rand = new Random();
          }
          rand.setSeed(System.currentTimeMillis());
      }
      
      /**
       * Sets the PRNG algorithm for the underlying SecureRandom instance
       * using the Security Provider API, as defined in 
       * <a href=http://java.sun.com/j2se/1.3/docs/guide/security/CryptoSpec.html#AppA>
       * Java Cryptography Architecture API Specification & Reference</a><p>
       * <strong>USAGE NOTE:</strong> This method carries <i>significant</i> overhead
       * and may take several seconds to execute.</p>
       * @param algorithm the name of the PRNG algorithm
       * @param provider the name of the provider 
       * @throws NoSuchAlgorithmException if the specified algorithm is not available
       * @throws NoSuchProviderException if the specified provider is not installed
       */
      public void setSecureAlgorithm(String algorithm, String provider) 
          throws NoSuchAlgorithmException,NoSuchProviderException {
          secRand = SecureRandom.getInstance(algorithm,provider);
      }
          
  }
  
  
  1.3       +3 -1      jakarta-commons-sandbox/math/src/test/org/apache/commons/math/MathTestSuite.java
  
  Index: MathTestSuite.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/math/src/test/org/apache/commons/math/MathTestSuite.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- MathTestSuite.java	15 May 2003 05:39:01 -0000	1.2
  +++ MathTestSuite.java	18 May 2003 00:58:52 -0000	1.3
  @@ -88,6 +88,8 @@
           suite.addTest(RealMatrixImplTest.suite());
           suite.addTest(FreqTest.suite());
           suite.addTest(UnivariateImplTest.suite());
  +        suite.addTest(TestStatisticTest.suite());
  +        suite.addTest(RandomDataTest.suite());
           return suite;
       }
   }
  
  
  
  1.1                  jakarta-commons-sandbox/math/src/test/org/apache/commons/math/RandomDataTest.java
  
  Index: RandomDataTest.java
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2003 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution. 
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  package org.apache.commons.math;
  
  import junit.framework.Test;
  import junit.framework.TestCase;
  import junit.framework.TestSuite;
  import junit.framework.AssertionFailedError;
  import java.security.NoSuchProviderException;
  import java.security.NoSuchAlgorithmException;
  /**
   * Test cases for the RandomData class.
   *
   * @author Phil Steitz
   * @version $Revision: 1.1 $ $Date: 2003/05/18 00:58:52 $
   */
  
  public final class RandomDataTest extends TestCase {
  
      public RandomDataTest(String name) {
          super(name);
      }
  
      private long smallSampleSize = 1000;
      private double[] expected = {250,250,250,250};
      private int largeSampleSize = 10000;
      private int tolerance = 50;
      private String[] hex = 
          {"0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"}; 
      private RandomDataImpl randomData = new RandomDataImpl(); 
      private TestStatisticImpl testStatistic = new TestStatisticImpl();
      
      
      public void setUp() { 
      }
  
      public static Test suite() {
          TestSuite suite = new TestSuite(RandomDataTest.class);
          suite.setName("RandomData Tests");
          return suite;
      }
  
      /** test dispersion and failure modes for nextInt() */
      public void testNextInt() {
          try {
              int x = randomData.nextInt(4,3);
              fail("IllegalArgumentException expected");
          } catch (IllegalArgumentException ex) {
              ;
          }
          Freq freq = new Freq();
          int value = 0;
          for (int i=0;i<smallSampleSize;i++) {
              value = randomData.nextInt(0,3);
              assertTrue("nextInt range",(value >= 0) && (value <= 3));
              freq.addValue(value);  
          }
          double[] observed = new double[4];
          for (int i=0; i<4; i++) {
              String iString = new Integer(i).toString();
              observed[i] = freq.getCount(iString);
          } 
          
          /* Use ChiSquare dist with df = 4-1 = 3, alpha = .001
           * Change to 11.34 for alpha = .01
           */
          assertTrue("chi-square test -- will fail about 1 in 1000 times",
              testStatistic.chiSquare(expected,observed) < 16.27);    
      }
      
      /** test dispersion and failure modes for nextLong() */
      public void testNextLong() {
         try {
              long x = randomData.nextLong(4,3);
              fail("IllegalArgumentException expected");
          } catch (IllegalArgumentException ex) {
              ;
          }
         Freq freq = new Freq();
         long value = 0;
          for (int i=0;i<smallSampleSize;i++) {
              value = randomData.nextLong(0,3);
              assertTrue("nextInt range",(value >= 0) && (value <= 3));
              freq.addValue(value);  
          }
          double[] observed = new double[4];
          for (int i=0; i<4; i++) {
              String iString = new Integer(i).toString();
              observed[i] = freq.getCount(iString);
          } 
          
          /* Use ChiSquare dist with df = 4-1 = 3, alpha = .001
           * Change to 11.34 for alpha = .01
           */
          assertTrue("chi-square test -- will fail about 1 in 1000 times",
              testStatistic.chiSquare(expected,observed) < 16.27);    
      }
      
      /** test dispersion and failure modes for nextSecureLong() */
      public void testNextSecureLong() {
          try {
              long x = randomData.nextSecureLong(4,3);
              fail("IllegalArgumentException expected");
          } catch (IllegalArgumentException ex) {
              ;
          }
          Freq freq = new Freq();
          long value = 0;
          for (int i=0;i<smallSampleSize;i++) {
              value = randomData.nextSecureLong(0,3);
              assertTrue("nextInt range",(value >= 0) && (value <= 3));
              freq.addValue(value);  
          }
          double[] observed = new double[4];
          for (int i=0; i<4; i++) {
              String iString = new Integer(i).toString();
              observed[i] = freq.getCount(iString);
          } 
          
          /* Use ChiSquare dist with df = 4-1 = 3, alpha = .001
           * Change to 11.34 for alpha = .01
           */
          assertTrue("chi-square test -- will fail about 1 in 1000 times",
              testStatistic.chiSquare(expected,observed) < 16.27);    
      }
      
      /** test dispersion and failure modes for nextSecureInt() */
      public void testNextSecureInt() {
          try {
              long x = randomData.nextSecureInt(4,3);
              fail("IllegalArgumentException expected");
          } catch (IllegalArgumentException ex) {
              ;
          }
          Freq freq = new Freq();
          int value = 0;
          for (int i=0;i<smallSampleSize;i++) {
              value = randomData.nextSecureInt(0,3);
              assertTrue("nextInt range",(value >= 0) && (value <= 3));
              freq.addValue(value);  
          }
          double[] observed = new double[4];
          for (int i=0; i<4; i++) {
              String iString = new Integer(i).toString();
              observed[i] = freq.getCount(iString);
          } 
          
          /* Use ChiSquare dist with df = 4-1 = 3, alpha = .001
           * Change to 11.34 for alpha = .01
           */
          assertTrue("chi-square test -- will fail about 1 in 1000 times",
              testStatistic.chiSquare(expected,observed) < 16.27);    
      }
      
      /** 
       * Make sure that empirical distribution of random Poisson(4)'s 
       * has P(X <= 5) close to actual cumulative Poisson probablity
       * and that nextPoisson fails when mean is non-positive
       * TODO: replace with statistical test, adding test stat to TestStatistic
       */
      public void testNextPoisson() {
          try {
              long x = randomData.nextPoisson(0);
              fail("zero mean -- expecting IllegalArgumentException");
          } catch (IllegalArgumentException ex) {
              ;
          }
          Freq f = new Freq();
          long v = 0;
          for (int i = 0; i<largeSampleSize; i++) {
              try {
                  f.addValue(randomData.nextPoisson(4.0d));
              } catch (Exception ex) {
                  fail(ex.getMessage());
              }
          }
          long cumFreq = f.getCount("0") + f.getCount("1") + f.getCount("2") + 
                          f.getCount("3") + f.getCount("4") + f.getCount("5");
          long sumFreq = f.getSumFreq();
          double cumPct = 
              new Double(cumFreq).doubleValue()/new Double(sumFreq).doubleValue();
          assertEquals("cum Poisson(4)",cumPct,0.7851,0.2);
          try {
              long x = randomData.nextPoisson(-1);
              fail("negative mean supplied -- IllegalArgumentException expected");
          } catch (IllegalArgumentException ex) {
              ;
          }
          try {
              long x = randomData.nextPoisson(0);
              fail("0 mean supplied -- IllegalArgumentException expected");
          } catch (IllegalArgumentException ex) {
              ;
          }
          
      }
      
      /** test dispersion and failute modes for nextHex() */
      public void testNextHex() {
          try {
              String x = randomData.nextHexString(-1);
              fail("negative length supplied -- IllegalArgumentException expected");
          } catch (IllegalArgumentException ex) {
              ;
          }
          try {
              String x = randomData.nextHexString(0);
              fail("zero length supplied -- IllegalArgumentException expected");
          } catch (IllegalArgumentException ex) {
              ;
          }
          String hexString = randomData.nextHexString(3);
          if (hexString.length() != 3) {
                  fail("incorrect length for generated string");
          }
          hexString = randomData.nextHexString(1);
          if (hexString.length() != 1) {
                  fail("incorrect length for generated string");
          }
          try {
              hexString = randomData.nextHexString(0);
              fail("zero length requested -- expecting IllegalArgumentException");
          } catch (IllegalArgumentException ex) {
              ;
          }
          if (hexString.length() != 1) {
                  fail("incorrect length for generated string");
          }      
          Freq f = new Freq();
          for (int i = 0; i < smallSampleSize; i++) {
              hexString = randomData.nextHexString(100);
              if (hexString.length() != 100) {
                  fail("incorrect length for generated string");
              }
              for (int j = 0; j < hexString.length(); j++) {
                  f.addValue(hexString.substring(j,j+1));
              }
          }
          double[] expected = new double[16];
          double[] observed = new double[16];
          for (int i = 0; i < 16; i++) {
              expected[i] = (double)smallSampleSize*100/(double)16;
              observed[i] = f.getCount(hex[i]);
          }
          /* Use ChiSquare dist with df = 16-1 = 15, alpha = .001
           * Change to 30.58 for alpha = .01
           */
          assertTrue("chi-square test -- will fail about 1 in 1000 times",
              testStatistic.chiSquare(expected,observed) < 37.70);    
      }
      
      /** test dispersion and failute modes for nextHex() */
      public void testNextSecureHex() {
          try {
              String x = randomData.nextSecureHexString(-1);
              fail("negative length -- IllegalArgumentException expected");
          } catch (IllegalArgumentException ex) {
              ;
          }
          try {
              String x = randomData.nextSecureHexString(0);
              fail("zero length -- IllegalArgumentException expected");
          } catch (IllegalArgumentException ex) {
              ;
          }
          String hexString = randomData.nextSecureHexString(3);
          if (hexString.length() != 3) {
                  fail("incorrect length for generated string");
          }
          hexString = randomData.nextSecureHexString(1);
          if (hexString.length() != 1) {
                  fail("incorrect length for generated string");
          }
          try {
              hexString = randomData.nextSecureHexString(0);
              fail("zero length requested -- expecting IllegalArgumentException");
          } catch (IllegalArgumentException ex) {
              ;
          }
          if (hexString.length() != 1) {
                  fail("incorrect length for generated string");
          }      
          Freq f = new Freq();
          for (int i = 0; i < smallSampleSize; i++) {
              hexString = randomData.nextSecureHexString(100);
              if (hexString.length() != 100) {
                  fail("incorrect length for generated string");
              }
              for (int j = 0; j < hexString.length(); j++) {
                  f.addValue(hexString.substring(j,j+1));
              }
          }
          double[] expected = new double[16];
          double[] observed = new double[16];
          for (int i = 0; i < 16; i++) {
              expected[i] = (double)smallSampleSize*100/(double)16;
              observed[i] = f.getCount(hex[i]);
          }
          /* Use ChiSquare dist with df = 16-1 = 15, alpha = .001
           * Change to 30.58 for alpha = .01
           */
          assertTrue("chi-square test -- will fail about 1 in 1000 times",
              testStatistic.chiSquare(expected,observed) < 37.70);    
      }
      
      /** test failure modes and dispersion of nextUniform() */  
      public void testNextUniform() {    
          try {
              double x = randomData.nextUniform(4,3);
              fail("IllegalArgumentException expected");
          } catch (IllegalArgumentException ex) {
              ;
          }
          try {
              double x = randomData.nextUniform(3,3);
              fail("IllegalArgumentException expected");
          } catch (IllegalArgumentException ex) {
              ;
          }
          double[] expected = new double[] {500,500};
          double[] observed = new double[] {0,0};
          double lower = -1d;
          double upper = 20d;
          double midpoint = (lower + upper)/2d;
          double result = 0;
          for (int i = 0; i < 1000; i++) {
              result = randomData.nextUniform(lower,upper);
              if ((result == lower) || (result == upper)) {
                  fail("generated value equal to an endpoint: " + result);
              } 
              if (result < midpoint) {
                  observed[0]++;
              } else {
                  observed[1]++;
              }
          }
          /* Use ChiSquare dist with df = 2-1 = 1, alpha = .001
           * Change to 6.64 for alpha = .01
           */
          assertTrue("chi-square test -- will fail about 1 in 1000 times",
              testStatistic.chiSquare(expected,observed) < 10.83);  
      }
      
      /** test failure modes and distribution of nextGaussian() */  
      public void testNextGaussian() { 
          try {
              double x = randomData.nextGaussian(0,0);
              fail("zero sigma -- IllegalArgumentException expected");
          } catch (IllegalArgumentException ex) {
              ;
          }
          Univariate u = new UnivariateImpl();
          for (int i = 0; i<largeSampleSize; i++) {
              u.addValue(randomData.nextGaussian(0,1));
          }
          double xbar = u.getMean();
          double s = u.getStandardDeviation();
          double n = u.getN(); 
          /* t-test at .001-level TODO: replace with externalized t-test, with
           * test statistic defined in TestStatistic
           */
          assertTrue(Math.abs(xbar)/(s/Math.sqrt(n))< 3.29);
      }
      
      /** test failure modes and distribution of nextExponential() */  
      public void testNextExponential() {
          try {
              double x = randomData.nextExponential(-1);
              fail("negative mean -- expecting IllegalArgumentException");
          } catch (IllegalArgumentException ex) {
              ;
          }
          assertEquals("0 mean", 0,randomData.nextExponential(0),10E-8); 
          long cumFreq = 0;
          double v = 0;
          for (int i = 0; i < largeSampleSize; i++) {
              v = randomData.nextExponential(1);
              assertTrue("exponential deviate postive", v > 0);
              if (v < 2) cumFreq++;
          }
          /* TODO: Replace with a statistical test, with statistic added to
           * TestStatistic.  Check below compares observed cumulative distribution
           * evaluated at 2 with exponential CDF 
           */
          assertEquals("exponential cumulative distribution",
              (double)cumFreq/(double)largeSampleSize,0.8646647167633873,.2);
      } 
      
      /** test reseeding, algorithm/provider games */
      public void testConfig() throws NoSuchProviderException, 
        NoSuchAlgorithmException{
          randomData.reSeed(1000);
          double v = randomData.nextUniform(0,1);
          randomData.reSeed();
          assertTrue("different seeds", 
              Math.abs(v - randomData.nextUniform(0,1)) > 10E-12);
          randomData.reSeed(1000);
          assertEquals("same seeds",v,randomData.nextUniform(0,1),10E-12);
          randomData.reSeedSecure(1000);
          String hex = randomData.nextSecureHexString(40);
          randomData.reSeedSecure();
          assertTrue("different seeds",
              !hex.equals(randomData.nextSecureHexString(40)));
          randomData.reSeedSecure(1000);
          assertTrue("same seeds",
              !hex.equals(randomData.nextSecureHexString(40))); 
          
          /* TODO: probably should remove this test as the package grows,
           * since it takes about 4 seconds
           */
          randomData.setSecureAlgorithm("SHA1PRNG","SUN");
          assertTrue("different seeds",
              !hex.equals(randomData.nextSecureHexString(40)));
          try {
              randomData.setSecureAlgorithm("NOSUCHTHING","SUN");
              fail("expecting NoSuchAlgorithmException");
          } catch (NoSuchAlgorithmException ex) {
              ;
          }
          
          try {
              randomData.setSecureAlgorithm("SHA1PRNG","NOSUCHPROVIDER");
              fail("expecting NoSuchProviderException");
          } catch (NoSuchProviderException ex) {
              ;
          }      
      }
  }
  
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org