/*::.
==================================================================================================================================
=================================================¦ Copyright © 2007 Allen Baker ¦=================================================
                                                 +------------------------------+
File:       Digest.java
Originator: Allen Baker (2007.02.01 14:54)
LayoutRev:  5
================================================================================================================================== */



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



/*.
==========================================================================================
Imports
------------------------------------------------------------------------------------------ */
import java.io.*;
import java.util.*;
import java.security.*;



/*::
======================================================================================================================== *//**
Instances of this class are digests of Strings, XStrings, byte[]s, or files. The default algorithm for computing the
digest is "SHA-512".<P>

2014.12.10: Modified to eliminate reliance on BouncyCastle and rely instead on standard SDK providers.

Note: The standard Java that you download from Oracle does not support some of these algorithms. You would have to add a
      JCE provider JAR from a 3rd-party such as BouncyCastle to your classpath to be able to use some of these
      algorithms.

See Appendix A in the Java Cryptography Architecture API Specification & Reference for information about standard
algorithm names. Alternatively, refer to the documentation of the JCE provider that you are using for information about
the algorithm names the provider supports.<P>

Here is a table of example algorithm names that are supported by the BouncyCastle JCE provider. These are just examples;
there may be other algorithm names supported by BouncyCastle and there may be an entirely different set supported by
another JCE provider:
   <BLOCKQUOTE>
      <PRE id="unindent">
         +------------+----------------+-------------------------------+
         |            | Size of Digest |                               |
         |            +------+---------+                               |
         |  Algorithm |      |  Hex    |                               |
         |    Name    | Bits | Digits  |            Comment            |
         +------------+------+---------+-------------------------------+
         | GOST3411   |  256 |     64  |                               |
         | MD2        |  128 |     32  |                               |
         | MD4        |  128 |     32  |                               |
         | MD5        |  128 |     32  |                               |
         | RipeMD128  |  128 |     32  | basic RipeMD                  |
         | RipeMD160  |  160 |     40  | enhanced version of RipeMD    |
         | SHA        |  160 |     40  |                               |
         | SHA1       |  160 |     40  | Same as "SHA"                 |
         | SHA-1      |  160 |     40  | Same as "SHA"                 |
         | SHA-224    |  224 |     56  | FIPS 180-2                    |
         | SHA-256    |  256 |     64  | FIPS 180-2                    |
         | SHA-384    |  384 |     96  | FIPS 180-2                    |
         | SHA-512    |  512 |    128  | FIPS 180-2                    |
         | Tiger      |  192 |     48  |                               |
         | Whirlpool  |  512 |    128  | ISO/IEC 10118-3:2004          |
         +------------+------+---------+-------------------------------+
      </PRE>
   </BLOCKQUOTE>

In addition to the algorithms listed in the documentation mentioned above, this class recognizes a compound digest
algorithm named "COMPOUND". Here's the specification on the compound digest algorithm:
   <BLOCKQUOTE>
      <PRE id="unindent">
         +------------+----------------+-------------------------------+
         |            | Size of Digest |                               |
         |            +------+---------+                               |
         |  Algorithm |      |  Hex    |                               |
         |    Name    | Bits | Digits  |            Comment            |
         +------------+------+---------+-------------------------------+
         | COMPOUND   | 1024 |    256  | This algorithm generates a    |
         |            |      |         | digest using a combination of |
         |            |      |         | several of the most hack-     |
         |            |      |         | resistant of the standard     |
         |            |      |         | digest algorithms that are    |
         |            |      |         | available from the JCE        |
         |            |      |         | provider.                     |
         +------------+------+---------+-------------------------------+
      </PRE>
   </BLOCKQUOTE>

The algorithims that are used to compute a "COMPOUND" digest can be changed using the setAlgorithmList() method. The
default set of algorithms that are used to compute a "COMPOUND" digest were choosen based upon information found at <A
href="http://paginas.terra.com.br/informatica/paulobarreto/hflounge.html"> The Hash Function Lounge</A> regarding the
algorithms supported the BouncyCastle JCE provider.<P>

This class can also generate a digest based on the "COMPOUND" algorithm that is any size (in <B>bits</B>) that is a
positive multiple of 8. The way to specify the size is to append the size to the "COMPOUND" algorithm name. Here are
some examples:
   <BLOCKQUOTE>
      <PRE id="unindent">
         +--------------+----------------+-------------------------------+
         |              | Size of Digest |                               |
         |              +------+---------+                               |
         |   Algorithm  |      |  Hex    |                               |
         |     Name     | Bits | Digits  |            Comment            |
         +--------------+------+---------+-------------------------------+
         | COMPOUND128  |  128 |     32  |                               |
         | COMPOUND160  |  160 |     40  |                               |
         | COMPOUND224  |  224 |     56  |                               |
         | COMPOUND256  |  256 |     64  |                               |
         | COMPOUND384  |  384 |     96  |                               |
         | COMPOUND512  |  512 |    128  |                               |
         | COMPOUND1024 | 1024 |    256  | Same as "COMPOUND"            |
         | COMPOUND3072 | 3072 |    768  |                               |
         +--------------+------+---------+-------------------------------+
      </PRE>
   </BLOCKQUOTE>

If the size that's appended to the "COMPOUND" algorithm name is not a positive multiple of 8, it will be increased
automatically to the smallest integer that is greater than the number and a multiple of 8. Here are some examples:

   <BLOCKQUOTE>
      <PRE id="unindent">
         +--------------+----------------+-------------------------------+
         |              | Size of Digest |                               |
         |              +------+---------+                               |
         |   Algorithm  |      |  Hex    |                               |
         |     Name     | Bits | Digits  |            Comment            |
         +--------------+------+---------+-------------------------------+
         | COMPOUND0    |    8 |      2  |                               |
         | COMPOUND1    |    8 |      2  |                               |
         | COMPOUND223  |  224 |     56  |                               |
         | COMPOUND250  |  256 |     64  |                               |
         | COMPOUND377  |  384 |     96  |                               |
         | COMPOUND513  |  520 |    130  |                               |
         | COMPOUND1019 | 1024 |    256  | Same as "COMPOUND"            |
         | COMPOUND3076 | 3080 |    770  |                               |
         +--------------+------+---------+-------------------------------+
      </PRE>
   </BLOCKQUOTE>

This class also provides a method - iterate() - that "re-digests" the digest a specified number of times. For example,
calling iterate() on a new Digest object with its "re-digest" count set to 3 like this:
   <BLOCKQUOTE>
      <PRE id="unindent">
         Digest x = (new Digest(theThingToDigest)).iterate(3);
      </PRE>
   </BLOCKQUOTE>

   Is equivalent to doing this:

      <BLOCKQUOTE>
         <PRE id="unindent">
            Digest x = new Digest(theThingToDigest);

            x = new Digest(x.bytes())
            x = new Digest(x.bytes())
            x = new Digest(x.bytes())
         </PRE>
      </BLOCKQUOTE>

<P>
   <DL>
      <DT>
         <B>
            Example usage:
         </B>
         <DD>
            <BLOCKQUOTE>
               <PRE id="unindent">
                  =========================================================================================
                  create a ConsoleStream to send all output through.
                  -----------------------------------------------------------------------------------------
                  ConsoleStream  out = new ConsoleStream();
                  out.setLineLength(1000);

                  =========================================================================================
                  generate digests for these things
                  -----------------------------------------------------------------------------------------
                  XString   str   = "c:/temp/test.txt";
                  XString  xstr  = new XString(str);
                  byte[]   bytes = str.getBytes();
                  File     file  = new File(str);

                  =========================================================================================
                  display various digests
                  -----------------------------------------------------------------------------------------
                  out.println("");
                  out.println("                           1         2         3         4         5         6         7         8         9         0         1         2         3         4         5         6");
                  out.println("                  1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
                  out.println("                  ----------------------------------------------------------------------------------------------------------------------------------------------------------------");
                  out.println("GOST3411       :  "  + (new Digest("GOST3411"  , str).XString()));
                  out.println("MD2            :  "  + (new Digest("MD2"       , str).XString()));
                  out.println("MD4            :  "  + (new Digest("MD4"       , str).XString()));
                  out.println("MD5            :  "  + (new Digest("MD5"       , str).XString()));
                  out.println("RipeMD128      :  "  + (new Digest("RipeMD128" , str).XString()));
                  out.println("RipeMD160      :  "  + (new Digest("RipeMD160" , str).XString()));
                  out.println("SHA            :  "  + (new Digest("SHA"       , str).XString()));
                  out.println("SHA1           :  "  + (new Digest("SHA1"      , str).XString()));
                  out.println("SHA-1          :  "  + (new Digest("SHA-1"     , str).XString()));
                  out.println("SHA-224        :  "  + (new Digest("SHA-224"   , str).XString()));
                  out.println("SHA-256        :  "  + (new Digest("SHA-256"   , str).XString()));
                  out.println("SHA-384        :  "  + (new Digest("SHA-384"   , str).XString()));
                  out.println("SHA-512        :  "  + (new Digest("SHA-512"   , str).XString()));
                  out.println("default        :  "  + (new Digest(              str).XString()));
                  out.println("Tiger          :  "  + (new Digest("Tiger"     , str).XString()));
                  out.println("Whirlpool      :  "  + (new Digest("Whirlpool" , str).XString()));
                  out.println("");
                  out.println("SHA-224(s)        "  , (new Digest("SHA-224"     , str  ).XString()));
                  out.println("COMPOUND224(s)    "  , (new Digest("COMPOUND224" , str  ).XString()));
                  out.println("SHA-224(x)        "  , (new Digest("SHA-224"     , xstr ).XString()));
                  out.println("COMPOUND224(x)    "  , (new Digest("COMPOUND224" , xstr ).XString()));
                  out.println("SHA-224(b)        "  , (new Digest("SHA-224"     , bytes).XString()));
                  out.println("COMPOUND224(b)    "  , (new Digest("COMPOUND224" , bytes).XString()));
                  out.println("SHA-224(f)        "  , (new Digest("SHA-224"     , file ).XString()));
                  out.println("COMPOUND224(f)    "  , (new Digest("COMPOUND224" , file ).XString()));
                  out.println("COMPOUND(f)       "  , (new Digest("COMPOUND"    , file ).XString()));
                  out.println("");
                  out.println("COMPOUND1         "  , (new Digest("COMPOUND1"    , str).XString()));
                  out.println("COMPOUND15        "  , (new Digest("COMPOUND15"   , str).XString()));
                  out.println("COMPOUND128       "  , (new Digest("COMPOUND128"  , str).XString()));
                  out.println("COMPOUND250       "  , (new Digest("COMPOUND250"  , str).XString()));
                  out.println("COMPOUND1020      "  , (new Digest("COMPOUND1020" , str).XString()));
                  out.println("COMPOUND          "  , (new Digest("COMPOUND"     , str).XString()));
                  out.println("COMPOUND1220      "  , (new Digest("COMPOUND1220" , str).XString()));
                  out.println("COMPOUND12200     "  , (new Digest("COMPOUND12200", str).XString()));
                  out.println("");
                  out.println("default           "  , (new Digest(                str).XString()));
                  out.println("default.i(23)     "  , (new Digest(                str).iterate(23).XString()));
                  out.println("COMPOUND          "  , (new Digest("COMPOUND"    , str).XString()));
                  out.println("COMPOUND.i(23)    "  , (new Digest("COMPOUND"    , str).iterate(23).XString()));
                  out.println("COMPOUND          "  , (new Digest("COMPOUND"    , str).XString()));
                  out.println("COMPOUND.i(1)     "  , (new Digest("COMPOUND"    , str).iterate(1).XString()));
                  out.println("COMPOUND1220      "  , (new Digest("COMPOUND1220", str).XString()));
                  out.println("COMPOUND1220.i(2) "  , (new Digest("COMPOUND1220", str).iterate(2).XString()));
               </PRE>
            </BLOCKQUOTE>
         </DD>
      </DT>
      <DT>
         <B>
            View Source:
         </B>
         <DD>
            <A href="Digest.java.html">
               Digest.java
            </A>
         </DD>
      </DT>
      <DT>
         <B>
            Author:
         </B>
         <DD>
            <A href="mailto:sourcecode.v01@cosmicabyss.com">
               Allen Baker
            </A>
         </DD>
      </DT>
   </DL>
*//*
======================================================================================================================== */
public class Digest
   {



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates a Digest of a data item using the default algorithm.

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

   @param
      pData is the XString, XString, byte[], File, or XFile for which the digest is generated.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type> Digest(Type pData) throws Exception
      {
      this.setAlgorithmAndSize(DFLT_ALGRTM);
      this.digest(pData);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates a Digest of a data item using the specified algorithm.

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

   @param
      pAlgorithm is the name of the algorithm to use for generating the digest. It may be a JCE
      provider supported algorithm name. (See the Java Cryptography Architecture API Specification &
      Reference for information about standard algorithm names.) Or to generate a compound digest, it
      may be the name "COMPOUND" or "COMPOUND" with a number appended, as in "COMPOUND256". The
      appended number can be any number that is a positive multiple of 8 (<B>bits</B>).
   @param
      pData is the XString, XString, byte[], File, or XFile for which the digest is generated.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1,Type2> Digest(Type1 pAlgorithm, Type2 pData) throws Exception
      {
      this.setAlgorithmAndSize(NCString.toNCString(pAlgorithm));
      this.digest(pData);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates a Digest of a data item using the COMPOUND algorithm which in turn uses the
   specified list of JCE provider supported algorithms.<P>

   This method changes the standard algorithms that are used to generate a "COMPOUND" digest. The
   COMPOUND algorithm generates a separate digest using each listed standard algorithm, mixes the
   results together and applies the default standard algorithm - SHA-512 - to the result in such a way
   that it is possible to generate an arbitrarily long digest. Here's an example of a list of standard
   algorithm names that could be passed to this method. Notice that the same name can be in the list
   multiple times and will be used as many times as it is listed.
      <BLOCKQUOTE>
         <PRE id="unindent">
            "GOST3411"
            "Whirlpool"
            "RipeMD128"
            "RipeMD160"
            "GOST3411"
            "SHA-512"
            "Whirlpool"
         </PRE>
      </BLOCKQUOTE>

   Only the names of algorithms that are supported by the JCE provider can be in the list. Any other
   names in the list will result in an Exception being thrown. Specifically, the use of "COMPOUND" or
   any of its size appended derivitives is not allowed in the list.<P>

   If null or an empty list is passed into this method, this method will reset the algorithms to the
   default list of algorithms.

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

   @param
      pAlgorithms is a list of Strings. Each XString in the list is one name of a JCE provider
      supported digest algorithm.
   @param
      pData is the XString, XString, byte[], File, or XFile for which the digest is generated.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1,Type2> Digest(List<Type1> pAlgorithms, Type2 pData) throws Exception
      {
      iAlgorithm = CMPND_ALGRTM;
      this.setAlgorithmList(pAlgorithms);
      this.digest(pData);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns the digest as a byte[].

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

   @return
      An array of bytes containing a binary representation of the digest.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public byte[] bytes()
      {
      return iBytes;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns the digest as a char-array of hexidecimal digits.

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

   @return
      An array of chars containing a hexidecimal character representation of the digest.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public char[] chars()
      {
      if (iChars == null)
         {
         iChars = this.string().toCharArray();
         }
      return iChars;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns the digest as a String of hexidecimal digits.

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

   @return
      A String containing a hexidecimal character representation of the digest.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public String string()
      {
      if (iStr == null)
         {
         iStr = UString.toHexString(iBytes);
         }
      return iStr;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns the digest as a String of hexidecimal digits. It is equivalent to the String()
   method and is here to override the toString() method defined by the Object class for use in String
   expressions.

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

   @return
      A String containing a hexidecimal character representation of the digest.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public String toString()
      {
      return this.string();
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns the digest as an XString of hexidecimal digits.

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

   @return
      An XString containing a hexidecimal character representation of the digest.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public XString xstring()
      {
      if (iXStr == null)
         {
         iXStr = new XString(this.string());
         }
      return iXStr;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method "re-digests" the digest one time. For example, using this method like this:
      <BLOCKQUOTE>
         <PRE id="unindent">
            Digest x = new Digest(theThingToDigest);
            x = x.iterate()
         </PRE>
      </BLOCKQUOTE>

   Is equivalent to doing this:
      <BLOCKQUOTE>
         <PRE id="unindent">
            Digest x = new Digest(theThingToDigest);
            x = new Digest(x.bytes());
         </PRE>
      </BLOCKQUOTE>

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

   @return
      A reference to this object
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public Digest iterate() throws Exception
      {
      return this.iterate(1);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method "re-digests" the digest the number of times specified by pIterations. For example, using
   this method like this:
      <BLOCKQUOTE>
         <PRE id="unindent">
            Digest x = new Digest(theThingToDigest);
            x = x.iterate(3);
         </PRE>
      </BLOCKQUOTE>

   Is equivalent to doing this:
      <BLOCKQUOTE>
         <PRE id="unindent">
            Digest x = new Digest(theThingToDigest);
            x = new Digest(x.bytes()) * x = new Digest(x.bytes());
            x = new Digest(x.bytes());
         </PRE>
      </BLOCKQUOTE>

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

   @return
      A reference to this object

   @param
      pIterations is the number of times to "re-digest" the digest.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public Digest iterate(int pIterations) throws Exception
      {
      /*.
      ==========================================================================================
      Reset the derived representations of the "real" digest (which is stored in iBytes) so that
      they will be re-derived after this method re-digests (changes) iBytes.
      ------------------------------------------------------------------------------------------ */
      iChars = null;
      iStr   = null;
      iXStr  = null;
      /*.
      ==========================================================================================
      Re-digest the existing digest pIteration times
      ------------------------------------------------------------------------------------------ */
      for (int i=0; i<pIterations; i++)
         {
         this.digest(this.bytes());
         }
      /*.
      ==========================================================================================
      Return a reference to this object so that this method can be used in situations like this:
         <BLOCKQUOTE>
            <PRE id="unindent">
               x = new Digest("food");
               System.out.println(x.iterate(103)); // print the digest
            </PRE>
         </BLOCKQUOTE>
      ------------------------------------------------------------------------------------------ */
      return this;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method changes the standard algorithms that are used to generate a "COMPOUND" digest. The
   COMPOUND algorithm generates a separate digest using each listed standard algorithm, mixes the
   results together and applies the default standard algorithm - SHA-512 - to the result in such a way
   that it is possible to generate an arbitrarily long digest. Here's an example of a list of standard
   algorithm names that could be passed to this method. Notice that the same name can be in the list
   multiple times and will be used as many times as it is listed.
      <BLOCKQUOTE>
         <PRE id="unindent">
            "GOST3411"
            "Whirlpool"
            "RipeMD128"
            "RipeMD160"
            "GOST3411"
            "SHA-512"
            "Whirlpool"
         </PRE>
      </BLOCKQUOTE>

   Only the names of algorithms that are supported by the JCE provider can be in the list. Any other
   names in the list will result in an Exception being thrown. Specifically, the use of "COMPOUND" or
   any of its size appended derivitives is not allowed in the list.<P>

   If null or an empty list is passed into this method, this method will reset the algorithms to the
   default list of algorithms.

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

   @return
      A reference to this object

   @param
      pAlgorithms is a list of Strings. Each XString in the list is one name of a JCE provider
      supported digest algorithm.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type> Digest setAlgorithmList(List<Type> pAlgorithms) throws Exception
      {
      /*.
      ==========================================================================================
      If no names were passed in, then reset to the default names
      ------------------------------------------------------------------------------------------ */
      if ((pAlgorithms == null) || (pAlgorithms.size() == 0))
         {
         iAlgorithmList = CMPND_ALGRTM_LST;
         return this;
         }
      /*.
      ==========================================================================================
      Recreate the list using case-insensitive XStrings. In the process, check the Strings to
      make sure that the user has not included "COMPOUND" in the list of algorithms. Its
      inclusion in the array would cause infinite recusion when a COMPOUND digest is computed.
      ------------------------------------------------------------------------------------------ */
      List<XString>  algorithms = new ArrayList<XString>();
      for (Type str : pAlgorithms)
         {
         XString  ncStr = new NCString(str);
         algorithms.add(ncStr);
         if (ncStr.startsWith(CMPND_ALGRTM))
            {
            Exception  e = new Exception
               (
               "Cannot use \""              +
               ncStr                        +
               "*\" as a component of the " +
               CMPND_ALGRTM                 +
               " algorithm"
               );
            throw(e);
            }
         }
      /*.
      ==========================================================================================
      Set the list of Strings containing the names of the algorithms that will be used to
      compute COMPOUND digests.
      ------------------------------------------------------------------------------------------ */
      iAlgorithmList = algorithms;
      return this;
      }



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

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

   @return
      A reference to this object

   @param
      pConsole is a ConsoleStream through which this objects sends its output.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public Digest 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="Digest.java.html#012">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public Digest 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
      <BLOCKQUOTE>
         <PRE id="unindent">
            CLASS_NAME       : the name of this class
            DFLT_LINE_LEN    : the default line length for word wrapping
            DFLT_ALGRTM      : default algorithm
            CMPND_ALGRTM     : compound algorithm
            CMPND_DGST_SZ    : compound digest size in BITS
            CMPND_ALGRTM_LST : compound algorithm component algorithms
         </PRE>
      </BLOCKQUOTE>
   ------------------------------------------------------------------------------------------ */
   private static final XString        CLASS_NAME       = new XString(Digest.class.getName());
   private static final int            DFLT_LINE_LEN    = ConsoleMessage.defaultLineLength();
   private static final XString        DFLT_ALGRTM      = new NCString("SHA-512");
   private static final XString        CMPND_ALGRTM     = new NCString("COMPOUND");
   private static final int            CMPND_DGST_SZ    = 1024;
   private static final List<XString>  CMPND_ALGRTM_LST = Arrays.asList
      (                                     //   Output size
                                            //   ----------------------
      (XString)(new NCString("SHA-512")),   //   512 bits
      (XString)(new NCString("MD5" )),      // + 128 bits
      (XString)(new NCString("SHA-384" )),  // + 384 bits
      (XString)(new NCString("SHA-224")),   // + 224 bits
      (XString)(new NCString("SHA-1")),     // + 160 bits
      (XString)(new NCString("SHA-256")),   // + 256 bits
      (XString)(new NCString("SHA-512" ))   // + 512 bits = 2176 bits
      );
   /*.
   ==========================================================================================
   Class variables
      cOut : console output.
   ------------------------------------------------------------------------------------------ */
   private static ConsoleStream  cOut = ConsoleStream.getSingleton();
   /*.
   ==========================================================================================
   Instance variables for storing the data used to construct the object
      <BLOCKQUOTE>
         <PRE id="unindent">
            iSize          : size of custom digest to generate
            iAlgorithm     : algorithm to use to generate digest
            iAlgorithmList : algorithms to use to generate compound digest
         </PRE>
      </BLOCKQUOTE>
   ------------------------------------------------------------------------------------------ */
   private int            iSize          = 0;
   private XString        iAlgorithm     = DFLT_ALGRTM;
   private List<XString>  iAlgorithmList = CMPND_ALGRTM_LST;
   /*.
   ==========================================================================================
   Instance variables for storing various representations of the computed digest.
      <BLOCKQUOTE>
         <PRE id="unindent">
            iBytes : stores the digest as a byte[]
            iChars : stores a character representation of the digest as a char[]
            iStr   : stores a character representation of the digest as a XString
            iXStr  : stores a character representation of the digest as an XString
         </PRE>
      </BLOCKQUOTE>
   ------------------------------------------------------------------------------------------ */
   private byte[]   iBytes = null;
   private char[]   iChars = null;
   private String   iStr   = null;
   private XString  iXStr  = null;



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method sets the instance variable that keeps track of what algorithm to use to compute the
   digest. If the algorithm is "COMPOUND" or any derivitive of "COMPOUND", this method also sets the
   instance variable that keeps track of what size to use to generate the digest.

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

   @return
      A reference to this object

   @param
      pAlgorithm is the name of the algorithm to use for generating the digest. It may be a JCE
      provider supported algorithm name. (See the Java Cryptography Architecture API Specification &
      Reference for information about standard algorithm names.) Or to generate a compound digest, it
      may be the name "COMPOUND" or "COMPOUND" with a number appended, as in "COMPOUND256". The
      appended number can be any number that is a positive multiple of 8 (<B>bits</B>).
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private Digest setAlgorithmAndSize(XString pAlgorithm) throws Exception
      {
      /*.
      ==========================================================================================
      If its just the compound name without a size appended, use the compound algorithm and
      default size
      ------------------------------------------------------------------------------------------ */
      if (pAlgorithm.equals(CMPND_ALGRTM))
         {
         iAlgorithm = CMPND_ALGRTM;
         iSize      = CMPND_DGST_SZ;
         iSize      = this.ceiling(iSize);
         }
      /*.
      ==========================================================================================
      If its the compound name concatenated with a size, use the compound algorithm and the
      specified size
      ------------------------------------------------------------------------------------------ */
      else if (pAlgorithm.startsWith(CMPND_ALGRTM))
         {
         NCString  tmp = new NCString(pAlgorithm);
         iAlgorithm = CMPND_ALGRTM;
         iSize      = UMath.decodeDecimalInt
            (
            tmp.trimLeft(CMPND_ALGRTM).string()
            );
         iSize = this.ceiling(iSize);
         }
      /*.
      ==========================================================================================
      Otherwise use the specified algorithm name and the size doesn't matter so just set it to
      0.
      ------------------------------------------------------------------------------------------ */
      else
         {
         iAlgorithm = pAlgorithm;
         iSize      = 0;
         }
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      return this;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns the smallest <B>positive</B> integer value that is greater than or equal to the
   argument and that is an exact multiple of 8.<P>

   * Note * This method is not in the XMath class because it is not generic enough to fit there and it
   is not straightforward enough to be worth the effort to put there. To make it fit there it would
   have to be made to work generically for all positive and negative pNum and an arbitrary multiple
   (which is hardcoded to 8 in this version).

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

   @param
      pNum is the value to compute the ceiling of.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private int ceiling(int pNum)
      {
      /*.
      ==========================================================================================
      First set the result to pNum. If pNum is negative (or anything else less than 8) then just
      make the result 8.
      ------------------------------------------------------------------------------------------ */
      int  result = ((pNum < 8)? 8 : pNum);
      /*.
      ==========================================================================================
      Then decide if result is a multiple of 8. If not then increase it until it is.
      ------------------------------------------------------------------------------------------ */
      int  mod = result % 8;
      if (mod > 0)
         {
         result += (8 - mod);
         }
      return result;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method generates the digest of a data item.

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

   @return
      A reference to this object

   @param
      pData is the data item for which the digest is generated.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private <Type> Digest digest(Type pData) throws Exception
      {
      if      (pData instanceof XString) return this.digest(((XString)pData).getBytes());
      else if (pData instanceof String)  return this.digest(((String)pData).getBytes());
      else if (pData instanceof byte[])  return this.digest(((byte[])pData));
      else if (pData instanceof File)    return this.digest(((File)pData));
      else
         {
         throw new Exception
            (
            "Cannot digest objects of class: "  +
            Util.className(pData)
            );
         }
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method generates the digest of a byte[].

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

   @return
      A reference to this object

   @param
      pBytes is the byte[] for which the digest is generated.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private Digest digest(byte[] pBytes) throws Exception
      {
      if (iAlgorithm.equals(CMPND_ALGRTM))
         {
         this.CompoundDigest(pBytes);
         }
      else
         {
         MessageDigest  md = MessageDigest.getInstance(iAlgorithm.string());
         md.update(pBytes);
         iBytes = md.digest();
         }
      return this;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method generates the digest of a file.

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

   @return
      A reference to this object

   @param
      pFile is the file for which the digest is generated.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private Digest digest(File pFile) throws Exception
      {
      if (iAlgorithm.equals(CMPND_ALGRTM))
         {
         this.CompoundDigest(pFile);
         }
      else
         {
         /*.
         ==========================================================================================
         Create the digest and digest stream objects
         ------------------------------------------------------------------------------------------ */
         MessageDigest      md     = MessageDigest.getInstance(iAlgorithm.string());
         DigestInputStream  reader = new DigestInputStream
            (
            new BufferedInputStream(new FileInputStream(pFile)),
            md
            );
         /*.
         ==========================================================================================
         Read the file adding all its bytes to the digest computation
         ------------------------------------------------------------------------------------------ */
         byte[]  byteArray = new byte[UMath.ONE_MEG];
         while (true)
            {
            int  bytesRead = reader.read(byteArray,0,UMath.ONE_MEG);
            if (bytesRead == (-1)) break;
            }
         /*.
         ==========================================================================================
         Close the file and complete the digest computation
         ------------------------------------------------------------------------------------------ */
         reader.close();
         iBytes = md.digest();
         }
      return this;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method generates a COMPOUND digest of a byte[] using a combination of the most hack resistant
   digest algorithms.<P>

   If the default COMPOUND digest is specified, this method generates a 1024 bit (256 hex digit)
   digest. The chance of a digest collision in a digest space that size is pretty remote. A digest of
   that size can be any one of 2^1024 (1.797693134862315907729305190789 e+308) unique digest values.
   That's many, many zillions of times more unique digest values than there are atoms in the known
   universe (which is roughly 1.0e+79 atoms).

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

   @return
      A reference to this object

   @param
      pBytes is the byte[] for which the digest is generated.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private Digest CompoundDigest(byte[] pBytes) throws Exception
      {
      iBytes = this.secondRound(this.firstRound(pBytes));
      return this;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method generates a COMPOUND digest of the contents of a file using a combination of the most
   hack resistant digest algorithms.<P>

   If the default COMPOUND digest is specified, this method generates a 1024 bit (256 hex digit)
   digest. The chance of a digest collision in a digest space that size is pretty remote. A digest of
   that size can be any one of 2^1024 (1.797693134862315907729305190789 e+308) unique digest values.
   That's many, many zillions of times more unique digest values than there are atoms in the known
   universe (which is roughly 1.0e+79 atoms).

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

   @return
      A reference to this object

   @param
      pFile is the file for which the digest is generated.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private Digest CompoundDigest(File pFile) throws Exception
      {
      iBytes = this.secondRound(this.firstRound(pFile));
      return this;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method computes a separate digest of the input parameter using each of the algorithms in the
   compound algorithms list. Then it mixes the digests together into a single large digest and returns
   that digest as a byte[].

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

   @return
      A byte[] containing a digest.

   @param
      pBytes is the byte[] for which the digest is generated.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private byte[] firstRound(byte[] pBytes) throws Exception
      {
      /*.
      ==========================================================================================
      Create an array containing enough byte arrays to hold a digest computed using each
      algorithm in the compound algorithms list.
      ------------------------------------------------------------------------------------------ */
      byte[][]  firstRound = new byte[iAlgorithmList.size()][];
      /*.
      ==========================================================================================
      For each algorithm specified in the compound algorithms list, compute a digest of the
      input object and store the digest in an array of digests.
      ------------------------------------------------------------------------------------------ */
      int  i = 0;
      for (XString algo : iAlgorithmList)
         {
         firstRound[i++] = (new Digest(algo,pBytes)).bytes();
         }
      /*.
      ==========================================================================================
      Mix the digests together into a single large digest and return it
      ------------------------------------------------------------------------------------------ */
      return this.mixByteArrays(firstRound);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method computes a separate digest of the input parameter using each of the algorithms in the
   compound algorithms list. Then it mixes the digests together into a single large digest and returns
   that digest as a byte[].

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

   @return
      A byte[] containing a digest.

   @param
      pFile is the file for which the digest is generated.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private byte[] firstRound(File pFile) throws Exception
      {
      /*.
      ==========================================================================================
      Create an array containing enough byte arrays to hold a digest computed using each
      algorithm in the compound algorithms list.
      ------------------------------------------------------------------------------------------ */
      byte[][]  firstRound = new byte[iAlgorithmList.size()][];
      /*.
      ==========================================================================================
      For each algorithm specified in the compound algorithms list, compute a digest of the
      input object and store the digest in an array of digests.
      ------------------------------------------------------------------------------------------ */
      int  i = 0;
      for (XString algo : iAlgorithmList)
         {
         firstRound[i++] = (new Digest(algo,pFile)).bytes();
         }
      /*.
      ==========================================================================================
      Mix the digests together into a single large digest and return it
      ------------------------------------------------------------------------------------------ */
      return this.mixByteArrays(firstRound);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method digests the input byte[] and then begins accumulating re-digests in a result array,
   re-digesting as many times as necessary, until the result array is completely filled with an iSize
   BITS long digest.

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

   @return
      A byte[] containing a digest.

   @param
      pBytes is the byte[] for which the digest is generated.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private byte[] secondRound(byte[] pBytes) throws Exception
      {
      /*.
      ==========================================================================================
      Convert the size in bits to a byte count and allocate a byte array of that size that will
      be used to accumulate the compound digest
      ------------------------------------------------------------------------------------------ */
      int     byteCount = (this.ceiling(iSize) / 8);
      byte[]  compound  = new byte[byteCount];
      /*.
      ==========================================================================================
      Loop until the accumulating compound digest is filled to capacity
      ------------------------------------------------------------------------------------------ */
      int  size = 0;
      while (size < byteCount)
         {
         /*.
         ==========================================================================================
         Use the standard algorithm to digest pBytes
         ------------------------------------------------------------------------------------------ */
         pBytes = new Digest(pBytes).bytes();
         /*.
         ==========================================================================================
         Append that digest to the compound digest
         ------------------------------------------------------------------------------------------ */
         for (int i=0; ((size<byteCount) && (i<pBytes.length)); size++, i++)
            {
            compound[size] = pBytes[i];
            }
         /*.
         ==========================================================================================
         If the compound digest isn't full yet then compute another digest using the algorithms in
         the compound algorithms list and put it into pBytes
         ------------------------------------------------------------------------------------------ */
         if (size < byteCount)
            {
            pBytes = firstRound(pBytes);
            }
         }
      /*.
      ==========================================================================================
      Perform a one-way hash across all elements of the digest array so that an attack cannot
      succeed by looking at just the first 512 bits.
      ------------------------------------------------------------------------------------------ */
      compound = UMath.RopL(compound,'^',7);
      compound = UMath.LopR(compound,'+',7);
      /*.
      ==========================================================================================
      And return the compound digest
      ------------------------------------------------------------------------------------------ */
      return compound;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method mixes an array of bytes into one single, large array of bytes. The returned byte[] will
   be as large as the sum of the sizes of all the input byte[]s. The byte[]s are mixed together by
   interleaving their bytes into the result byte[] that is returned from this method. For example, if
   these were the input byte[]s:

      <BLOCKQUOTE>
         <PRE id="unindent">
            array[1] = 1
            array[2] = 2,2
            array[3] = 3,3,3
            array[4] = 4,4,4,4
            array[5] = 5,5,5,5,5
            array[6] = 6,6,6,6,6,6
            array[7] = 7,7,7,7,7,7,7
         </PRE>
      </BLOCKQUOTE>

   Then this would be the interleaved byte[]:

      <BLOCKQUOTE>
         <PRE id="unindent">
            result = 1,2,3,4,5,6,7,2,3,4,5,6,7,3,4,5,6,7,4,5,6,7,5,6,7,6,7,7
         </PRE>
      </BLOCKQUOTE>

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

   @return
      A byte[] containing a mix of all the bytes found in all the input byte[]s.

   @param
      pByteArrays is an array of byte[]s that will be mixed together into a single, larger byte[].
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private byte[] mixByteArrays(byte[][] pByteArrays) throws Exception
      {
      /*.
      ==========================================================================================
      Figure out the size of each byte[]; calculate the sum of the sizes of the byte[]s; and
      remember the size of the longest byte array.
      ------------------------------------------------------------------------------------------ */
      int    max   = 0;
      int    sum   = 0;
      int[]  sizes = new int[pByteArrays.length];
      for (int i=0; i<sizes.length; i++)
         {
         int  byteArraySize = pByteArrays[i].length;

         sizes[i] = byteArraySize;
         sum      = sum + byteArraySize;
         max      = UMath.max(max,byteArraySize);
         }
      /*.
      ==========================================================================================
      'result' is the byte[] into which the bytes from the byte[]s will be interleaved. 'c' is
      the index of the next byte in result to be loaded from one of the byte[]s. Result will be
      as big as the sum of the sizes of all the byte[]s.
      ------------------------------------------------------------------------------------------ */
      byte[]  result = new byte[sum];
      int     c      = 0;
      /*.
      ==========================================================================================
      Interleave the individual bytes from the pByteArrays into one giant byte[].

      For each byte[] byte position...
      ------------------------------------------------------------------------------------------ */
      for (int i=0; i<max; i++)
         {
         /*.
         ==========================================================================================
         For each byte[]...
         ------------------------------------------------------------------------------------------ */
         for (int j=0; j<pByteArrays.length; j++)
            {
            /*.
            ==========================================================================================
            If the byte[] has a byte in the ith position, load it into the cth element of the result
            array and then increment c to index the next available element in the result array.
            ------------------------------------------------------------------------------------------ */
            if (i<sizes[j])
               {
               result[c++] = pByteArrays[j][i];
               }
            }
         }
      /*.
      ==========================================================================================
      Return the byte[]
      ------------------------------------------------------------------------------------------ */
      return result;
      }



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  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.Digest
            </DD>
         </DT>
      </DL>

   <P><B>Implementation: </B><A HREF="Digest.java.html#024">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);
      /*.
      ==========================================================================================
      Get a list of all the password-based encryption algorithms supported by the installed
      providers
      ------------------------------------------------------------------------------------------ */
      ArrayList<String> messageDigests = Util.getJceProviderImplementations("MessageDigest");
      for (int i=0; i<messageDigests.size(); i++)
         {
         int  outputSize = Util.getJceDigestOutputSize(messageDigests.get(i));
         }
      /*.
      ==========================================================================================
      Test each algorithm name to see if it is valid; if it is, add it to our results array
      ------------------------------------------------------------------------------------------ */
      ArrayList<String>  results = new ArrayList<String>();
      for (int i=0; i<messageDigests.size(); i++)
         {
         String  algorithmName = messageDigests.get(i);
         /*.
         ==========================================================================================
         If the algorithm name is valid (supported by the installed provider), then no exception
         will be thrown.
         ------------------------------------------------------------------------------------------ */
         try
            {
            /*.
            ==========================================================================================
            Create a digest object
            ------------------------------------------------------------------------------------------ */
            Digest obj = new Digest(algorithmName);
            /*.
            ==========================================================================================
            If no exception has been thrown by now, then the providers appear to think the algorithm
            name is valid. Store the name in an array which will be sorted and output.
            ------------------------------------------------------------------------------------------ */
            int     outputSize       = Util.getJceDigestOutputSize(algorithmName);
            String  outputSizeString = (new XString(outputSize)).alignRight(4, ' ').toString();
            results.add(outputSizeString + " " + algorithmName);
            }
         /*.
         ==========================================================================================
         If an exception was thrown, then the algorithm name isn't valid. Just ignore it then and
         go on to the next one.
         ------------------------------------------------------------------------------------------ */
         catch (Exception e)
            {
            }
         }
      /*.
      ==========================================================================================
      Sort the array of valid names into descending order according to output size in bits
      ------------------------------------------------------------------------------------------ */
      results = Util.sortedArrayList(results);
      results = Util.reversedArrayList(results);
      /*.
      ==========================================================================================
      Print the sorted array to cOut
      ------------------------------------------------------------------------------------------ */
      int  count = 0;
      cOut.println("    OUTP");
      cOut.println("IDX SIZE ALGORITHM NAME");
      cOut.println("--- ---- ------------------------------------");
      for (String algorithmName : results)
         {
         count++;
         String  countString = (new XString(count)).alignRight(3, ' ').toString();
         cOut.println(countString + " " + algorithmName);
         }

      cOut.println();
      cOut.println();
      cOut.println();
      cOut.println();

      /*.
      ==========================================================================================
      Generate digests for these things
      ------------------------------------------------------------------------------------------ */
      String   str   = "c:/temp/test.txt";
      XString  xstr  = new XString(str);
      byte[]   bytes = str.getBytes();
      File     file  = new File(str);
      /*.
      ==========================================================================================
      Do some testing
      ------------------------------------------------------------------------------------------ */
      cOut.setLineLength(1000);
      cOut.println("                           1         2         3         4         5         6         7         8         9         0         1         2         3         4         5         6");
      cOut.println("                  1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
      cOut.println("                  ----------------------------------------------------------------------------------------------------------------------------------------------------------------");
      cOut.println("MD2            :  "  + (new Digest("MD2"             , str).xstring()));
      cOut.println("MD5            :  "  + (new Digest("MD5"             , str).xstring()));
      cOut.println("SHA            :  "  + (new Digest("SHA"             , str).xstring()));
      cOut.println("SHA1           :  "  + (new Digest("SHA1"            , str).xstring()));
      cOut.println("SHA-1          :  "  + (new Digest("SHA-1"           , str).xstring()));
      cOut.println("SHA-224        :  "  + (new Digest("SHA-224"         , str).xstring()));
      cOut.println("SHA-256        :  "  + (new Digest("SHA-256"         , str).xstring()));
      cOut.println("SHA-384        :  "  + (new Digest("SHA-384"         , str).xstring()));
      cOut.println("SHA-512        :  "  + (new Digest("SHA-512"         , str).xstring()));

      cOut.setLineLength(0);
      cOut.println("SHA-224(s)        "  , (new Digest("SHA-224"     , str  ).xstring()));
      cOut.println("COMPOUND224(s)    "  , (new Digest("COMPOUND224" , str  ).xstring()));
      cOut.println("SHA-224(x)        "  , (new Digest("SHA-224"     , xstr ).xstring()));
      cOut.println("COMPOUND224(x)    "  , (new Digest("COMPOUND224" , xstr ).xstring()));
      cOut.println("SHA-224(b)        "  , (new Digest("SHA-224"     , bytes).xstring()));
      cOut.println("COMPOUND224(b)    "  , (new Digest("COMPOUND224" , bytes).xstring()));
      cOut.println("SHA-224(f)        "  , (new Digest("SHA-224"     , file ).xstring()));
      cOut.println("COMPOUND224(f)    "  , (new Digest("COMPOUND224" , file ).xstring()));
      cOut.println("COMPOUND(f)       "  , (new Digest("COMPOUND"    , file ).xstring()));
      cOut.println("");
      cOut.println("COMPOUND1         "  , (new Digest("COMPOUND1"    , str).xstring()));
      cOut.println("COMPOUND15        "  , (new Digest("COMPOUND15"   , str).xstring()));
      cOut.println("COMPOUND128       "  , (new Digest("COMPOUND128"  , str).xstring()));
      cOut.println("COMPOUND250       "  , (new Digest("COMPOUND250"  , str).xstring()));
      cOut.println("COMPOUND1020      "  , (new Digest("COMPOUND1020" , str).xstring()));
      cOut.println("COMPOUND          "  , (new Digest("COMPOUND"     , str).xstring()));
      cOut.println("COMPOUND1220      "  , (new Digest("COMPOUND1220" , str).xstring()));
      cOut.println("COMPOUND12200     "  , (new Digest("COMPOUND12200", str).xstring()));
      cOut.println("");
      cOut.println("default           "  , (new Digest(                str).xstring()));
      cOut.println("default.i(23)     "  , (new Digest(                str).iterate(23).xstring()));
      cOut.println("COMPOUND          "  , (new Digest("COMPOUND"    , str).xstring()));
      cOut.println("COMPOUND.i(23)    "  , (new Digest("COMPOUND"    , str).iterate(23).xstring()));
      cOut.println("COMPOUND          "  , (new Digest("COMPOUND"    , str).xstring()));
      cOut.println("COMPOUND.i(1)     "  , (new Digest("COMPOUND"    , str).iterate(1).xstring()));
      cOut.println("COMPOUND1220      "  , (new Digest("COMPOUND1220", str).xstring()));
      cOut.println("COMPOUND1220.i(2) "  , (new Digest("COMPOUND1220", str).iterate(2).xstring()));
      }



   }  // class Digest