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


#include "comb/comb-print.h"
#include "fxttypes.h"


class ksubset_twoclose_list
// k-subsets (0 <= kmin <= k <= kmax <= n)
//   in a two-close order with homogeneous moves.
// Representation as list of elements.
{
private:
    ulong * S;  // k-subset (as list of elements)
    struct SF_t  // stack frame
    {
        ulong n1;  // max number of ones yet to be filled in
        ulong n0;  // max number of zeros yet to be filled in
        ulong sn;  // position in S[]
        ulong q;   // direction (0 or 1)
        ulong c;   // 1: first branch, 2: second branch, 3: finished
        SF_t( )
            : n1(0), n0(0), sn(0), q(0), c(1)
        { ; }
        SF_t( ulong tn1, ulong tn0, ulong tsn, ulong tq )
            : n1(tn1), n0(tn0), sn(tsn), q(tq), c(1)
        { ; }
    };
    SF_t * SF;
    ulong d;  // position on stack frame

    ulong n_s;  // length of list; <= kmax
    const ulong n;   // subsets of the n-element set
    const ulong kmin, kmax;   // kmin <= k <= kmax
    const bool w;    // false by default; modifies ordering

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

public:
    ksubset_twoclose_list( ulong tn, ulong tkmin, ulong tkmax, bool tw=false )
        : n(tn), kmin(tkmin), kmax(tkmax), w(tw)
    {
        S = new ulong[n];
        SF = new SF_t[n + 1];  // last is write-only

        first();
    }

    ~ksubset_twoclose_list()
    {
        delete [] S;
        delete [] SF;
    }

    ulong *data()  const  { return S; }
    ulong len()  const  { return n_s; }

private:
//    __attribute__((always_inline))
    inline
    void put_sf( ulong td, ulong n1, ulong n0, ulong sn, ulong q )
    { SF[ td ] = SF_t { n1, n0, sn, q }; }

public:
    void first()
    {
        const ulong kmax0 = n - kmin;
        put_sf( 0, kmax, kmax0, 0, 0 );
        //      d   n1    n0    sn q
        d = 0;
        n_s = 0;

        next();  // note
    }

//    __attribute__((hot,flatten))
    bool next()
    {
        if ( n == 0 )  return false;  // so n==0 works

    QR:  // read stack frame
        SF_t & Zd = SF[d];
        const ulong & n0 = Zd.n0;
        const ulong & n1 = Zd.n1;
        const ulong & sn = Zd.sn;
        const ulong & q = Zd.q;
        ulong & c = Zd.c;

    QD:  // decide next state
        if ( d == n )  // visit
        {
            n_s = sn;  // for len()
            d -= 1;  // return
            return true;
        }

        if ( c == 3 )  // return to level d-1
        {
            if ( d == 0 )  return false;  // level 0 done
            d -= 1;
            goto QR;
        }

        if ( q )  // recursion
        {
            if ( c == 1 )  goto Q1;
            else           goto Q2;
        }
        else
        {
            if ( c == 1 )  goto Q3;
            else           goto Q4;
        }


    Q1:  // q == true, first recursion
        // if ( q )
        // if ( n0 != 0 )
        // { S[d] = 0;  next_rec( d, n1, n0-1, w ^ (d & 1) ); }
        c = 2;
        if ( n0 != 0 )
        {
            d += 1;
            put_sf( d, n1, n0-1, sn, w ^ (d & 1) );
            goto QR;  // recurse
        }
        goto Q2;

    Q2:  // q == true, second recursion
        // if ( q )
        // if ( n1 != 0 )
        // { S[d] = 1;  next_rec( d, n1-1, n0, q ); }
        c = 3;
        if ( n1 != 0 )
        {
            S[ sn ] = d;
            d += 1;
            put_sf( d, n1-1, n0, sn+1, q );
            goto QR;  // recurse
        }

        goto QD;  // return

    Q3:  // q == false, first recursion
        // if ( ! q )
        // if ( n1 != 0 )
        // { S[d] = 1;  next_rec( d, n1-1, n0, q ); }
        c = 2;
        if ( n1 != 0 )
        {
            S[ sn ] = d;
            d += 1;
            put_sf( d, n1-1, n0, sn+1, q );
            goto QR;  // recurse
        }

        goto Q4;

    Q4:  // q == false, second recursion
        // if ( ! q )
        // if ( n0 != 0 )
        // { S[d] = 0;  next_rec( d, n1, n0-1, w ^ (d & 1) ); }
        c = 3;
        if ( n0 != 0 )
        {
            d += 1;
            put_sf( d, n1, n0-1, sn, w ^ (d & 1) );
            goto QR;  // recurse
        }
        goto QD;  // return

        return false;  // unreached
    }

    void print( const char * bla )  const
    { print_set( bla, data(), len() ); }

    void print_deltaset( const char * bla )  const
    { print_set_as_deltaset( bla, data(), len(), n ); }
};
// -------------------------



#endif  // !defined HAVE_KSUBSET_TWOCLOSE_LIST_H__
