#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 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.
{
public:
    ulong n_;    // Number of elements of set (set = {1,2,3,...,n})
    ulong *mxp_;   // m[k] = max(s[0], s[1], ..., s[k-1]) + 1
    ulong *rgs_;   // RGS

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

public:
    explicit setpart_rgs_lex(ulong n)
    {
        n_ = n;
        mxp_ = new ulong[n_+1];
        mxp_[0] = 1;    // sentinel:  m[0] = 1
        rgs_ = new ulong[n_];
        first();
    }

    ~setpart_rgs_lex()
    {
        delete [] mxp_;
        delete [] rgs_;
    }

    void first()
    {
        for (ulong k=0; k<n_; ++k)  rgs_[k] = 0;
        for (ulong k=1; k<=n_; ++k)  mxp_[k] = 1;
    }

    void last()
    {
        for (ulong k=0; k<n_; ++k)  rgs_[k] = k;
        for (ulong k=1; k<=n_; ++k)  mxp_[k] = k;
    }



    bool next()
    {
        if ( mxp_[n_] >= n_ )  return false;

        ulong k = n_;
        do  { --k; }  while ( (rgs_[k] + 1) > mxp_[k] );  // may read sentinel

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

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

        while ( ++k < n_ )  // fill tail with zeros
        {
            rgs_[k] = 0;
            mxp_[k+1] = mm;
        }

        return true;
    }

    bool prev()
    {
        if ( mxp_[n_] == 1 )  return false;

        ulong k = n_;
        do  { --k; }  while ( rgs_[k]==0 );

        rgs_[k] -= 1;
        ulong mm = mxp_[k+1] = max2(mxp_[k], rgs_[k]+1);

        while ( ++k < n_ )
        {
            rgs_[k] = mm;  // == m[k]
            ++mm;
            mxp_[k+1] = mm;
        }

        return true;
    }

    const ulong* data()  const  { return rgs_; }
    ulong num_sets()  const  { return ( n_ ? mxp_[n_] : 0 ); }


    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 += ( rgs_[j] >= mx1 );
                const ulong mj = mxp_[j+1];
                if ( mj != mx1 )  return false;
            }
        }

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


#endif  // !defined HAVE_SETPART_RGS_LEX_H__
