/*::.
==================================================================================================================================
=================================================¦ Copyright © 2007 Allen Baker ¦=================================================
                                                 +------------------------------+
File:       CombinationGenerator.java
Originator: Allen Baker (2007.04.24 19:31)
LayoutRev:  5
================================================================================================================================== */



/*.
==========================================================================================
Package
------------------------------------------------------------------------------------------ */
package cosmicabyss.com.lib;



/*.
==========================================================================================
Imports
------------------------------------------------------------------------------------------ */
import java.io.*;
import java.util.*;
import java.text.*;
import java.math.BigInteger;



/*::
======================================================================================================================== *//**
This class systematically generates all combinations of n elements, taken r at a time. The algorithm is described by
Kenneth H. Rosen, Discrete Mathematics and Its Applications, 2nd edition (NY: McGraw-Hill, 1991), pp. 284-286.<P>

This code was shamelessly plagerized from: http://www.merriampark.com/comb.htm on 2007.04.24.

<P>
   <DL>
      <DT>
         <B>
            Example usage:
         </B>
            The class is very easy to use. Suppose that you wish to generate all possible three-letter combinations of
            the letters "a", "b", "c", "d", "e", "f", "g". Put the letters into an array. Keep calling the combination
            generator's getNext() method until there are no more combinations left. The getNext() method returns an
            array of integers, which tells you the order in which to arrange your original array of letters. Here is a
            snippet of code which illustrates how to use the CombinationGenerator class.
         <DD>
            <BLOCKQUOTE>
               <PRE id="unindent">
                  XString[]             elements     = {"a","b","c","d","e","f","g"};
                  CombinationGenerator  x            = new CombinationGenerator(elements.length,3);
                  int[]                 indices;
                  StringBuffer          combination;
                  while (x.hasMore())
                    {
                    combination = new StringBuffer();
                    indices     = x.getNext();
                    for (int i = 0; i < indices.length; i++)
                       {
                       combination.append(elements[indices[i]]);
                       }
                    System.out.println(combination.toString());
                    }
               </PRE>
            </BLOCKQUOTE>
         </DD>
      </DT>
      <DT>
         <B>
            Example usage:
         </B>
            Another example of the usage of the CombinationGenerator is shown below in connection with the Zen Archery
            problem.<P>

            In his book Wonders of Numbers (Oxford: Oxford University Press, 2001), pp. 275-276, Clifford Pickover posed
            a "Zen Archery" problem. In its simplest form, there is a target with 24 numbers on it. The archer must
            shoot 5 arrows at the target and hit numbers adding up to 200. The 24 numbers on the target are<P>
         <DD>
            <BLOCKQUOTE>
               <PRE id="unindent">
                  97,101,139,41,37,31,29,89,23,19,8,13,
                  131,19,73,97,19,139,79,67,61,17,113,127
               </PRE>
            </BLOCKQUOTE>
         </DD>
            Pickover posed a similar problem at Archery by the Numbers. This is really a combinatorial problem -- given
            the 24 numbers taken 5 at a time, which unique combinations add up to 200?<P>

            There is some quick and dirty Java code on the Web, associated with Pickover's book, which solves the Zen
            archery problem for the 24 numbers given. However, it is not exactly a model of good programming, and it
            even assumes some foreknowledge of the answer in the code, i.e. The fact that all combinations adding up to
            200 include the number 8.<P>

            Using our CombinationGenerator class, we can write a neater, more general solution of the Zen archery
            problem (ZenArchery.java) as follows:
         <DD>
            <BLOCKQUOTE>
               <PRE id="unindent">
                   import java.util.*;
                   import java.math.*;

                   public abstract class ZenArchery
                      {

                      private static int getSum (Vector v)
                         {
                         int sum = 0;
                         Integer n;
                         for (int i = 0; i < v.size(); i++)
                            {
                            n = (Integer) v.elementAt (i);
                            sum += n.intValue();
                            }
                         return sum;
                         }

                      public static Vector compute(int[] array, int atATime, int desiredTotal)
                         {
                         int[]                 indices;
                         CombinationGenerator  gen             = new CombinationGenerator (array.length, atATime);
                         Vector                results         = new Vector();
                         Vector                combination;
                         BigInteger            numCombinations = gen.getTotal();

                         System.out.println ("Num combinations to test " + numCombinations.toString());

                         int      sum;
                         Integer  intObj;
                         while (gen.hasMore())
                            {
                            combination = new Vector();
                            indices     = gen.getNext();
                            for (int i = 0; i < indices.length; i++)
                               {
                               intObj = new Integer(array[indices[i]]);
                               combination.addElement(intObj);
                               }
                            sum = getSum(combination);
                            if (sum == desiredTotal)
                               {
                               Collections.sort(combination);
                               if ( ! results.contains(combination))
                                  {
                                  System.out.println(combination.toString());
                                  results.addElement(combination);
                                  }
                               }
                            }
                         return results;
                         }

                      public static void main (XString[] args)
                         {
                         int  array[]=
                            {
                            97,101,139,41,37,31,29,89,23,19,8,13,
                            131,19,73,97,19,139,79,67,61,17,113,127
                            };
                         Vector  results = ZenArchery.compute(array,5,200);
                         System.out.println ("Num results " + results.size());
                         }

                      }

                   Here is the output from running the ZenArchery class with Pickover's data:

                   Num combinations to test 42504
                   [8, 17, 37, 41, 97]
                   [8, 23, 31, 41, 97]
                   [8, 13, 37, 41, 101]
                   [8, 19, 31, 41, 101]
                   [8, 23, 31, 37, 101]
                   [8, 13, 17, 61, 101]
                   [8, 13, 17, 23, 139]
                   [8, 23, 41, 61, 67]
                   [8, 19, 19, 41, 113]
                   [8, 17, 41, 61, 73]
                   [8, 13, 29, 37, 113]
                   [8, 19, 23, 37, 113]
                   [8, 19, 29, 31, 113]
                   [8, 13, 17, 31, 131]
                   [8, 13, 29, 61, 89]
                   [8, 13, 23, 29, 127]
                   [8, 23, 29, 67, 73]
                   [8, 23, 29, 61, 79]
                   [8, 13, 19, 29, 131]
                   [8, 17, 19, 29, 127]
                   [8, 17, 29, 67, 79]
                   [8, 19, 23, 61, 89]
                   [8, 13, 23, 67, 89]
                   [8, 17, 19, 67, 89]
                   [8, 13, 17, 73, 89]
                   [8, 19, 19, 23, 131]
                   [8, 17, 23, 73, 79]
                   Num results 27
               </PRE>
            </BLOCKQUOTE>
         </DD>
      </DT>
      <DT>
         <B>
            View Source:
         </B>
         <DD>
            <A href="CombinationGenerator.java.html">
               CombinationGenerator.java
            </A>
         </DD>
      </DT>
      <DT>
         <B>
            Author:
         </B>
         <DD>
            <A href="mailto:sourcecode.v01@cosmicabyss.com">
               Allen Baker
            </A>
         </DD>
      </DT>
   </DL>
*//*
======================================================================================================================== */
public class CombinationGenerator
   {



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates a CombinationGenerator class object.

   <P><B>Implementation: </B><A HREF="CombinationGenerator.java.html#000">View source</A>

   @param
      N is the number of elements (the size of the set to pick combinations from)
   @param
      R is the number taken each time (the size of the combination set)
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public CombinationGenerator(int n, int r)
      {
      if (r > n)
         {
         throw new IllegalArgumentException();
         }
      if (n < 1)
         {
         throw new IllegalArgumentException();
         }
      this.n = n;
      this.r = r;
      a = new int[r];
      BigInteger nFact = getFactorial(n);
      BigInteger rFact = getFactorial(r);
      BigInteger nminusrFact = getFactorial(n - r);
      total = nFact.divide(rFact.multiply (nminusrFact));
      reset();
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method ...

   <P><B>Implementation: </B><A HREF="CombinationGenerator.java.html#001">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public void reset()
      {
      for (int i = 0; i < a.length; i++)
         {
         a[i] = i;
         }
      numLeft = new BigInteger(total.toString());
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns the number of combinations not yet generated

   <P><B>Implementation: </B><A HREF="CombinationGenerator.java.html#002">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public BigInteger getNumLeft()
      {
      return numLeft;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns whether or not there are more combinations to return

   <P><B>Implementation: </B><A HREF="CombinationGenerator.java.html#003">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public boolean hasMore ()
      {
      return numLeft.compareTo(BigInteger.ZERO) == 1;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns the total number of combinations

   <P><B>Implementation: </B><A HREF="CombinationGenerator.java.html#004">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public BigInteger getTotal()
      {
      return total;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method computes the factorial of n

   <P><B>Implementation: </B><A HREF="CombinationGenerator.java.html#005">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   private static BigInteger getFactorial(int n)
      {
      BigInteger fact = BigInteger.ONE;
      for (int i = n; i > 1; i--)
         {
         fact = fact.multiply(new BigInteger(Integer.toString(i)));
         }
      return fact;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method generates the next combination (algorithm from Rosen p. 286)

   <P><B>Implementation: </B><A HREF="CombinationGenerator.java.html#006">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public int[] getNext()
      {

      if (numLeft.equals(total))
         {
         numLeft = numLeft.subtract(BigInteger.ONE);
         return a;
         }

      int i = r - 1;
      while (a[i] == n - r + i)
         {
         i--;
         }
      a[i] = a[i] + 1;
      for (int j = i + 1; j < r; j++)
         {
         a[j] = a[i] + j - i;
         }

      numLeft = numLeft.subtract(BigInteger.ONE);
      return a;

      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method sets the ConsoleStream for this object.

   <P><B>Implementation: </B><A HREF="CombinationGenerator.java.html#007">View source</A>

   @return
      A reference to this object

   @param
      pConsole is a ConsoleStream through which this objects sends its output.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public CombinationGenerator setOut(ConsoleStream pConsole)
      {
      cOut = pConsole;
      return this;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This boilerplate method is used for testing an instantiated object of this class and may include any
   code the developer chooses.

   <P><B>Implementation: </B><A HREF="CombinationGenerator.java.html#008">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public CombinationGenerator test() throws Exception
      {
      cOut.titledPrintf
         (
         "\"HELLO WORLD!\"",
         "%s  %s  %s",
         "I'm an object of the", CLASS_NAME, "class, and I approved this message."
         );
      return this;
      }



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Protected  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Private  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */



   /*.
   ==========================================================================================
   Class Constants
      CLASS_NAME:
         The name of this class
      DFLT_LINE_LEN:
         The default line length for word wrapping
   ------------------------------------------------------------------------------------------ */
   private static final XString  CLASS_NAME    = new XString(CombinationGenerator.class.getName());
   private static final int      DFLT_LINE_LEN = ConsoleMessage.defaultLineLength();
   /*.
   ==========================================================================================
   Class variables
      cOut:
         Console output.
   ------------------------------------------------------------------------------------------ */
   private static ConsoleStream  cOut = ConsoleStream.getSingleton();
   /*.
   ==========================================================================================
   Instance variables
   ------------------------------------------------------------------------------------------ */
   private int[]       a;
   private int         n;
   private int         r;
   private BigInteger  numLeft;
   private BigInteger  total;



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Inner Classes  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Public Static Methods  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method allows this class file to be unit tested as a standalone application. It's the method
   that's called when the class is invoked from the command line by using the java application launcher
   - "java". Main() is not a required method, but the practice of putting one in each class and
   wrapping class test code within it allows easy unit testing of the class; and main does not need to
   be removed when testing is complete.

   <P>
      <DL>
         <DT>
            <B>
               Command line usage:
            </B>
            <DD>
               Java cosmicabyss.com.lib.CombinationGenerator
            </DD>
         </DT>
      </DL>

   <P><B>Implementation: </B><A HREF="CombinationGenerator.java.html#009">View source</A>

   @param
      pArgs contains the command line arguments with which this class was invoked as an application.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public static void main(String[] pArgs) throws Exception
      {
      /*.
      ==========================================================================================
      Greetings !
      ------------------------------------------------------------------------------------------ */
      cOut.banner(CLASS_NAME);
      /*.
      ==========================================================================================
      Test code
      ------------------------------------------------------------------------------------------ */
      XString[]  attributeNames =
         {
         new XString("structured"),
//         new XString("unstructured"),
         new XString("fn.sales"),
         new XString("fn.mktg"),
         new XString("fn.LP"),
         new XString("fn.spprt"),
         new XString("fn.IT"),
         new XString("fn.mfg"),
         new XString("fn.HR"),
         new XString("fn.REWS"),
         new XString("fn.fin"),
         new XString("fn.UNKNOWN"),
         new XString("biz.all"),
         new XString("biz.CUST"),
         new XString("biz.IPG"),
         new XString("biz.PSG"),
         new XString("biz.TSG"),
         new XString("biz.CORP"),
         new XString("seg.any"),
         new XString("seg.consumer"),
         new XString("seg.HHO"),
         new XString("seg.enterprise"),
         new XString("region.WW"),
         new XString("region.NA"),
         new XString("region.EMEA"),
         new XString("region.APJ"),
         new XString("region.LAR"),
         };
      XString[]  assetNames =
         {
         new XString("ATCLVasontRepository "),
         new XString("CAT"),
         new XString("Cheetah"),
         new XString("Concentra"),
         new XString("CRL"),
         new XString("DMS-IPG"),
         new XString("DSPP"),
         new XString("eComCat"),
         new XString("ESP"),
         new XString("ESS/Non-StopDMF"),
         new XString("HPDC"),
         new XString("hpshopping"),
         new XString("ICED"),
         new XString("Jobs@hp"),
         new XString("KCTContentRepository"),
         new XString("Learn@hp"),
         new XString("MMR"),
         new XString("OCBU"),
         new XString("PavilionFactory"),
         new XString("PIT"),
         new XString("PRISM.Gemstone"),
         new XString("ProjectDUO"),
         new XString("RACE"),
         new XString("SAPKnowledgeServices"),
         new XString("SGBUDCTM"),
         new XString("SOAR"),
         new XString("SPI"),
         new XString("SWD-VasontRepository"),
         new XString("TheSource"),
         new XString("UCR"),
         new XString("WebPublishingCenter"),
         new XString("Write4HR"),
         new XString("Write4REWS"),
         };
      int[][]  assetVectors =
         {
         {1,/*0,*/0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0},
         {1,/*0,*/0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,0,0,0,0},
         {1,/*0,*/0,0,1,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,1,0,0,0,0},
         {0,/*1,*/0,1,1,1,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,0},
         {0,/*1,*/0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0},
         {0,/*1,*/0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,1,1,1,1},
         {0,/*1,*/1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0},
         {1,/*0,*/0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0},
         {0,/*1,*/1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0},
         {0,/*1,*/0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0},
         {1,/*0,*/0,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,1,0,0,0,0},
         {1,/*1,*/1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,1,0,0,0},
         {0,/*1,*/0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,0,0,1,0,0,0,0},
         {0,/*1,*/0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,1,0,0,0,0},
         {0,/*1,*/1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0},
         {0,/*1,*/0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,1,0,0,0,0},
         {0,/*1,*/0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0},
         {0,/*1,*/0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0},
         {0,/*1,*/0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0},
         {0,/*1,*/1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,0},
         {1,/*0,*/0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,0},
         {0,/*1,*/1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0},
         {0,/*1,*/0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,0,0,1,0,0,0,0},
         {0,/*1,*/0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0},
         {0,/*1,*/1,1,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0},
         {0,/*1,*/0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,0},
         {1,/*0,*/0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,1,1,0,0,0,0},
         {1,/*0,*/0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0},
         {0,/*1,*/1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,0},
         {0,/*1,*/0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0},
         {0,/*1,*/0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0},
         {0,/*1,*/0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,1,0,0,0,0},
         {0,/*1,*/0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,1,0,0,0,0}
         };
      /*.
      ==========================================================================================
      This is the number of assets
      ------------------------------------------------------------------------------------------ */
      int  nAssets = assetVectors.length;
      /*.
      ==========================================================================================
      This is the number of attribute values in each assetVector. All the vectors have the same
      number of attributes.
      ------------------------------------------------------------------------------------------ */
      int  n = assetVectors[0].length;
      /*.
      ==========================================================================================
      For each asset (the "this" asset)
      ------------------------------------------------------------------------------------------ */
      for (int thisIdx=0; thisIdx<nAssets; thisIdx++)
         {
         int      maxR  = 8;
//         XString  title = new XString(" " + assetNames[thisIdx] + " ");
//         cOut.println();
//         cOut.println();
//         cOut.println(title.alignCenter(150,'-'));


if (thisIdx==0)
   {
   DecimalFormat fmt = new DecimalFormat("###,###,###,###");
   cOut.println("combinations of " + n + " taken " + maxR + " at a time is: " + fmt.format(combinations(n,maxR)));
   cOut.println("max combinations checked per asset is: " + fmt.format(maxCombinationsChecked(n,maxR)));
   cOut.println();
   }

cOut.println();
cOut.println("--------------------------------- " + assetNames[thisIdx] + " ---------------------------------");


         boolean noSolution = true;
         /*.
         ==========================================================================================
         This is the this asset vector for which we are trying to find the smallest unique
         combination of values.
         ------------------------------------------------------------------------------------------ */
         int[]  thisVector = assetVectors[thisIdx];
         /*.
         ==========================================================================================
         For r = 1 .. Number of attributes
         ------------------------------------------------------------------------------------------ */
         for (int r=1; r<=maxR; r++)
            {
            boolean  simplestUniqueCombinationFound = false;
            CombinationGenerator combinations = new CombinationGenerator(n,r);
            while (combinations.hasMore())
               {
               boolean combinationIsUnique = true;
               int[]   combination         = combinations.getNext();
               /*.
               ==========================================================================================
               For each of the other assets check to see if there is a match with the this asset on all
               of the r attributes in this combination
               ------------------------------------------------------------------------------------------ */
               if (combinationIsUnique(combination,thisIdx,assetVectors))
                  {
                  simplestUniqueCombinationFound = true;
                  displayCombination(cOut,combination,thisVector,assetNames[thisIdx],attributeNames);
//                  break;
                  }

               } // while there are more combinations of size r

            if (simplestUniqueCombinationFound)
               {
               noSolution = false;
               break;
               }

            } // each size of conmbination starting at the smallest
         if (noSolution)
            {
            displayFailure(cOut,assetNames[thisIdx],maxR);
            }
         } // each this asset
      }



public static boolean combinationIsUnique
   (
   int[]    pCombination,
   int      pThisIdx,
   int[][]  pVecs
   )
   {
   int    nAssets    = pVecs.length;
   int[]  thisVector = pVecs[pThisIdx];
   for (int otherIdx=0; otherIdx<nAssets; otherIdx++)
      {
      /*.
      ==========================================================================================
      Don't test the focus asset against itself
      ------------------------------------------------------------------------------------------ */
      if (pThisIdx==otherIdx) continue;
      /*.
      ==========================================================================================
      This is the other asset vector we are looking for a match against at the moment.
      ------------------------------------------------------------------------------------------ */
      int[]  otherVector = pVecs[otherIdx];
      /*.
      ==========================================================================================
      If a match was found then this is not a unique combination and there is no need to
      continue checking it against the remaining other assets.
      ------------------------------------------------------------------------------------------ */
      if (combinationsMatch(thisVector,otherVector,pCombination))
         {
         return false;
         }
      }
   return true;
   }



public static boolean combinationsMatch
   (
   int[] pVec1,
   int[] pVec2,
   int[] pCombination
   )
   {
   boolean  match = true;
   for (int i=0; i<pCombination.length; i++)
      {
      int  idx = pCombination[i];
      match = match && (pVec1[idx]==pVec2[idx]);
      if ( ! match) break;
      }
   return match;
   }



public static void  displayCombination
   (
   ConsoleStream  pOut,
   int[]          pCombination,
   int[]          pVec,
   XString        pAssetName,
   XString[]      pAttNames
   )
   {
   /*.
   ==========================================================================================
   Each attribute will be put in a field this size
   ------------------------------------------------------------------------------------------ */
   int fieldSize = 0;
   for (int i=0; i<pAttNames.length; i++)
      {
      fieldSize = UMath.max(fieldSize,pAttNames[i].length());
      }
   fieldSize += "NOT()".length() + 3;
   /*.
   ==========================================================================================
   ------------------------------------------------------------------------------------------ */
   pOut.print("[" + UString.alignRight(pCombination.length,2,'0') + "]   ");
   for (int i = 0; i < pCombination.length; i++)
      {
      int      idx     = pCombination[i];
      XString  attName = pAttNames[idx];
      boolean  vecVal  = (pVec[idx]==1);

      if (i>0) pOut.print ("   ");

      XString  attribute = new XString
         (
         (vecVal)?
            UString.alignRight(idx,2,'0') + "." + attName :
            UString.alignRight(idx,2,'0') + "." + "NOT(" + attName + ")"
         );
      attribute = attribute.alignLeft(fieldSize,' ');
      pOut.print(attribute);
      }
   pOut.println ();
//   pOut.println ("     (((" + pAssetName + ")))");
   }


public static void  displayFailure
   (
   ConsoleStream  pOut,
   XString        pAssetName,
   int            pR
   )
   {
   /*.
   ==========================================================================================
   ------------------------------------------------------------------------------------------ */
 //  pOut.println("[" + UString.alignRight(pR,2,'0') + "]   **** NO SOLUTION ****" + "     (((" + pAssetName + ")))");
   pOut.println("[" + UString.alignRight(pR,2,'0') + "]   **** NO SOLUTION ****");
   }


public static BigInteger factorial(int pNum)
   {
   BigInteger n = BigInteger.ONE;
   for (int i=1; i<=pNum; i++)
      {
      n = n.multiply(BigInteger.valueOf(i));
      }
   return n;
   }


public static BigInteger combinations(int pN, int pR)
   {
   BigInteger nFac   = factorial(pN);
   BigInteger rFac   = factorial(pR);
   BigInteger n_rFac = factorial(pN-pR);

   return nFac.divide(rFac.multiply(n_rFac));
   }


public static BigInteger maxCombinationsChecked(int pN, int pR)
   {
   BigInteger n = BigInteger.ZERO;
   for (int i=1; i<=pR; i++)
      {
      n = n.add(combinations(pN,i));
      }
   return n;
   }


   }  // class CombinationGenerator



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Notes  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */