#if !defined  HAVE_PERM1_TOPSORT_H__
#define       HAVE_PERM1_TOPSORT_H__
// This file is part of the FXT library.
// Copyright (C) 2023 Joerg Arndt
// License: GNU General Public License version 3 or later,
// see the file COPYING.txt in the main directory.

#include "fxttypes.h"

class perm1_topsort
// Permutations satisfying some partial order.
// Knuth's "Algorithm V", section 7.2.1.2, p.343 in vol.4A/1 of TAOCP.
{
private:
    ulong *P;  // permutation in [1...n]
    ulong *Q;  // inverse permutation in [1...n], these are the top-sorts
    ulong n;
    ulong j, l, k;
    enum state_t  { V2, V3, V4, V5 };
    state_t state;
    bool (*cond)(ulong, ulong);  // condition function

    perm1_topsort(const perm1_topsort&) = delete;
    perm1_topsort & operator = (const perm1_topsort&) = delete;
public:
    explicit perm1_topsort( ulong tn, bool (*tcond)(ulong, ulong) )
    {
        n = tn;
        cond = tcond;
        P = new ulong[n+1];
        Q = new ulong[n+1];

        first();  // note
    }

    ~perm1_topsort()
    {
        delete [] P;
        delete [] Q;
    }

    const ulong *data()  const  { return Q + 1; }
    const ulong *data_perm()  const  { return P + 1; }
    ulong get_n()  const  { return n; }

    void first()
    {
        // V1: Initialize.
        for (ulong i=0; i <= n; ++i)  P[i] = Q[i] = i;
        k = n;
        state = V3;  // note
    }

    bool next()
    {
#define goto_state(s)  { state = s;  goto top; }

    top: ;
        switch ( state )
        {
        case V2:  // V2: Visit.
            {
                state = V3;
                k = n;
                return true;
                break;
            }
        case V3:  // V3: Can k move left?
            {
                j = Q[k];
                l = P[j-1];
                const bool q = cond( k, l );  // k < l ==> OK
                if ( q )  { goto_state( V4 ); }
                else      { goto_state( V5 ); }
                break;
            }
        case V4:  // V4: Yes, move it.
            {
                P[j-1] = k;  P[j] = l;
                Q[k] = j-1;  Q[l] = j;
                goto_state( V2 );
                break;
            }
        case V5:  // V5: No, put k back
            {
                while ( j < k )
                {
                    l = P[j+1];
                    P[j] = l;
                    Q[l] = j;
                    j += 1;
                }
                P[k] = Q[k] = k;
                k -= 1;
                if ( k==0 )  { return false; }
                goto_state( V3 );
                break;
            }
        }
        return false;  // unreached
    }

//    void print_perm(const char *bla)
};
// -------------------------


#endif // !defined HAVE_PERM1_TOPSORT_H__
