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


#include "comb/is-setpart-rgs.h"
#include "comb/comb-print.h"

#include "fxttypes.h"

//#include "fxtio.h"
//#include "jjassert.h"


class setpart_rgs_fixed_content_vec
// Set partitions whose restricted growth string (RGS) has fixed content-vector.
// For example, the content-vector [3, 2, 3] corresponds to the set partitions
//  of set with 8 elements into subsets of sizes 3, 2, 3 (in that order).
// Lexicographic order.
{
public:
    ulong n_;    // Number of elements of set (set = {1,2,3,...,n})
    ulong *rgs_;   // RGS
    ulong *mxp_;   // m[k] = max(s[0], s[1], ..., s[k-1]) + 1
    ulong *tailc_;   // content in tail just processed
    ulong *content_;   // content: must have c[j] != 0 for j < nc and sum(j=0..nc-1, c[j]) == n
    // rgs[] will contain content[j] elements j, for 0 <= j <= nc
    ulong nc_;   // first nc entries in content[] are used

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

public:
    explicit setpart_rgs_fixed_content_vec(ulong n)
    // Must have n >= 1.
    // Must call set_content(c,nc) or first(c,nc) before usage!
    {
        n_ = n;
        mxp_ = new ulong[n_+1];
        mxp_[0] = 0;
        content_ = new ulong[n_];
        nc_ = 0;
        tailc_ = new ulong[n_];
        rgs_ = new ulong[n_];
    }

    ~setpart_rgs_fixed_content_vec()
    {
        delete [] rgs_;
        delete [] mxp_;
        delete [] tailc_;
        delete [] content_;
    }

private:
    void set_mxp()
    {
        ulong mx1 = 0;
        for (ulong j=0; j<n_; ++j)
        {
            mx1 += ( rgs_[j] >= mx1 );
            mxp_[j+1] = mx1;
        }
    }

    void set_mxp(ulong j0)
    // Fix maxp[] starting from index j0.
    {
        ulong mx1 = ( j0==0 ? 0 : mxp_[j0] );
        for (ulong j=j0; j<n_; ++j)
        {
            mx1 += ( rgs_[j] >= mx1 );
            mxp_[j+1] = mx1;
        }
    }

//    void print_tc()  const
//    {
//        cerr << "tailc[] = ";
//        for (ulong j=0; j<nc_; ++j)
//            cerr << " " << tailc_[j];
//        cerr << endl;
//    }

public:
    bool set_content(const ulong *c, ulong nc)
    // Set content.
    // Must have nc >= 1.
    // c[] must be a composition of n, that is,
    // must have c[j] != 0 for j < nc and sum(j=0..nc-1, c[j]) == n
    {
        nc_ = nc;
        ulong s = 0;
        for (ulong j=0; j<nc_; ++j)
        {
            const ulong cj = c[j];
            if ( cj==0 )  return false;
            content_[j] = cj;
            s += cj;
        }

        if ( s != n_ )  return false;

        return first();
    }

    bool first(const ulong *c, ulong nc)
    {
        if ( ! set_content(c, nc) )  return false;
        return first();
    }

    bool first()
    {
        for (ulong j=0, wr = 0; j<nc_; ++j)
        {
            const ulong cj = content_[j];
            for (ulong i=0; i<cj; ++i)
            {
                rgs_[wr] = j;
                ++wr;
            }
        }

        for (ulong j=0; j<n_; ++j)  tailc_[j] = 0;

        set_mxp();
        return true;
    }



    bool next()
    {
        ulong k = n_;
        // find rightmost step up
        while  ( --k )
        {
            const ulong rk = rgs_[k];
            const ulong rk1 = rgs_[k-1];
            if ( rk > rk1 )
                if ( rk1 + 1 <= mxp_[k-1] )
                    break;

            tailc_[ rgs_[k] ] += 1;  // gather content of tail
        }
        // tailc[] reflect contents starting at index k + 1
        // leftmost change will be at [k-1]
        const ulong k1 = k - 1;


        // last rgs[] starts [ 0 1 2 3 ...]
        if ( rgs_[k] == k )  return false;
        // also works for all-zero rgs[], because we stop at k==0


#if 1
        if ( k == n_ - 1 )  // easy case: swap last two elements
        {
            const ulong rk1 = rgs_[k-1];
            const ulong rk = rgs_[k];
            rgs_[k] = rk1;
            rgs_[k-1] = rk;

            set_mxp(k-1);

            // tailc[] still all-zero
            return true;
        }
#endif


        const ulong rk1 = rgs_[k-1];
        ulong rk1n = rk1 + 1;  // min new value for rgs[k-1]

        const ulong rk = rgs_[k];
        tailc_[rk] += 1;  // save rgs[k]
        tailc_[rk1] += 1;  // save rgs[k-1]

        while ( true )  // find min value > rgs[k-1] in tail
        {
//            jjassert( rk1n < nc_ );
            if ( tailc_[rk1n] != 0 )  { break; }
            rk1n += 1;
        }
        rgs_[k-1] = rk1n;
        tailc_[rk1n] -= 1;  // remove rk1n

        ulong mi = 0;  // minimum in tail
        while ( tailc_[mi] == 0 )  mi += 1;
        rgs_[k] = mi;
        tailc_[mi] -= 1;  // remove mi


        // fill tail with content in tailc_[]:
        ulong rd = 0;  // read position in tailc[] == element to fill in
        while ( ++k < n_ )
        {
            while ( tailc_[rd] == 0 )  ++rd;  // skip empty bins
            rgs_[k] = rd;
            tailc_[rd] -= 1;
        }

        set_mxp(k1);
        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
    {
        // content[] OK?
        if ( nc_ == 0 )  return false;  // set_content() never called
        ulong s = 0;
        for (ulong j=0; j<nc_; ++j)
        {
            if ( content_[j] == 0 )  return false;
            s += content_[j];
        }
        if ( s != n_ )  return false;

        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;
            }
        }

        // tailc[] OK (all-zero)?
        for (ulong j=0; j<n_; ++j)
            if ( tailc_[j] != 0 )  return false;

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


#endif  // !defined HAVE_SETPART_RGS_FIXED_CONTENT_VEC_H__
