Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*
    * (C) Copyright 2013 DataGenerator Contributors.
    *
    * Licensed under the Apache License, Version 2.0 (the "License");
    * you may not use this file except in compliance with the License.
    * You may obtain a copy of the License at
    *
    *     http://www.apache.org/licenses/LICENSE-2.0
    *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   *
   */
  package org.finra.datagenerator.generation;
  
  import java.io.File;
  import java.util.Arrays;
  import java.util.List;
  import java.util.Random;
This class can take in a specification of the number of possible choices at each point in an array. It then produces a selection of integer arrays, such that for each choice of two points in in the first array, some array in the selection has any possible pair of values at those two points. In other words, we cover every pair of combinations.
  
  public class UnsynchronizedAllPairs {
  
      private static final Logger log = Logger.getLogger(UnsynchronizedAllPairs.class);
    
prime power to use for prime-based construction
  
      private int primePower;
    
primePower = basePrime ^ power
  
      private int basePrime;
      private int power;
    
The prime-based construction constructs its solution using blocks with p+1 columns and p*p rows. p+1 may be smaller than the number of columns we have to fill up. In this case, we number each column base p+1. This is the number of digits required for this numbering: we will end up with at most primeDigits * p * p rows.
  
      private int primeDigits;
    
maximum number of columns with this prime and repeats
  
      private int maxColumns;
    
block from prime-based solution
  
      private int[][] primeBlock;
    
Copy of input giving number of choices at each stage
  
      private int[] numChoices;
    
used to make loads of decisions at random: this stuff mostly works by doing its best following random guesses so that we can make loads of attempts and pick the best
  
      private Random r;
    
Keeps track of the number of combinations accounted for so far
  
      private Used used;
    
Used to keep track of the number of times each combination has been used, as a check, and in the prime-based strategy. Indexed by a, a value, b, bvalue (yes this is redundant but I want to keep this bit simple).
  
      private int[][][][] counts;
    
Seed value. Reseeded in generate to make it easier to reproduce any bugs
  
      private long mySeed;
    
set this to control whether the prime-based method shuffles
  
      private boolean shuffle;
    
List of small primes. We can detect prime powers of these
  
      private static int smallPrimes[] = new int[]{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};

    
Construct given number of choices at each point and the starting random number seed. This does some one-time work
  
      public UnsynchronizedAllPairs(int[] choiceslong seedboolean shouldShuffle) {
          // Be paranoid about user fiddling with array after calling
          // constructor
           = choices.clone();
           = seed;
          = shouldShuffle;
         if (.<=1) { // silly number of columns, which might cause us to blow up
             // later
             return;
         }
          = new Used();
          = new int[.][][][];
         for(int i = 0; i<.i++){
             [i] = new int[[i]][][];
             for(int iVal = 0; iVal<[i]; iVal++){
                 [i][iVal] = new int[.][];
                 for(int j = 0; j<.j++){
                     [i][iVal][j] = new int[[j]];
                 }
             }
         }
 		// Now work out a prime or prime power to use for prime-number based
         // construction. Given a prime, or prime power, p, the construction
         // produces p^2 rows and p+1 columns. The first column is p 0s,
         // then p 1s, then p 2s..
         // and so on. The second column is 0,1,2,3..p-1,0,1,2,...
         // The ith column is the first column plus (i-1) times the second,
         // (mod p, or using Galois arithmetic if p is a prime power). If we
         // specify the values in the same row in any two different columns
         // we can work out the values in the same row for the first two
         // columns by solving a simultaneous linear equation in two
         // unknowns. This set of equations has a non-zero determinant and
         // we are working in a field so it has precisely one solution,
         // which means that each pair of values occurs in precisely one
         // row for each pair of columns.
 
         // We can extend the number of columns of this by using more
         // than one copy of each column, below each other. Write the
         // index of each column down base (p+1) and choose copies of
         // the original prime-based column according to the digits of
         // that column's index in this base. Any two columns have
         // different numbers and so will differ in at least one digit
         // of this representation, and therefore will have a set of
         // pairs produced by one of the columns from the basic
         // construction
         // First of all find a lower bound on p
          = 2;
         for(int i = 0; i<.i++){
             if ([i]>) {
                  = [i];
             }
         }
         // search here for a prime, or a power of a small prime
         ploop:
         for(;;){
             for(int p : ){
                 if (==p) { // is a prime
                      = p;
                      = 1;
                     break ploop;
                 }
                 // see if primePower is a power of p
                  = 1;
                 for(int reduced = ;;){
                     if ((reduced%p)!=0) {
                         if (>1) { // divisible by a small prime but not a power
                             // what's more, we know it is not a power of two,
                             // since we test for that first
                             if ((&1)==0) {
                                 ++;
                             } else {
                                  += 2;
                             }
                             continue ploop;
                         }
                         // here if not divisible by p
                         break;
                     }
                     reduced = reduced/p;
                     // now reduced = primePower / p^power
                     if (reduced==1) { // primePower = p ^ power
                          = p;
                         break ploop;
                     }
                     ++;
                 }
             }
             for(int i = [.-1]+2;; i += 2){
                 if (i*i>) { // primePower is a large prime
                      = ;
                      = 1;
                     break ploop;
                 }
                 if ((%i)==0) { // primePower is not a prime
                     // note that even if primePower = i * i, that does us no
                     // good, as i probably isn't prime
                     break;
                 }
             }
             if ((&1)==0) {
                 ++;
             } else {
                  += 2;
             }
         }
          = +1;
         // now build basic block of prime-based solution
          = new int[*][];
         if (==1) {
             for(int i = 0; i<.i++){
                 int[] row = new int[];
                 [i] = row;
                 int a = i/;
                 int b = i%;
                 row[0] = a;
                 row[1] = b;
                 for(int j = 2; j<j++){
                     row[j] = (a+b*(j-1))%;
                 }
             }
         } else {
             // Need to do arithmetic over a Galois field. For the most
             // part we can think of this as working with polynomials mod
             // some polynomial, where the other polynomial happens to
             // be chosen such that this is a field, not a ring (that is,
             // so that every non-zero element has an inverse). Galois theory
             // is only necessary to guarantee that such a polynomial exists,
             // and that one exists such that 1, x, x^2, x^3... produces
             // all non-zero polynomials. We search for one such here, with
             // the highest non-zero power of x having coefficient 1.
             int[] magicPoly = new int[];
             int[] trial = new int[];
             int forcePositive = *;
             int exhaust = 1;
             exhaustLoop:
             for(; exhaust<exhaust++){
                 int val = exhaust;
                 for(int i = 0; i<i++){
                     magicPoly[i] = val%;
                     val = val/;
                 }
                 // start off with 1
                 for(int i = 0; i<trial.lengthi++){
                     trial[i] = 0;
                 }
                 trial[0] = 1;
                 iLoop:
                 for(int i = 0;; i++){
                     // multiply by x.
                     int top = trial[trial.length-1];
                     for(int j = trial.length-1; j>0; j--){
                         trial[j] = trial[j-1];
                     }
                     trial[0] = 0;
                     // top.x^power =  - top * magicPoly
                     for(int j = 0; j<trial.lengthj++){
                         int coeff = trial[j]-top*magicPoly[j]+forcePositive;
                         trial[j] = coeff%;
                     }
                     if (i==(-2)) { // should have cycled round to 1 again
                         for(int j = 1; j<trial.lengthj++){
                             if (trial[j]!=0) {
                                 continue exhaustLoop;
                             }
                         }
                         if (trial[0]==1) { // success!
                             break exhaustLoop;
                         }
                         // failure
                         continue exhaustLoop;
                     }
                     // Check for early failure cases: 1 or 0
                     for(int j = 1; j<trial.lengthj++){
                         if (trial[j]!=0) {
                             continue iLoop;
                         }
                     }
                     if ((trial[0]==1)||(trial[0]==0)) { // early failure
                         continue exhaustLoop;
                     }
                 }
             }
             if (exhaust==) {
                 throw new IllegalStateException(
                         "Could not find good polynomial");
             }
             /*
              System.err.println("Exhaust is " + exhaust);
              for (int i = 0; i < magicPoly.length; i++)
              {
              System.err.println("Magic " + magicPoly[i]);
              }
              */
              = new int[*][];
             for(int i = 0; i<.i++){
                 [i] = new int[];
             }
             int[] zeroColumn = new int[];
             int[] oneColumn = new int[];
             // System.err.println("basePrime = " + basePrime);
             for(int i = 0; i<.i++){
                 // fill in the first two columns with polynomials written
                 // down as numbers
                 int z = i/;
                 [i][0] = z;
                 int o = i%;
                 [i][1] = o;
                 // System.err.println("i = " + i + " z = " + z + " o = " + o);
                 // Split first two columns out as polynomials
                 for(int j = 0; j<j++){
                     zeroColumn[j] = z%;
                     z = z/;
                     oneColumn[j] = o%;
                     o = o/;
                 }
                 // To work out the rest of the row, we continually
                 // add oneColumn to zeroColumn to produce the new entry,
                 // then multiply oneColumn by x. So each column is
                 // z + x^k * o, for some value of k, and we already know
                 // that x^k cycles round
                 int last = -1;
                 for(int j = 2;; j++){
                     int v = 0;
                     int intPower = 1;
                     for(int k = 0; k<k++){
                         int x = (zeroColumn[k]+oneColumn[k])%;
                         v = x*intPower+v;
                         intPower = intPower*;
                     }
                     [i][j] = v;
                     if (j==last) {
                         break;
                     }
                     int top = oneColumn[oneColumn.length-1];
                     for(int k = oneColumn.length-1; k>0; k--){
                         oneColumn[k] = oneColumn[k-1];
                     }
                     oneColumn[0] = 0;
                     // top.x^power =  - top * magicPoly
                     for(int k = 0; k<oneColumn.lengthk++){
                         int coeff = oneColumn[k]-top*magicPoly[k]
                                 +forcePositive;
                         oneColumn[k] = coeff%;
                     }
                 }
             }
         }
          = 1;
         while (<.){
             ++;
              *= (+1);
         }
          = 0;
         for(int i = 0; i<.i++){
              += [i];
         }
         // back to setting up stuff for greedy algorithm
         // Will keep track of combination of position and value
         // to occur, together with the number of other position-value
         // combinations it has yet to meet, ordered by that number
          = new ArrayList[-1];
         for(int i = 0; i<.i++){
             [i] = new ArrayList<Combination>();
         }
         // Combination indexed by position and value
         for(int i = 0; i<.i++){
             Combination[] arr = new Combination[[i]];
             [i] = arr;
             for(int j = 0; j<arr.lengthj++){
                 Combination c = new Combination();
                 c.position = i;
                 c.value = j;
                 c.numLeft = -[i];
                 arr[j] = c;
                 c.positionInArray = [0].size();
                 [0].add(c);
             }
         }
     }

    
This class keeps track of whether particular pairs of position-value combinations have been used yet
 
     private class Used {

        
total number of pairs required
 
         private int numPossible = 0;

        
construct from array giving number of choices at each point
 
         Used() {
             // Build up array and count number of distinct pairs. Each
             // pair of positions appears once as i,j and once as j,i.
             // Here we make use of that by storing it only in the order
             // in which i < j, sizing the arrays to suit.
             int n1 = .-1;
              = new boolean[n1][][];
             for(int i = 0; i<n1i++){
                 int lenHere = .-1-i;
                 [i] = new boolean[lenHere][];
                 for(int j = 0; j<lenHerej++){
                     [i][j] = new boolean[[i]
                             *[i+j+1]];
                      += [i][j].length;
                 }
             }
         }
 
         // test used only during debugging
         boolean isAllUsed() {
             // System.err.println("Possible " + numPossible +
             //   " used " + numUsed);
             for(int i = 0; i<.i++){
                 boolean[][] ui = [i];
                 for(int j = 0; j<ui.lengthj++){
                     boolean[] uj = ui[j];
                     for(int k = 0; k<uj.lengthk++){
                         if (!uj[k]) {
                             return false;
                         }
                     }
                 }
             }
             return true;
         }

        
clear all used flags
 
         void reset() {
             for(int i = 0; i<.i++){
                 for(int j = 0; j<[i].lengthj++){
                     Arrays.fill([i][j], false);
                 }
             }
              = 0;
         }

        
return the number of possible slots
 
         int getNumPossible() {
             return ;
         }

        
return the number of used slots
 
         int getNumUsed() {
             return ;
         }
        
Triangular array mapping to boolean array of used or not. We demand that the first index is less than the second, which means that we don't need to allocate n^2 values. For 3 choices we actually store just 0,1 0,2 1,2
 
         private boolean[][][] isUsed;
        
number of combinations used
 
         private int numUsed = 0;

        
mark the combination of aVal at a, bVal at b as used, returning the previous value.
 
         boolean setUsed(int aint aValint bint bVal) {
             if (a==b) {
                 ..println("Seed "+);
                 throw new IllegalArgumentException("a == b");
             }
             if (a>b) {
                 int t = a;
                 a = b;
                 b = t;
                 t = aVal;
                 aVal = bVal;
                 bVal = t;
             }
             int index = aVal*[b]+bVal;
             int ba = b-a-1;
             boolean previous = [a][ba][index];
             if (!previous) {
                 ++;
                 [a][ba][index] = true;
             }
             return previous;
         }

        
return whether the combination of aVal at a, bVal at b is used
 
         boolean isCombinationUsed(int aint aValint bint bVal) {
             if (a==b) {
                 ..println("Seed "+);
                 throw new IllegalArgumentException("a == b");
             }
             if (a>b) {
                 int t = a;
                 a = b;
                 b = t;
                 t = aVal;
                 aVal = bVal;
                 bVal = t;
             }
             int index = aVal*[b]+bVal;
             int ba = b-a-1;
             return [a][ba][index];
         }
     }

    
Fill in the counts array and return the minimum value of any count, given a design. This is used both for checking designs and for removing redundant rows from prime-based designs.
 
     private int minCount(int[][] design) {
         for(int i = 0; i<.i++){
             int[][][] c1 = [i];
             for(int iVal = 0; iVal<c1.lengthiVal++){
                 int[][] c2 = c1[iVal];
                 for(int j = 0; j<c2.lengthj++){
                     Arrays.fill(c2[j], 0);
                 }
             }
         }
         for(int d = 0; d<design.lengthd++){
             int[] row = design[d];
             for(int i = 0; i<row.lengthi++){
                 for(int j = 0; j<row.lengthj++){
                     if (i==j) {
                         continue;
                     }
                     [i][row[i]][j][row[j]]++;
                 }
             }
         }
         int min = .;
         for(int i = 0; i<.i++){
             int[][][] c1 = [i];
             for(int iVal = 0; iVal<c1.lengthiVal++){
                 int[][] c2 = c1[iVal];
                 for(int j = 0; j<c2.lengthj++){
                     if (i==j) {
                         continue;
                     }
                     int[] c3 = c2[j];
                     for(int k = 0; k<c3.lengthk++){
                         int x = c3[k];
                         if (min>x) {
                             min = x;
                         }
                     }
                 }
             }
         }
         return min;
     }

    
get String rep
 
     public static String[][] getStringResult(int[][] resultTranslator tran) {
         String[][] ret = new String[result.length][result[0].length];
 
         for(int i = 0; i<result.lengthi++){
             for(int j = 0; j<result[i].lengthj++){
                 String toPrint;
                 if (tran==null) {
                     toPrint = Integer.toString(result[i][j]);
                 } else {
                     toPrint = tran.translate(jresult[i][j]);
                 }
                 ret[i][j] = toPrint;
             }
         }
         return ret;
 
     }

    
print out the design
 
     public static void showResult(int[][] resultTranslator tranString dir) {
         StringBuffer sb = new StringBuffer("\n");
         for(int i = 0; i<result.lengthi++){
             for(int j = 0; j<result[i].lengthj++){
                 //if (j != 0)
                 //{
                 sb.append('|');
                 //}
                 String toPrint;
                 if (tran==null) {
                     toPrint = Integer.toString(result[i][j]);
                 } else {
                     toPrint = tran.translate(jresult[i][j]);
                 }
                 sb.append(toPrint);
             }
             sb.append("|\n");
         }
         sb.append("SIZE:"+result.length);
 
         .debug(sb.toString());
         //FileWriterUsingLog4jLogger.write("PDLogging", dir, "pw_combos.txt", sb.toString(), false);
 
     }

    
Generate and print out stuff for ever, or until max goes
 
     public static int[][] indefiniteGenerate(
             int[] choiceslong seedint maxboolean shuffle,
             Translator tranString dir) {
         int bestSofar = .;
         UnsynchronizedAllPairs ap = new UnsynchronizedAllPairs(choicesseedshuffle);
         for(int go = 0;; go++){
             int[][] result;
             switch (go%2) {
                 // Want to do the prime-based generation first as it may
                 // be pretty if shuffling is turned off
                 case 0:
                     result = ap.generateViaPrime(dir);
                     break;
                 case 1:
                     result = ap.generateGreedy();
                     break;
                 default:
                     throw new IllegalStateException("Bad case");
             }
             /*
              if (!ap.used.isAllUsed())
              {
              System.err.println("Trouble seen by used:");
              showResult(result);
              System.err.println("Seed " + ap.mySeed);
              throw new IllegalStateException("Generated bad result");
              }
              */
             if (ap.minCount(result)<1) {
                 ..println("Trouble:");
                 showResult(resultnulldir);
                 ..println("Seed "+ap.mySeed);
                 throw new IllegalStateException("Generated bad result");
             }
             if (result.length<bestSofar) {
                 bestSofar = result.length;
                 .debug("New best of "+bestSofar+" rows at go "
                         +go);
                 .debug("Data Varaitions:"+bestSofar);
                 showResult(resulttrandir);
                 .debug("New best was "
                         +bestSofar+" rows at go "+go);
             }
             //if ((max > 0) && (go >= max))
             if ((go+1)>=max) {
                 return result;
             }
         }
     }

    
Work out the value in a cell for a prime-based table of size primePower*primePower rows and primePower+1 columns, stacked on top of each other primeDigits times.
 
     private int getCell(int rowint col) {
         int p2 = *;
         if ((row<0)||(row>=p2*)
                 ||(col<0)||(col>)) {
             throw new IllegalArgumentException("Outside table");
         }
         int digit = row/p2;
         row = row%p2;
         int base = +1;
         for(int i = 0; i<digiti++){
             col = col/base;
         }
         col = col%base;
         return [row][col];
     }

    
return an array of numbers 0..num-1 in random order, assuming that shuffle is true
 
     private int[] randomOrder(int num) {
         int[] result = new int[num];
         for(int i = 0; i<numi++){
             result[i] = i;
         }
         if (!) {
             return result;
         }
         for(int i = 0; i<numi++){
             // Choose at random from amongst integers not yet chosen
             int from = .nextInt(num-i)+i;
             int t = result[i];
             result[i] = result[from];
             result[from] = t;
         }
         return result;
     }
 
     public int[][] generateViaPrime(String dir) {
          = new Random(++);
         int[][] first = new int[**][];
         for(int i = 0; i<first.lengthi++){
             first[i] = new int[.];
         }
         int[] rowShuffle = randomOrder(first.length);
         int[] colShuffle = randomOrder();
         for(int i = 0; i<.i++){
             int past = [i];
             int col = colShuffle[i];
             for(int j = 0; j<first.lengthj++){
                 int x = getCell(rowShuffle[j], col);
                 if (x>=past) { // outside range of choices at this position
                     x = .nextInt(past);
                 }
                 first[j][i] = x;
             }
         }
         int min = minCount(first);
         if (min<1) {
             ..println("MySeed is "+);
             showResult(firstnulldir);
             throw new IllegalStateException("Prime construction failed");
         }
         // see if we can discard any rows, by looking to see what
         // removing them would do to the counts
         int wp = 0;
         for(int i = 0; i<first.lengthi++){
             // look to see if we really need this row
             int[] thisRow = first[i];
             int j = 0;
             // For each pair of columns, see what the count would be
             // if we skipped this row
             checkCounts:
             for(; j<.j++){
                 for(int k = 0; k<.k++){
                     if (j==k) {
                         continue;
                     }
                     int x = [j][thisRow[j]][k][thisRow[k]];
                     if (x<=1) { // must have the row to stop this count going non-zero
                         break checkCounts;
                     }
                 }
             }
             if (j!=.) { // needed this row as didn't scan right to the end to find
                 // a reason to have it
                 for(int k = 0; k<.k++){
                     first[wp][k] = first[i][k];
                 }
                 wp++;
             } else { // can delete the row so ammend counts
                 for(j = 0; j<.j++){
                     for(int k = 0; k<.k++){
                         if (j==k) {
                             continue;
                         }
                         [j][thisRow[j]][k][thisRow[k]]--;
                     }
                 }
             }
         }
         if (wp==first.length) {
             return first;
         }
         int[][] result = new int[wp][];
         System.arraycopy(first, 0, result, 0, wp);
         return result;
     }

    
Combination of position and value, together with number of matches left to make with other position-value pairs, and an index of its position in an array of combinations with the same number of matches left
 
     private static class Combination {
 
         int position;
         int value;
         int numLeft;
         int positionInArray;
     }
    
sum over numChoices[]
 
     private int sumOfNumChoices;
    
combinations of position, value by number of pairings of each to make
 
     private ArrayList<Combination>[] combinationsByLeft;
    
combinations by position and value
 
     private Combination[][] combinationByPositionValue;

    
remove a combination from combinationsByLeft
 
     private void removeFromByLeft(Combination c) {
         ArrayList<CombinationfromHere = [c.numLeft];
         int s1 = fromHere.size()-1;
         Combination replacement = fromHere.get(s1);
         replacement.positionInArray = c.positionInArray;
         fromHere.set(c.positionInArrayreplacement);
         fromHere.remove(s1);
         c.positionInArray = -1;
     }

    
Add a combination to combinationsByLeft
 
     private void addToByLeft(Combination c) {
         c.positionInArray = [c.numLeft].size();
         [c.numLeft].add(c);
         // System.err.println("Add to at " + c.numLeft);
     }

    
decrement the number of combinations required by a combination, moving it from list to list
 
     private void decrementPosition(int positionint value) {
         // System.err.println("Decrement position " + position + " value " + value);
         Combination c = [position][value];
         removeFromByLeft(c);
         // System.err.println("Current need is " + c.numLeft);
         c.numLeft--;
         addToByLeft(c);
     }

    
This strategy keeps track of the number of combinations left to particular position/value combinations
 
     public int[][] generateGreedy() {
          = new Random(++);
         if (.<2) {
             return new int[0][];
         }
         .reset();
         for(Combination c : [0]){
             // Work out number of pairings of this particular position-value
             // combination with others
             c.numLeft = -[c.position];
             addToByLeft(c);
         }
         [0].clear();
         ArrayList<int[]> result = new ArrayList<int[]>();
         int highest = .-1;
         // Will use these arrays to keep track of which positions
         // in a partial row have been filled in
         boolean[] posUsed = new boolean[.];
         int[] positionsUsed = new int[.];
         lineloop:
         for(;;){
             // Find a combination with most pairings to match
             // System.err.println("New line");
             while ([highest].size()==0){ // Note that we always decrease the number of other values a
                 // combination has to pair with, so values always move down
                 // this array, not up.
                 highest--;
                 // System.err.println("Highest is " + highest);
                 if (highest<=0) { // all done
                     // System.err.println("All DONE");
                     break lineloop;
                 }
             }
             ArrayList<CombinationfromHere = [highest];
             int size = fromHere.size();
             Combination c = fromHere.get(.nextInt(size));
 
             // Build up a line starting with the chosen combinations
             int[] line = new int[.];
             line[c.position] = c.value;
             // note down see how many matches are yet to do
             int before = .getNumUsed();
             // will keep track of what positions in the new line are filled
             // in at each point
             Arrays.fill(posUsedfalse);
             posUsed[c.position] = true;
             int inUse = 1;
             positionsUsed[0] = c.position;
 
             // Now look for best extension, considering all choices
             searchLoop:
             while (inUse<.){
                 int topScore = .;
                 int numTops = 0;
                 Combination top = null;
                 for(int searchHere = highestsearchHere>0; searchHere--){
                     if (searchHere<topScore) {
                         // Can't possibly score more that searchHere by including a combination
                         // from here so break
                         break;
                     }
                     for(Combination d : [searchHere]){
                         if (posUsed[d.position]) {
                             continue;
                         }
                         int score = 0;
                         for(int l = 0; l<inUsel++){
                             int otherPos = positionsUsed[l];
                             if (!.isCombinationUsed(otherPosline[otherPos],
                                     d.positiond.value)) {
                                 score++;
                             }
                         }
                         if (score>topScore) {
                             topScore = score;
                             top = d;
                             numTops = 1;
                         } else if (score==topScore) { // Have a tie. Choice the new value with probability
                             // 1 in number-of-contendors-so-far. This makes all
                             // contendors equally likely to be chosen, regardless of
                             // the order in which they appear.
                             numTops++;
                             if (.nextInt(numTops)==0) {
                                 top = d;
                             }
                         }
                     }
                 }
                 if (top==null) { // no matches left to make
                     for(int k = 0; k<.k++){
                         if (!posUsed[k]) {
                             line[k] = .nextInt([k]);
                         }
                     }
                     break;
                 }
                 // note down all the new matches
                 for(int k = 0; k<inUsek++){
                     int otherPos = positionsUsed[k];
                     int otherVal = line[otherPos];
                     if (!.setUsed(otherPosotherVal,
                             top.positiontop.value)) {
                         decrementPosition(otherPosotherVal);
                         decrementPosition(top.positiontop.value);
                     }
                 }
                 positionsUsed[inUse++] = top.position;
                 line[top.position] = top.value;
                 posUsed[top.position] = true;
             }
             if (before==.getNumUsed()) { // have not decreased number of matches to make
                 throw new IllegalStateException("useless line");
             }
             result.add(line);
         }
         return result.toArray(new int[result.size()][]);
     }
 
     public static String[][] main(String[] sString dirFile cfilethrows IOException {
 
         String[][] pwc = null;
 
         List<IntegerchoiceList = new ArrayList<Integer>();
         String sp = "";
         boolean trouble = false;
         boolean noShuffle = false;
         int maxGoes = 100;
         long seed = 42;
         String choiceFile = null;
         int s1 = s.length-1;
         try {
             for(int i = 0; i<s.lengthi++){
                 sp = s[i].trim();
                 if (sp.startsWith("-")) {
                     if ((sp.equals("-file"))&&(i<s1)) {
                         choiceFile = s[++i].trim();
                     } else if ((sp.equals("-goes"))&&(i<s1)) {
                         sp = s[++i].trim();
                         maxGoes = Integer.parseInt(sp);
                     } else if (sp.equals("-noShuffle")) {
                         noShuffle = true;
                     } else if ((sp.equals("-seed"))&&(i<s1)) {
                         sp = s[++i].trim();
                         seed = Long.parseLong(sp);
                     } else {
                         ..println("Could not handle flag "+sp);
                         trouble = true;
                     }
                 } else {
                     int numChoices = Integer.parseInt(sp);
                     if (numChoices<1) {
                         ..println(
                                 "Number of choices must be > 1 at every point");
                         trouble = false;
                     }
                     choiceList.add(numChoices);
                 }
             }
         }
         catch (NumberFormatException nf) {
             ..println("Could not read number in "+sp);
             trouble = true;
         }
 
         int[] choices = new int[choiceList.size()];
         for(int i = 0; i<choices.lengthi++){
             choices[i] = choiceList.get(i);
         }
         Translator tran = null;
        if (choiceFile!=null) {
            if (!choiceList.isEmpty()) {
                ..println(
                        "Cannot give both -file <name> and numeric choices");
                trouble = true;
            } else {
                tran = new Translator(choiceFilecfile); // modified to just take the file
                choices = tran.getNumChoices();
            }
        }
        if (choices.length<2) {
            ..println(
                    "No point trying to work with less than two points");
            trouble = true;
        }
        if (trouble) {
            ..println(
                    "Arguments should be the number of choices at each point");
            ..println(
                    "E.g. to find a design for a system with three parameters,");
            ..println(
                    "with 2 choices for the first, 3 for the second, and 4 for the third,");
            ..println("Use arguments 2 3 4");
            ..println(
                    "Or use -file <name> with one line of strings per option");
            ..println("and # a comment");
            ..println(
                    "Can add flags [-goes #] [-noShuffle] [-seed #] [-file <name>]");
            ..println("-goes 0 => keep trying until halted");
            return null;
        }
        .debug("Working on choices:");
        for(int i = 0; i<choices.lengthi++){
            .debug(" x ");
            .debug(choices[i]);
        }
        .debug("\n");
        .debug("MaxGoes "+maxGoes+" seed "+seed
                +" noShuffle "+noShuffle);
        int[][] result = UnsynchronizedAllPairs.indefiniteGenerate(choicesseedmaxGoes, !noShuffletrandir);
        pwc = getStringResult(resulttran);
        return pwc;
    }
    // Check function for Galois Field based constructions
    static void gfCheck(int maxPowerString dir) {
        for(int p = 1; p<=maxPowerp++){
            for(int i = 0; i<.i++){
                int prime = [i];
                int pp = 1;
                for(int j = 0; j<pj++){
                    pp *= prime;
                }
                if (pp>121) {
                    // We will run out of memory very quickly, because there are
                    // (pp + 1) * (pp + 1) combinations of columns and pp * pp pairs to check
                    // for for each combination of columns, and we keep all of this
                    // in memory
                    continue;
                }
                ..println("Prime "+prime+" power "+pp);
                int[] choices = new int[pp+1];
                Arrays.fill(choicespp);
                ..println("Before ap");
                UnsynchronizedAllPairs ap = new UnsynchronizedAllPairs(choices, 42, true);
                ..println("Before generate");
                int[][] gen = ap.generateViaPrime(dir);
                ..println("after generate");
                if (gen.length!=pp*pp) {
                    throw new IllegalArgumentException("Bad length");
                }
            }
        }
    }

    
Class to read input file, create choices, and translate
    private static class Translator {
        Translator(String filenameFile cfilethrows IOException {
            BufferedReader br = null;
            try {
                //br = new BufferedReader(new FileReader(filename));
                br = new BufferedReader(new FileReader(cfile));
                List<String[]> sofar = new ArrayList<String[]>();
                for(;;){
                    String line = br.readLine();
                    if (line==null) {
                        break;
                    }
                    List<Stringsl = new ArrayList<String>();
                    StringTokenizer st = new StringTokenizer(line"|"); // now tokenize on | instead of , because values may contain ,
                    while (st.hasMoreTokens()){
                        String token = st.nextToken();
                        /*int pos = token.indexOf('#'); // what is this? comments? we neeed to be able to use all chars for values
                         if (pos == 0)
                         {
                         break;
                         }
                         else if (pos > 0)
                         {
                         sl.add(token.substring(0, pos));
                         break;
                         }*/
                        sl.add(token);
                    }
                    int len = sl.size();
                    //if (len == 0)
                    //{
                    //	//System.err.println(
                    //	//  "Discarding line with only one option: " + sl.get(0));
                    //}
                    //else if (len >= 1)
                    if (len>=1) {
                        String[] fromLine = new String[len];
                        sofar.add(sl.toArray(fromLine));
                    }
                }
                int lines = sofar.size();
                 = new String[lines][];
                 = sofar.toArray();
                 = new int[lines];