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


//#include "ds/bitarray.h"
class bitarray;
//#include "ds/left-rightarray.h"
class left_right_array;


#include "fxttypes.h"


static inline bool perm_is_identity(const ulong *f, ulong n)
// Return whether f[] is the identical permutation,
// that is, whether f[k]==k for all k= 0...n-1
{
    for (ulong k=0; k<n; ++k)  if ( f[k] != k )  return false;
    return true;
}
// -------------------------


static inline bool perm_has_fixed_points(const ulong *f, ulong n)
// Return whether f[k]==k for any k= 0...n-1
{
    for (ulong k=0; k<n; ++k)  if ( f[k] == k )  return true;
    return false;
}
// -------------------------


static inline ulong perm_count_fixed_points(const ulong *f, ulong n)
// Return number of fixed points in f[]
{
    ulong ct = 0;
    for (ulong k=0; k<n; ++k)  ct += ( f[k] == k );
    return ct;
}
// -------------------------


static inline ulong perm_count_descents(const ulong *f, ulong n)
// Return number of descents in f[]
{
    if ( n<=1 ) return 0;
    ulong ct = 0;
    ulong vl = f[0],  v;
    for (ulong k=1; k<n; ++k)
    {
        v = f[k];
        ct += ( v < vl );
        vl = v;
    }
    return ct;
}
// -------------------------


static inline ulong perm_count_excedances(const ulong *f, ulong n, bool eq=true)
// Return number of excedances in f[]:
// an excedance is a position k where f[k]>k.
// If eq==false then return number of k such that f[k]<k
{
    ulong ct = 0;
    if ( eq )  for (ulong k=0; k<n; ++k)  ct += ( f[k] > k );
    else       for (ulong k=0; k<n; ++k)  ct += ( f[k] < k );
    return ct;
}
// -------------------------


static inline ulong perm_count_displacements(const ulong *f, ulong n)
// Return number of elements f[j] != j.
{
    ulong ct = 0;
    for (ulong j=0; j<n; ++j)  { ct += ( f[j] != j ); }
    return ct;
}
// -------------------------


static inline ulong perm_count_right_displacements(const ulong *f, ulong n)  // A369376
{
    ulong ni = 0;
    for (ulong j=0; j<n; ++j)  { if ( f[j] > j )  ni += 1; }
    return ni;
}
// -------------------------


static inline ulong perm_count_left_displacements(const ulong *f, ulong n)  // A369376
{
    ulong ni = 0;
    for (ulong j=0; j<n; ++j)  { if ( f[j] < j )  ni += 1; }
    return ni;
}
// -------------------------


static inline bool perm_is_derangement(const ulong *f, ulong n)
// Return whether f[] is a derangement of identity,
// that is, whether f[k]!=k for all k
{
    for (ulong k=0; k<n; ++k)  if ( f[k] == k )  return false;
    return true;
}
// -------------------------


static inline bool perm_is_derangement(const ulong *f, const ulong *g, ulong n)
// Return whether f[] is a derangement of g[],
// that is, whether f[k]!=g[k] for all k
{
    for (ulong k=0; k<n; ++k)  if ( f[k] == g[k] )  return false;
    return true;
}
// -------------------------


static inline bool perm_is_connected(const ulong *f, ulong n)
// Return whether permutation is connected (indecomposable),
// that is, whether {f[0],f[1],...,f[k]} != {0,...,k} for all k<n-1.
// That is, whether no proper prefix of the array is mapped to itself.
{
    if ( n <= 1 )  return true;
    ulong m = 0;  // maximum
    for (ulong k=0; k<n-1; ++k)  // for all proper prefixes
    {
        const ulong fk = f[k];
        if ( fk > m )  m = fk;
        if ( m <= k )  return false;
    }
    return true;
}
// -------------------------


static inline bool perm_is_updown_permutation(const ulong *f, ulong n)
// Return whether f is an up-down condition,
// that is, whether f[0] < f[1] > f[2] < f[3] > ...
{
    if ( n<2 )  return true;

    for (ulong k=1; k<n; k+=2)  { if ( f[k] <= f[k-1] ) return false; }
    for (ulong k=2; k<n; k+=2)  { if ( f[k] >= f[k-1] ) return false; }

    return true;
}
// -------------------------


static inline bool perm_is_cyclic(const ulong *f, ulong n)
// Return whether permutation is exactly one cycle.
{
    if ( n<=1 )  return true;
    ulong k = 0,  e = 0;
    do  { e=f[e]; ++k; }  while ( e != 0 );
    return  ( k == n );
}
// -------------------------


static inline bool perm_is_involution(const ulong *f, ulong n, bool hint=false)
// Return whether max cycle length is <= 2,
// that is, whether f * f = id.
// Set hint to true if the permutation is expected to be
// an involution
{
    if ( hint )
    {
        ulong z = 0;
        for (ulong k=0; k<n; ++k)  z |= (f[f[k]] ^ k);
        return  ( 0 == z );
    }
    else
    {
        for (ulong k=0; k<n; ++k)  if ( f[f[k]] != k )  return false;
        return true;
    }
}
// -------------------------


static inline bool perm_is_inverse(const ulong *f, const ulong *g, ulong n, bool hint=false)
// Return whether f[] is the inverse of g[].
// Set hint to true if the permutations are expected to be
// mutual inverses.
{
    if ( hint )
    {
        ulong z = 0;
        for (ulong k=0; k<n; ++k)  z |= (f[g[k]] ^ k);
        return  (0==z);
    }
    else
    {
        for (ulong k=0; k<n; ++k)  if ( f[g[k]] != k )  return false;
        return true;
    }
}
// -------------------------


static inline bool perm_is_square(const ulong *f, const ulong *g, ulong n)
// Return whether f * f == g  as a permutation
{
    for (ulong k=0; k<n; ++k)  if ( g[k] != f[f[k]] )  return false;
    return true;
}
// -------------------------


static inline ulong perm_get_index(const ulong *f, ulong n)
// Return index of permutation.
{
    ulong i = 0;
    for (ulong k=1; k<n; ++k)  if ( f[k-1]>f[k] )  i += k;
    return i;
}
// -------------------------


static inline ulong perm_major_index(const ulong *f, ulong n)
{
    if ( n<=1 )  return 0;
    ulong s = 0;
    --n;
    for (ulong j=0; j<n; ++j)
        if ( f[j] > f[j+1] )  s += (j+1);
    return s;
}
// -------------------------


// perm/permq.cc:
ulong perm_count_inversions(const ulong *f, ulong n);
ulong perm_count_inversions(const ulong *f, ulong n, left_right_array *tLR);
bool perm_is_valid(const ulong *f, ulong n, bitarray *bp=nullptr);
ulong perm_count_transpositions(const ulong *f, ulong n, bitarray *bp=nullptr);
ulong perm_get_parity(const ulong *f, ulong n, bitarray *bp=nullptr);
ulong perm_count_cycles(const ulong *f, ulong n, bitarray *bp=nullptr);
bool perm_is_simple(const ulong *f, ulong n);
//ulong perm_get_periods(const ulong *f, ulong n, ulong *p, bitarray *bp=nullptr);


//: functions for the application of permutations to data
//: are found in perm/permapply.h

//: functions for random permutations
//: are found in perm/permrand.h

#endif  // !defined HAVE_PERMQ_H__
