docs: public static double entropy(double[] vector) {
          if (vector == null || vector.length < 1)
              return 0;
          else {
              double ret = 0;
              for (double d : vector)
                  ret += d * Math.log(d);
              return ret;
  
          }
      }//end entropy
  
      /**
       * This returns the kronecker delta of two doubles.
       * @param i the first number to compare
       * @param j the second number to compare
       * @return 1 if they are equal, 0 otherwise
       */
      public static int kroneckerDelta(double i, double j) {
          return (i == j) ? 1 : 0;
      }
  
      /**
       * This calculates the adjusted r^2 including degrees of freedom.
       * Also known as calculating "strength" of a regression
       * @param rSquared the r squared value to calculate
       * @param numRegressors number of variables
       * @param numDataPoints size of the data applyTransformToDestination
       * @return an adjusted r^2 for degrees of freedom
       */
      public static double adjustedrSquared(double rSquared, int numRegressors, int numDataPoints) {
          double divide = (numDataPoints - 1.0) / (numDataPoints - numRegressors - 1.0);
          double rSquaredDiff = 1 - rSquared;
          return 1 - (rSquaredDiff * divide);
      }
  
  
      public static double[] normalizeToOne(double[] doubles) {
          normalize(doubles, sum(doubles));
          return doubles;
      }
  
      public static double min(double[] doubles) {
          double ret = doubles[0];
          for (double d : doubles)
              if (d < ret)
                  ret = d;
          return ret;
      }
  
      public static double max(double[] doubles) {
          double ret = doubles[0];
          for (double d : doubles)
              if (d > ret)
                  ret = d;
          return ret;
      }
  
      /**
       * Normalizes the doubles in the array using the given value.
       *
       * @param doubles the array of double
       * @param sum the value by which the doubles are to be normalized
       * @exception IllegalArgumentException if sum is zero or NaN
       */
      public static void normalize(double[] doubles, double sum) {
  
          if (Double.isNaN(sum)) {
              throw new IllegalArgumentException("Can't normalize array. Sum is NaN.");
          }
          if (sum == 0) {
              // Maybe this should just be a return.
              throw new IllegalArgumentException("Can't normalize array. Sum is zero.");
          }
          for (int i = 0; i < doubles.length; i++) {
              doubles[i] /= sum;
          }
      }//end normalize
  
      /**
       * Converts an array containing the natural logarithms of
       * probabilities stored in a vector back into probabilities.
       * The probabilities are assumed to sum to one.
       *
       * @param a an array holding the natural logarithms of the probabilities
       * @return the converted array
       */
      public static double[] logs2probs(double[] a) {
  
          double max = a[maxIndex(a)];
          double sum = 0.0;
  
          double[] result = new double[a.length];
          for (int i = 0; i < a.length; i++) {
              result[i] = Math.exp(a[i] - max);
              sum += result[i];
          }
  
          normalize(result, sum);
  
          return result;
      }//end logs2probs
  
      /**
       * This returns the entropy for a given vector of probabilities.
       * @param probabilities the probabilities to getFromOrigin the entropy for
       * @return the entropy of the given probabilities.
       */
      public static double information(double[] probabilities) {
          double total = 0.0;
          for (double d : probabilities) {
              total += (-1.0 * log2(d) * d);
          }
          return total;
      }//end information
  
      /**
       *
       *
       * Returns index of maximum element in a given
       * array of doubles. First maximum is returned.
       *
       * @param doubles the array of doubles
       * @return the index of the maximum element
       */
      public static /*@pure@*/ int maxIndex(double[] doubles) {
  
          double maximum = 0;
          int maxIndex = 0;
  
          for (int i = 0; i < doubles.length; i++) {
              if ((i == 0) || (doubles[i] > maximum)) {
                  maxIndex = i;
                  maximum = doubles[i];
              }
          }
  
          return maxIndex;
      }//end maxIndex
  
      /**
       * This will return the factorial of the given number n.
       * @param n the number to getFromOrigin the factorial for
       * @return the factorial for this number
       */
      public static double factorial(double n) {
          if (n == 1 || n == 0)
              return 1;
          for (double i = n; i > 0; i--, n *= (i > 0 ? i : 1)) {
          }
          return n;
      }//end factorial
  
  
  
      /** The small deviation allowed in double comparisons. */
      public static double SMALL = 1e-6;
  
      /**
       * Returns the log-odds for a given probability.
       *
       * @param prob the probability
       *
       * @return the log-odds after the probability has been mapped to
       * [Utils.SMALL, 1-Utils.SMALL]
       */
      public static /*@pure@*/ double probToLogOdds(double prob) {
  
          if (gr(prob, 1) || (sm(prob, 0))) {
              throw new IllegalArgumentException("probToLogOdds: probability must " + "be in [0,1] " + prob);
          }
          double p = SMALL + (1.0 - 2 * SMALL) * prob;
          return Math.log(p / (1 - p));
      }
  
      /**
       * Rounds a double to the next nearest integer value. The JDK version
       * of it doesn't work properly.
       *
       * @param value the double value
       * @return the resulting integer value
       */
      public static /*@pure@*/ int round(double value) {
  
          return value > 0 ? (int) (value + 0.5) : -(int) (Math.abs(value) + 0.5);
      }//end round
  
      /**
       * This returns the permutation of n choose r.
       * @param n the n to choose
       * @param r the number of elements to choose
       * @return the permutation of these numbers
       */
      public static double permutation(double n, double r) {
          double nFac = MathUtils.factorial(n);
          double nMinusRFac = MathUtils.factorial((n - r));
          return nFac / nMinusRFac;
      }//end permutation
  
  
      /**
       * This returns the combination of n choose r
       * @param n the number of elements overall
       * @param r the number of elements to choose
       * @return the amount of possible combinations for this applyTransformToDestination of elements
       */
      public static double combination(double n, double r) {
          double nFac = MathUtils.factorial(n);
          double rFac = MathUtils.factorial(r);
          double nMinusRFac = MathUtils.factorial((n - r));
  
          return nFac / (rFac * nMinusRFac);
      }//end combination
  
  
      /**
       * sqrt(a^2 + b^2) without under/overflow.
       */
      public static double hypotenuse(double a, double b) {
          double r;
          if (Math.abs(a) > Math.abs(b)) {
              r = b / a;
              r = Math.abs(a) * Math.sqrt(1 + r * r);
          } else if (b != 0) {
              r = a / b;
              r = Math.abs(b) * Math.sqrt(1 + r * r);
          } else {
              r = 0.0;
          }
          return r;
      }//end hypotenuse
  
      /**
       * Rounds a double to the next nearest integer value in a probabilistic
       * fashion (e.g. 0.8 has a 20% chance of being rounded down to 0 and a
       * 80% chance of being rounded up to 1). In the limit, the average of
       * the rounded numbers generated by this procedure should converge to
       * the original double.
       *
       * @param value the double value
       * @param rand the random number generator
       * @return the resulting integer value
       */
      public static int probRound(double value, Random rand) {
  
          if (value >= 0) {
              double lower = Math.floor(value);
              double prob = value - lower;
              if (rand.nextDouble() < prob) {
                  return (int) lower + 1;
              } else {
                  return (int) lower;
              }
          } else {
              double lower = Math.floor(Math.abs(value));
              double prob = Math.abs(value) - lower;
              if (rand.nextDouble() < prob) {
                  return -((int) lower + 1);
              } else {
                  return -(int) lower;
              }
          }
      }//end probRound
  
      /**
       * Rounds a double to the given number of decimal places.
       *
       * @param value the double value
       * @param afterDecimalPoint the number of digits after the decimal point
       * @return the double rounded to the given precision
       */
      public static /*@pure@*/ double roundDouble(double value, int afterDecimalPoint) {
  
          double mask = Math.pow(10.0, (double) afterDecimalPoint);
  
          return (double) (Math.round(value * mask)) / mask;
      }//end roundDouble
  
  
  
      /**
       * Rounds a double to the given number of decimal places.
       *
       * @param value the double value
       * @param afterDecimalPoint the number of digits after the decimal point
       * @return the double rounded to the given precision
       */
      public static /*@pure@*/ float roundFloat(float value, int afterDecimalPoint) {
  
          float mask = (float) Math.pow(10, (float) afterDecimalPoint);
  
          return (float) (Math.round(value * mask)) / mask;
      }//end roundDouble
  
      /**
       * This will return the bernoulli trial for the given event.
       * A bernoulli trial is a mechanism for detecting the probability
       * of a given event occurring k times in n independent trials
       * @param n the number of trials
       * @param k the number of times the target event occurs
       * @param successProb the probability of the event happening
       * @return the probability of the given event occurring k times.
       */
      public static double bernoullis(double n, double k, double successProb) {
  
          double combo = MathUtils.combination(n, k);
          double q = 1 - successProb;
          return combo * Math.pow(successProb, k) * Math.pow(q, n - k);
      }//end bernoullis
  
      /**
       * Tests if a is smaller than b.
       *
       * @param a a double
       * @param b a double
       */
      public static /*@pure@*/ boolean sm(double a, double b) {
  
          return (b - a > SMALL);
      }
  
      /**
       * Tests if a is greater than b.
       *
       * @param a a double
       * @param b a double
       */
      public static /*@pure@*/ boolean gr(double a, double b) {
  
          return (a - b > SMALL);
      }
  
      /**
       * This will take a given string and separator and convert it to an equivalent
       * double array.
       * @param data the data to separate
       * @param separator the separator to use
       * @return the new double array based on the given data
       */
      public static double[] fromString(String data, String separator) {
          String[] split = data.split(separator);
          double[] ret = new double[split.length];
          for (int i = 0; i < split.length; i++) {
              ret[i] = Double.parseDouble(split[i]);
          }
          return ret;
      }//end fromString
  
      /**
       * Computes the mean for an array of doubles.
       *
       * @param vector the array
       * @return the mean
       */
      public static /*@pure@*/ double mean(double[] vector) {
  
          double sum = 0;
  
          if (vector.length == 0) {
              return 0;
          }
          for (int i = 0; i < vector.length; i++) {
              sum += vector[i];
          }
          return sum / (double) vector.length;
      }//end mean
  
      /**
       * This will return the cholesky decomposition of
       * the given matrix
       * @param m the matrix to convert
       * @return the cholesky decomposition of the given
       * matrix.
       * See:
       * http://en.wikipedia.org/wiki/Cholesky_decomposition
       * @throws NonSquareMatrixException
       */
      public CholeskyDecomposition choleskyFromMatrix(RealMatrix m) throws Exception {
          return new CholeskyDecomposition(m);
      }//end choleskyFromMatrix
  
  
  
      /**
       * This will convert the given binary string to a decimal based
       * integer
       * @param binary the binary string to convert
       * @return an equivalent base 10 number
       */
      public static int toDecimal(String binary) {
          long num = Long.parseLong(binary);
          long rem;
          /* Use the remainder method to ensure validity */
          while (num > 0) {
              rem = num % 10;
              num = num / 10;
              if (rem != 0 && rem != 1) {
                  System.out.println("This is not a binary number.");
                  System.out.println("Please try once again.");
                  return -1;
              }
          }
          return Integer.parseInt(binary, 2);
      }//end toDecimal
  
  
      /**
       * This will translate a vector in to an equivalent integer
       * @param vector the vector to translate
       * @return a z value such that the value is the interleaved lsd to msd for each
       * double in the vector
       */
      public static int distanceFinderZValue(double[] vector) {
          StringBuilder binaryBuffer = new StringBuilder();
          List<String> binaryReps = new ArrayList<>(vector.length);
          for (int i = 0; i < vector.length; i++) {
              double d = vector[i];
              int j = (int) d;
              String binary = Integer.toBinaryString(j);
              binaryReps.add(binary);
          }
          //append from left to right, the least to the most significant bit
          //till all strings are empty
          while (!binaryReps.isEmpty()) {
              for (int j = 0; j < binaryReps.size(); j++) {
                  String curr = binaryReps.get(j);
                  if (!curr.isEmpty()) {
                      char first = curr.charAt(0);
                      binaryBuffer.append(first);
                      curr = curr.substring(1);
                      binaryReps.set(j, curr);
                  } else
                      binaryReps.remove(j);
              }
          }
          return Integer.parseInt(binaryBuffer.toString(), 2);
  
      }//end distanceFinderZValue
  
      /**
       * This returns the distance of two vectors
       * sum(i=1,n)   (q_i - p_i)^2
       * @param p the first vector
       * @param q the second vector
       * @return the distance between two vectors
       */
      public static double euclideanDistance(double[] p, double[] q) {
  
          double ret = 0;
          for (int i = 0; i < p.length; i++) {
              double diff = (q[i] - p[i]);
              double sq = Math.pow(diff, 2);
              ret += sq;
          }
          return ret;
  
      }//end euclideanDistance
  
      /**
       * This returns the distance of two vectors
       * sum(i=1,n)   (q_i - p_i)^2
       * @param p the first vector
       * @param q the second vector
       * @return the distance between two vectors
       */
      public static double euclideanDistance(float[] p, float[] q) {
  
          double ret = 0;
          for (int i = 0; i < p.length; i++) {
              double diff = (q[i] - p[i]);
              double sq = Math.pow(diff, 2);
              ret += sq;
          }
          return ret;
  
      }//end euclideanDistance
  
      /**
       * This will generate a series of uniformally distributed
       * numbers between l times
       * @param l the number of numbers to generate
       * @return l uniformally generated numbers
       */
      public static double[] generateUniform(int l) {
          double[] ret = new double[l];
          Random rgen = new Random();
          for (int i = 0; i < l; i++) {
              ret[i] = rgen.nextDouble();
          }
          return ret;
      }//end generateUniform
  
  
      /**
       * This will calculate the Manhattan distance between two sets of points.
       * The Manhattan distance is equivalent to:
       * 1_sum_n |p_i - q_i|
       * @param p the first point vector
       * @param q the second point vector
       * @return the Manhattan distance between two object
       */
      public static double manhattanDistance(double[] p, double[] q) {
  
          double ret = 0;
          for (int i = 0; i < p.length; i++) {
              double difference = p[i] - q[i];
              ret += Math.abs(difference);
          }
          return ret;
      }//end manhattanDistance
  
  
  
      public static double[] sampleDoublesInInterval(double[][] doubles, int l) {
          double[] sample = new double[l];
          for (int i = 0; i < l; i++) {
              int rand1 = randomNumberBetween(0, doubles.length - 1);
              int rand2 = randomNumberBetween(0, doubles[i].length);
              sample[i] = doubles[rand1][rand2];
          }
  
          return sample;
      }
  
      /**
       * Generates a random integer between the specified numbers
       * @param begin the begin of the interval
       * @param end the end of the interval
       * @return an int between begin and end
       */
      public static int randomNumberBetween(double begin, double end) {
          if (begin > end)
              throw new IllegalArgumentException("Begin must not be less than end");
          return (int) begin + (int) (Math.random() * ((end - begin) + 1));
      }
  
      /**
       * Generates a random integer between the specified numbers
       * @param begin the begin of the interval
       * @param end the end of the interval
       * @return an int between begin and end
       */
      public static int randomNumberBetween(double begin, double end, RandomGenerator rng) {
          if (begin > end)
              throw new IllegalArgumentException("Begin must not be less than end");
          return (int) begin + (int) (rng.nextDouble() * ((end - begin) + 1));
      }
  
      /**
       * Generates a random integer between the specified numbers
       * @param begin the begin of the interval
       * @param end the end of the interval
       * @return an int between begin and end
       */
      public static int randomNumberBetween(double begin, double end, org.nd4j.linalg.api.rng.Random rng) {
          if (begin > end)
              throw new IllegalArgumentException("Begin must not be less than end");
          return (int) begin + (int) (rng.nextDouble() * ((end - begin) + 1));
      }
  
      /**
       *
       * @param begin
       * @param end
       * @return
       */
      public static float randomFloatBetween(float begin, float end) {
          float rand = (float) Math.random();
          return begin + (rand * ((end - begin)));
      }
  
      public static double randomDoubleBetween(double begin, double end) {
          return begin + (Math.random() * ((end - begin)));
      }
  
      public static void shuffleArray(int[] array, long rngSeed) {
          shuffleArray(array, new Random(rngSeed));
      }
  
      public static void shuffleArray(int[] array, Random rng) {
          //https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm
          for (int i = array.length - 1; i > 0; i--) {
              int j = rng.nextInt(i + 1);
              int temp = array[j];
              array[j] = array[i];
              array[i] = temp;
          }
      }
  }