#if !defined  HAVE_SIGNED_PERM_GRAY_H__
#define       HAVE_SIGNED_PERM_GRAY_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 "comb/perm-trotter.h"
#include "comb/ruler-func.h"
#include "aux0/swap.h"
#include "perm/permq.h"  // perm_parity()
#include "fxttypes.h"

#include <iostream>


class signed_perm_gray
// Signed permutations (hyperoctahedral group), Gray code.
// Every second signed permutation in the list is a pure rotation.
// There are n! * 2^n signed permutations of n elements.
// We flip a sign most often, the number of transpositions is minimal, as in
//  James F. Korsh, Paul S. LaFollette: A loopless Gray code for rooted trees,
//  ACM Transactions on Algorithms, vol.2, no.2, pp.135-152, (April-2006).
// An ordering with a transposition at every step is given in
//  Yuan Qiu, Aaron Williams: Generating Signed Permutations by
//  Twisting Two-Sided Ribbons, (2023), https://arxiv.org/abs/2311.06974
{
private:
    perm_trotter PR;  // permutation of coordinates
    ruler_func RF;  // for reflections
    bool * refl_q;    // refl_q[j] says whether coordinate j is reflected
    bool is_rot;    // whether signed permutation alone is a rotation
    ulong n;

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

public:
    signed_perm_gray(ulong tn)
        : PR(tn), RF(tn), n(tn)
    {
        refl_q = new bool[n];
        first();
    }
    ~signed_perm_gray()
    {
        delete [] refl_q;
    }

public:
    const ulong * perm_data()  const  { return PR.data(); }
    const ulong * perm_inv_data()  const  { return PR.invdata(); }
    const bool * refl_data()  const  { return refl_q; }

    bool is_rotation()  const
    // Return whether current is pure rotation
    { return is_rot; }

    void first()
    {
        RF.first();
        PR.first();
        for (ulong j=0; j<n; ++j)  { refl_q[j] = false; }
        is_rot = true;
    }

    bool next()
    {
        is_rot = ! is_rot;  // one coordinate (namely r) is reversed

        // next reflection:
        const ulong r = RF.next();
        if ( r < n )
        {
            refl_q[r] = ! refl_q[r];  // flip reflection for coordinate r
            return true;
        }
        // done all reflections

        // next permutation:
        if ( ! PR.next() )  return false;  // last perm

        ulong a, b;
        PR.get_swap( a, b );
        swap2( refl_q[a], refl_q[b] );
        return true;
    }

    bool next_rotation()
    // Go to next pure rotation.
    {
        if ( ! next() )  return false;
        if ( ! next() )  return false;
        return true;
    }


    void print(const char * bla, bool offset_one_q=false)  const
    {
        using std::cout;
        cout << bla;
        cout << "(";
        for (ulong j =0; j<n; ++j)
        {
            cout << ( refl_data()[j] ? '-' : '+' )
                 << perm_data()[j] + offset_one_q
                 << ( j+1 == n ? "" : ", ");
        }
        cout << ")";

//        cout << "    (";
//        for (ulong j =0; j<n; ++j)
//        {
//            cout << ( refl_data()[j] )
//                 << " "
//                 << perm_data()[j] + offset_one_q
//                 << ( j+1 == n ? "" : ",  ");
//        }
//        cout << ")";
    }

    bool OK()
    {
        ulong pf = 0;
        for (ulong j=0; j<n; ++j)  { pf ^= refl_q[j]; }
        const ulong pp = perm_get_parity( perm_data(), n );
        if ( ( pf ^ pp ) != ( ! is_rot ) )  return false;
        return true;
    }
};
// -------------------------

#endif // !defined HAVE_SIGNED_PERM_GRAY_H__
