package net.sf.colossus.util;


/**
 * This class is a permutation generator. The permutations are generated
 * using Dershowitz's method, meaning that a permutation only differs
 * by the previous permutation by a single interchange of two adjacent
 * elements. In many problem domains this allows a efficient dynamic
 * update of a permutation function.
 * @author Peter Unold
 * @see "Dershowitz, Nachum. "A simplified loop - 
 *     free algorithm for generating permutations" BIT - 15 1975 158 - 164"
 * @see <a href="http://www.daimi.aau.dk/~pjunold/alg/dershowitz.html">
 *     Dershowitz's Permutation Generator</a>
 */
class PermGen
{
    int[] m_p; // permutation
    int[] m_l; // location
    int[] m_t; // linked list
    int[] m_d; // direction
    int m_size;

    PermGen(int size)
    {
        m_size = size;
        m_p = new int[size];
        m_l = new int[size];
        m_t = new int[size + 1];
        m_d = new int[size];

        for (int i = 0; i < size; ++i)
        {
            m_p[i] = i;
            m_l[i] = i;
            m_d[i] = -1;
        }

        for (int j = 1; j <= size; ++j)
        {
            m_t[j] = j - 1;
        }
    }

    /** generates the next permutation. If the function returns n, then
     the elements at position n and n + 1 in the previous permutation
     were interchanged to get the new permutation.
     @return the index of the lower element which was interchanged or
     - 1 if the last permutation has been reached.  */
    public int getNext()
    {
        int cur, neig, curpos, neigpos, neigpos2;

        // 3
        if (m_t[m_size] < 1)
        {
            return -1;
        }

        // 4
        cur = m_t[m_size];
        curpos = m_l[cur];
        neigpos = curpos + m_d[cur];
        neig = m_p[neigpos];

        // 5
        m_l[cur] = neigpos;
        m_l[neig] = curpos;
        m_p[curpos] = neig;
        m_p[neigpos] = cur;

        // 6
        m_t[m_size] = m_size - 1;

        // 7
        neigpos2 = neigpos + m_d[cur];

        if (neigpos2 < 0 || neigpos2 >= m_size || cur < m_p[neigpos2])
        {
            m_d[cur] = -m_d[cur];
            m_t[cur + 1] = m_t[cur];
            m_t[cur] = cur - 1;
        }
        return (curpos < neigpos) ? curpos : neigpos;
    }

    /** get the current permutation */
    public int[] getCurrent()
    {
        return m_p;
    }

    // TODO JUnit
    /** Unit test for PermGen. */
    public static void main(String[] args)
    {
        if (args.length == 0)
        {
            System.out.println("Need to provide a size.");
            return;
        }
        int size = Integer.parseInt(args[0]);
        int[] perm = new int[size];
        int count = 0;
        PermGen pg = new PermGen(size);
        do
        {
            perm = pg.getCurrent();
            System.out.print(++count + " : ");
            for (int i = 0; i < size; i++)
            {
                System.out.print(perm[i]);
            }
            System.out.println();
        }
        while (pg.getNext() != -1);
    }
}
