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

#include "fxttypes.h"
#include "jjassert.h"

class perm1_prefix_cond
// Generate all permutations (one-based) with restricted prefixes,
// in lexicographic order.
// Knuth's "Algorithm X", section 7.2.1.2, p.334 in vol.4A/1 of TAOCP.
{
private:
    ulong n;   // number of elements to permute
    ulong *a;  // current permutation of {1, ..., n}, one-based array!
    ulong *l;  // auxiliary table: links
    ulong *u;  // auxiliary table: undo operations
    bool (*cond)(const ulong*, ulong);  // condition function

    ulong k, q, p;
    enum state_t  { X2, X3, X5, X6 };
    state_t state;

    perm1_prefix_cond(const perm1_prefix_cond&) = delete;
    perm1_prefix_cond & operator = (const perm1_prefix_cond&) = delete;

public:
    explicit perm1_prefix_cond(ulong nn,
                               bool (*cnd)(const ulong *a, ulong k))
    {
        n = (nn > 0 ? nn : 1);
        cond = cnd;
        a = new ulong[n+1];  // a[0] unused
        l = new ulong[n+1];
        u = new ulong[n+1];
    }

    ~perm1_prefix_cond()
    {
        delete [] a;
        delete [] l;
        delete [] u;
    }

    const ulong *data1()  const  { return a; }
    const ulong *data0()  const  { return a + 1; }

    bool first()
    // Must be called before next() !
    {
        // X1: Initialize
        for (ulong i=0; i<n; i++)  l[i] = i+1;
        l[n] = 0;
        k = 1;

        state = X2;
        return next();  // note
    }

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

    top: ;
        switch ( state )
        {

        case X2:  // X2: Enter level k
            {
                p = 0;
                q = l[0];
                goto_state( X3 );
                break;
            }

        case X3:  // X3: test (a[1], ..., a[k])
            {
                a[k] = q;
                if ( ! cond(a, k) )  goto_state( X5 );  // inline to optimize
                if ( k == n )
                {
                    state = X6;
                    return true;
                    break;
                }

                // X4: Increase k:
                u[k] = p;
                l[p] = l[q];
                ++k;
                goto_state( X2 );
                break;
            }

        case X5:  // X5: Increase a[k]
            {
                p = q;
                q = l[p];
                if ( q != 0 )  goto_state( X3 );
                goto_state( X6 );
                break;
            }
        case X6:  // X6: Decrease k
            {
                --k;
                if ( 0 == k )  return false;
                p = u[k];
                q = a[k];
                l[p] = q;
                goto_state( X5 );
                break;
            }
        }
        jjassert( 0 );
        return false;  // unreached
    }
};
// -------------------------


#endif  // !defined HAVE_PERM1_PREFIX_COND_H__
