#if !defined  HAVE_NECKLACE_MOD_COLOR_H__
#define       HAVE_NECKLACE_MOD_COLOR_H__
// This file is part of the FXT library.
// Copyright (C) 2025 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/cyclic-words.h"  // is_cyclic_lex_min()
#include "comb/lyndon-factorization.h"  // is_lyndon_word()

#include "fxtio.h"

#include "fxttypes.h"
//#include "bits/bitsperlong.h"

// If defined, arrays are used instead of pointers, this gives a small speedup:
//#define NECKLACE_MOD_COLOR_MAX_ARRAY_LEN 96  // default off

class necklace_mod_color
// Generate necklaces, iterative algorithm.
// Necklaces are such that a number k in the necklace is
//   preceded by all numbers 0, 1, 2, ..., k-1.
// In other words, the underlying equivalence relation additionally
//   contains all permutations of colors (numbers).
{
public:
#if !defined NECKLACE_MOD_COLOR_MAX_ARRAY_LEN
    ulong *a_;  // the string, NOTE: one-based
    ulong *mm_;  // mm[j] == max number for a[j]
    ulong *dv_;  // delta sequence of divisors of n
#else
    ulong a_[NECKLACE_MOD_COLOR_MAX_ARRAY_LEN + 1];
    ulong mm_[NECKLACE_MOD_COLOR_MAX_ARRAY_LEN + 2];
    ulong dv_[NECKLACE_MOD_COLOR_MAX_ARRAY_LEN + 1];
#endif
    ulong n_;   // length of strings
    ulong m1_;  // m-ary strings, m1=m-1
    ulong j_;   // period of the word (if necklaces)

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

public:
    explicit necklace_mod_color(ulong n, ulong m=-1UL)
    {
        n_ = ( n ? n : 1 );  // at least 1
        m1_ = ( m>1 ? m-1 : 1 );  // at least 2
#if !defined NECKLACE_MOD_COLOR_MAX_ARRAY_LEN
        a_ = new ulong[n_+1];
        mm_ = new ulong[n_+2];  // last element is write-only
        dv_ = new ulong[n_+1];
#endif
        for (ulong j=1; j<=n; ++j)  dv_[j] = ( 0==(n_%j ) );  // divisors
        first();
    }

    ~necklace_mod_color()
    {
#if !defined NECKLACE_MOD_COLOR_MAX_ARRAY_LEN
        delete [] a_;
        delete [] mm_;
        delete [] dv_;
#endif
    }

    void first()
    {
        for (ulong j=0; j<=n_; ++j)  a_[j] = 0;
        mm_[0] = 1;  // sentinel
        mm_[1] = 0;
        for (ulong j=2; j<=n_; ++j)  mm_[j] = 1;
        j_ = 1;
    }

    const ulong * data()  const  { return  a_ + 1; }

    ulong next_pre()  // next pre-necklace
    // return j (zero when finished)
    {
        // Find rightmost digit that can be incremented:
        ulong j = n_;
        while ( a_[j] == mm_[j] )  { --j; }

        if ( 0==j_ )   return 0;  // last

        // Increment:
        a_[j] += 1;

        // Adjust mm[]:
        ulong z = mm_[j] + ( a_[j] == mm_[j] );
        z -= ( z > m1_ );
        for (ulong k=j+1; k<=n_; ++k)  mm_[k] = z;

        // Copy periodically:
        for (ulong k=j+1; k<=n_; ++k)  a_[k] = a_[k-j];

        j_ = j;
        return  j;
    }

    bool is_necklace()  const
    {
        return ( 0!=dv_[j_] );  // whether j divides n
    }

    bool is_lyndon_word()  const
    {
        return ( j_==n_ );  // whether j equals n
    }

    ulong next()  // next necklace
    {
        do
        {
            next_pre();
            if ( 0==j_ )  return 0;
        }
        while ( 0==dv_[j_] );  // until j divides n
        return j_;
    }

    ulong next_lyndon_word()  // next Lyndon word
    {
        do
        {
            next_pre();
            if ( 0==j_ )  return 0;
        }
        while ( j_==n_ );  // until j equals n
        return j_;  // == n
    }

    bool OK()  const
    {
        for (ulong k=0; k<=n_; ++k)  // a[k] <= mm[k]
            if ( a_[k] > mm_[k] )
                return false;

        for (ulong k=1; k<n_; ++k)  // mm[k+1] - mm[k] <= 1
            if ( mm_[k+1] -  mm_[k] > 1 )
                return false;

        if ( ! is_setpart_rgs( data(), n_ ) )  return false;

        if ( is_necklace() && ! is_cyclic_lex_min( data(), n_ ) )  return false;

        if ( is_necklace() &&  !( j_ == max_lyndon_prefix_len( data(), n_ )) )  return false;

        if ( is_lyndon_word() && ! ::is_lyndon_word( data(), n_ ) )  return false;

        return true;
    }

    void debug_print()  const
    {
        using std::cout;
        for (ulong k=1; k<=n_; ++k)
            cout << ' ' << mm_[k];
    }
};
// -------------------------

//#undef NECKLACE_MOD_COLOR_MAX_ARRAY_LEN  // better leave in


#endif  // !defined HAVE_NECKLACE_MOD_COLOR_H__
