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

#include "perm/rotate.h"

#include "fxttypes.h"
//#include "jjassert.h"

//#include <iostream>

class binary_rot
// Binary strings by rotations of prefixes.
// This is the "cooler" order from:
// Brett Stevens, Aaron Williams: The coolest order of binary strings,
// 6th International Conference on Fun with Algorithms (FUN 2012),
// San Servolo, Italy. LNCS 7288, pp.322-333, (2012).
{
private:
    ulong *b_;  // binary word
    ulong n_;  // number of bits
    ulong ct_;  //
    ulong ct_max_;
public:
    binary_rot(ulong n)
    {
        n_ = n;
        ct_max_ = 1UL << n_;
        b_ = new ulong[n_ + 2];
        // sentinels
        b_[n_+0] = 1;
        b_[n_+1] = 0;
        first();
    }

    ~binary_rot()
    {
        delete [] b_;
    }

    void first()
    {
        ct_ = 0;
        ulong j = 0;
        while ( j < n_ )  { b_[j] = 0;  ++j; }
    }

    const ulong * data()  { return b_; }

private:
    ulong find01()  const
    // return 0 if no [1,0] in word
    {
        ulong j = 1;
        while ( true )
        {
            while ( b_[j] == 0 )  { ++j; }
            // here b[j] == 1
            if ( b_[j+1] == 0 )
            {
                if ( j != n_ )  { return j; }
                else            { return 0; }
            }
            ++j;
        }
    }
public:
    bool next()
    {
        // Let j be the minimum value such that b[j],b[j+1] = 1,0 and j > 0.
        // If j exists, then rotate j + 2 bits.
        // Otherwise, flip b[0], and then rotate n bits.
        ++ct_;
        if ( ct_ >= ct_max_ )  { return false; }
        ulong j = find01();
        if ( j==0 )
        {
            b_[0] ^= 1;
            rotate_left1( b_, n_ );
//            std::cout << " :::::::::::::\n";
        }
        else
        {
            rotate_left1( b_, j+2 );
//            std::cout << " -------------\n";
        }

        return true;
    }

    ulong num_ones()  const
    {
        ulong h = 0;
        for (ulong j=0; j<n_; ++j)  { h += b_[j]; }
        return h;
    }
};
// -------------------------


#endif  // !defined HAVE_BINARY_ROT_H__
