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


#include "aux0/swap.h"
//#include "comb/endo-enup.h"
#include "comb/comb-print.h"
#include "fxttypes.h"


class perm_st_pref
// Permutations in single track order:
// all columns are cyclic shifts of the first column.
// Swaps of inverse permutation are done in prefix.
{
public:
    ulong *M;  // mixed radix digits with radix = [2, 3, 4, ..., n-1, (sentinel=-1)]
    ulong *P;  // permutation
    ulong *Pi;  // inverse permutation
    ulong n;   // permutations of n elements

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

public:
    explicit perm_st_pref(ulong tn)
    {
        n = ( tn == 0 ? 1 : tn );
        M = new ulong[n];
        P = new ulong[n];
        Pi = new ulong[n];
        M[n-1] = 0;  // sentinel
        first();
    }

    ~perm_st_pref()
    {
        delete [] P;
        delete [] Pi;
        delete [] M;
    }

    const ulong * data()  const  { return P; }
    const ulong * invdata()  const  { return Pi; }

    void first()
    {
        for (ulong k=0; k < n-1; ++k)  M[k] = 0;
        for (ulong k=0; k < n; ++k)  P[k] = Pi[k] = k;
    }

//    void first_x()
//    {
//        for (ulong k=0; k < n-1; ++k)  M[k] = 0;
//        for (ulong k=0, e=0;  k < n;  ++k)
//        {
//            P[k] = e;
//            Pi[e] = k;
//            e = next_enup(e, n-1);
//        }
//    }

    bool next()
    {
        // increment mixed radix number:
        ulong j = 0;
        while ( M[j] == j+1 )  { M[j] = 0;  ++j; }

        if ( j == n - 1 )  return false;  // current permutation is last
        ++M[j];

        // j:  swaps (elements in permutation, or positions in inverse permutation)
        // 0:  (0,1)
        // 1:  (1,2)
        // 2:  (0,1) (2,3)  // once every 3! = 6 times
        // 3:  (1,2) (3,4)
        // 4:  (0,1) (2,3) (4,5)  // once every 5! = 120 times
        // 5:  (1,2) (3,4) (5,6)
        // 6:  (0,1) (2,3) (4,5) (6,7)  // once every 7! = 5040 times
        // j:  (m, m+1) (m+2, m+3) ... (j, j+1)  where m = j%2
        // Average number of swaps is 1 + 1/3!*(1 + 1/5!*(1 + 1/7!*(...))) =
        // = 1.168055831129507...
        ulong e1 = j & 1,  e2 = e1 + 1;
        do
        {
            const ulong i1 = Pi[e1];  // position of element e1
            const ulong i2 = Pi[e2];  // position of element e2
            Pi[e1] = i2;
            Pi[e2] = i1;
            P[i1] = e2;
            P[i2] = e1;
            e1 += 2;  e2 += 2;
        }
        while ( e1 <= j );

        return true;
    }

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

    void print_inv(const char *bla, bool dfz=false)  const
    // If dfz is true then Dots are printed For Zeros.
    {
        print_perm(bla, invdata(), n, dfz);
    }
};
// -------------------------


#endif  // !defined HAVE_PERM_ST_PREF_H__
