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


#include "sort/minmaxmed23.h"  // max2()
#include "comb/is-setpart-rgs.h"
#include "comb/comb-print.h"

#include "fxttypes.h"


class setpart_rgs_lex
// Set partitions of the n-set as restricted growth strings (RGS):
// strings s[0, 1, ..., n-1] such that s[k] <= max(s[0], s[1], ..., s[k-1]) + 1.
// Lexicographic order.
{
private:
    ulong n;    // Number of elements of set (set = {1,2,3,...,n})
    ulong *M;   // m[k] = max(s[0], s[1], ..., s[k-1]) + 1 where s = rgs
    ulong *S;   // RGS

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

public:
    explicit setpart_rgs_lex(ulong tn)
    {
        n = tn;
        M = new ulong[n+1];
        M[0] = 1;    // sentinel:  m[0] = 1
        S = new ulong[n];
        first();
    }

    ~setpart_rgs_lex()
    {
        delete [] M;
        delete [] S;
    }

    const ulong* data()  const  { return S; }
    ulong num_sets()  const  { return ( n ? M[n] : 0 ); }

    void first()
    {
        for (ulong k=0; k<n; ++k)  S[k] = 0;
        for (ulong k=1; k<=n; ++k)  M[k] = 1;
    }

    void last()
    {
        for (ulong k=0; k<n; ++k)  S[k] = k;
        for (ulong k=1; k<=n; ++k)  M[k] = k;
    }


    bool next()
    {
        if ( M[n] >= n )  return false;

        ulong k = n;
        do  { --k; }  while ( S[k] + 1 > M[k] );  // may read sentinel

//        if ( k == 0 )  return false;

        S[k] += 1UL;
#if 0
        const ulong mm = M[k+1] = max2(M[k], S[k]+1);
#else  // faster:
        ulong mm = M[k];
        mm += (S[k] >= mm);
        M[k+1] = mm;  // == max2(M[k], S[k]+1)
#endif

        while ( ++k < n )  // fill tail with zeros
        {
            S[k] = 0;
            M[k+1] = mm;
        }

        return true;
    }

    bool prev()
    {
        if ( M[n] == 1 )  return false;

        ulong k = n;
        do  { --k; }  while ( S[k]==0 );

        S[k] -= 1;
        ulong mm = M[k+1] = max2(M[k], S[k]+1);

        while ( ++k < n )
        {
            S[k] = mm;  // == m[k]
            ++mm;
            M[k+1] = mm;
        }

        return true;
    }

    void print_M(const char *bla, bool dfz=false)  const  // debug
    // If dfz is true then Dots are printed For Zeros.
    { print_vec(bla, M, n, dfz); }

    void print(const char *bla, bool dfz=false)  const
    // If dfz is true then Dots are printed For Zeros.
    { print_vec(bla, data(), n, dfz); }

    void print_sets(const char *bla, ulong off=1)  const
    { print_setpart(bla, data(), n, num_sets(), off); }


    bool OK()  const
    {
        if ( ! is_setpart_rgs( data(), n) )  return false;

        if ( n != 0 )  // prefix maxima in m[0,1,2,...,n-1] OK?
        {
            ulong mx1 = 0;
            for (ulong j=0; j<n; ++j)
            {
                mx1 += ( S[j] >= mx1 );
                const ulong mj = M[j+1];
                if ( mj != mx1 )  return false;
            }
        }

        return true;
    }
};
// -------------------------


#endif  // !defined HAVE_SETPART_RGS_LEX_H__
