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

#include "comb/mixedradix-aux.h"
#include "comb/is-mixedradix-num.h"
#include "comb/comb-print.h"

#include "fxttypes.h"

//#include "jjassert.h"


class mixedradix_fixed_content_lex
// Mixed radix counting, fixed content.
// Equivalently, subsets of a multiset with a fixed number of elements.
// Lexicographic order.
{
protected:
    ulong *a_;   // digits
    ulong *m1_;  // nines (radix minus one) for each digit
    ulong n_;    // Number of digits
    ulong c_;    // Content; number of elements of multiset
    ulong j_;    // leftmost position of last change

private:  // have pointer data
    mixedradix_fixed_content_lex(const mixedradix_fixed_content_lex&) = delete;
    mixedradix_fixed_content_lex & operator = (const mixedradix_fixed_content_lex&) = delete;

public:
    explicit mixedradix_fixed_content_lex(ulong n,
                                          ulong c,
                                          ulong mm, const ulong *m = nullptr)
    {
        n_ = n;
        c_ = c;
        a_ = new ulong[n_+1+(n_==0)];
        m1_ = new ulong[n_+1+(n_==0)];

        a_[0] = 1;   // sentinel: !=0, and !=m1[n]
        m1_[0] = 2;  // sentinel

        mixedradix_init(n_, mm, m, m1_+1);

        first();
    }

    ~mixedradix_fixed_content_lex()
    {
        delete [] m1_;
        delete [] a_;
    }

    const ulong * data()  const  { return a_ + 1; }
    const ulong * nines()  const  { return m1_; }
    ulong num_digits()  const  { return n_; }

private:
    void write_tail(ulong st, ulong r)
    // Fill tail starting at st with lex-min word with content r.
    {
        ulong j = n_;
        while ( r != 0 )
        {
            const ulong m = m1_[j];
            if ( r >= m )
            {
                a_[j] = m;
                r -= m;
            }
            else
            {
                a_[j] = r;
                r = 0;
            }
            --j;
        }
        while ( j >= st )  { a_[j] = 0; --j; }
    }
public:
    bool first()
    {
        ulong s = 0;  // max. sum of digits
        for (ulong j=1; j <= n_; ++j)  { s += m1_[j]; }
        if ( c_ > s )  { return false; }  // content to large
        write_tail( 1, c_ );
        j_ = 1;
        return true;
    }

    bool next()
    {
        if ( c_ == 0 )  { return false; }

        ulong r = 0;  // content of tail
        ulong j = n_;
        while ( r == 0 )  // while nothing to add
        {
            r += a_[j];
            a_[j] = 0;
            --j;
        }

//        jjassert( r != 0 );

        while ( a_[j] == m1_[j] )  // while cannot add; can read sentinels
        {
            r += a_[j];
            a_[j] = 0;
            --j;
        }

//        jjassert( (long)j >= 0 );

        j_ = j;
        if ( j==0 )  return false;  // current is last

        ++a_[j];
        write_tail( j + 1, r - 1 );
        return true;
    }

    ulong pos()  const  { return j_ - 1; }  // position of last change

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

    void print_nines(const char *bla)  const
    { print_mixedradix(bla, m1_+1, n_, false); }

    ulong to_num()  const
    // Return (integer) value of mixed radix number.
    { return mixedradix2num(data(), m1_, n_); }

    bool OK()  const
    {
        if ( ! is_mixedradix_num(data(), n_, m1_) )  return false;
        return true;
    }
};
// -------------------------


#endif  // !defined HAVE_MIXEDRADIX_FIXED_CONTENT_LEX_H__
