

#include "comb/perm-restrpref.h" // class perm_restrpref

#include "perm/printcycles.h"
#include "comb/comb-print.h"
#include "perm/permq.h"

#include "comb/fact2perm.h"

#include "fxtiomanip.h"
#include "aux0/swap.h"
#include "fxttypes.h"

//#include "perm/permq.h" // is_involution(), is_connected()
//#include "jjassert.h"

#include "demo/nextarg.h" // NXARG()

//% Permutations with restricted prefixes

ulong *z;
ulong *fc;
//
void visit(const ulong *a, ulong n, ulong ct)
{
    cout << setw(4) << ct << ":";
    for (ulong i=0; i<n; ++i)  z[i] = a[i+1] - 1;
    print_perm("  ", z, n);
//    for (ulong i=1; i<=n; ++i)  cout << "  " << a[i];
    cout << "    ";
    perm2ffact(z, n, fc);
//    perm2rfact(z, n, fc);
//    perm2ffact_swp(z, n, fc);
//    perm2rfact_swp(z, n, fc);
    print_perm("  ", fc, n-1);
    cout << "    ";
    print_cycles(z,n);

//    ulong z[n];  for (ulong k=0; k<n; ++k)  z[k] = a[k+1]-1;
//    if ( is_cyclic(z, n) )  cout << "  Cyc";
//    if ( is_connected(z, n) )  cout << "  Ind";
//    if ( is_involution(z, n) )  cout << "  Inv";
//    if ( is_updown_permutation(z, n) )  cout << "  UpD";
//    if ( is_derangement(z, n) )  cout << "  Der";

    cout << endl;
}
// -------------------------


bool cond_0(const ulong *, ulong)
// trivial (empty) condition
{
    return true;
}
// -------------------------

bool cond_inv(const ulong *a, ulong k)
// involution condition
{
    ulong ak = a[k];
    if ( (ak<=k) && (a[ak]!=k) )  return false;
    return true;
//    return  ( a[a[k]]==k );  // cannot use this as permutation may be incomplete
}
// -------------------------

bool cond_updown(const ulong *a, ulong k)
// up-down condition: a1 < a2 > a3 < a4 > ...
{
//    if ( k<3 ) return true;
//    if ( a[k]>a[k-1] )   return ( a[k-1]<a[k-2] );
//    else                 return ( a[k-1]>a[k-2] );
    if ( k<2 ) return true;
    if ( (k%2) )  return ( a[k]<a[k-1] );
    else          return ( a[k]>a[k-1] );
}
// -------------------------

ulong N;  // set to n in main()
bool cond_indecomp(const ulong *a, ulong k)
// indecomposable condition: {a1,...,ak} != {1,...,k} for all k<n
{
    if ( k==N )  return true;
    for (ulong i=1; i<=k; ++i)  if ( a[i]>k )  return true;
    return false;
}
// -------------------------

bool cond_derange(const ulong *a, ulong k)
// derangement condition: f[k]!=k for all k
{
    return ( a[k] != k );
}
// -------------------------

bool cond_xx1(const ulong *a, ulong k)
// condition: f[k-1]!=f[k]-1 for all k
{
    if ( k==1 )  return true;
    return ( a[k-1] != a[k]-1 );  // must look backward
}
// -------------------------


bool cond_dist(const ulong *a, ulong k)
// condition: abs(f[k]-k) >= 2
{
    ulong ak = a[k];

//    if ( ak<=k )  return ( k-ak <= 1 );  else return true;  // A000079 Powers of 2

    const ulong d = ( ak>k ? ak-k : k-ak );

    return ( d>=2 );  // A001883: 1, 4, 29, 206, 1708, 15702, 159737, 1780696,

//    return ( d>=1 );  // derangements
//    return ( d<2 );  // A000045: Fibonacci numbers
//    return ( d<=2 );  // A002524: 1, 2, 6, 14, 31, 73, 172, 400, 932, 2177,
}
// -------------------------


int
main(int argc, char **argv)
{
    ulong n = 5;
    NXARG(n, "Permutations of {1,2,...,n}");
    z = new ulong[n];
    N = n;

    fc = new ulong[n];

    ulong c = 4;
    NXARG(c, "Condition function: "
          "\n      (0: empty), (1: involutions),"
          "\n      (2: up-down), (3: indecomp),"
          "\n      (4: derangement), (5: no [x,x+1])"
          "\n      (6: distance condition)"
          );

    perm_restrpref *perm;
    switch ( c )
    {
    case 0:
        cout << "All permutations:" << endl;
        perm = new perm_restrpref(n, cond_0, visit);  break;
    case 1:
        cout << "Involutions (self-inverse permutations):" << endl;
        perm = new perm_restrpref(n, cond_inv, visit);  break;
    case 2:
        cout << "Up-down permutations:" << endl;
        perm = new perm_restrpref(n, cond_updown, visit);  break;
    case 3:
        cout << "Connected (indecomposable) permutations:" << endl;
        perm = new perm_restrpref(n, cond_indecomp, visit);  break;
    case 4:
        cout << "Derangements:" << endl;
        perm = new perm_restrpref(n, cond_derange, visit);  break;
    case 5:
        cout << "No [x,x+1]:" << endl;
        perm = new perm_restrpref(n, cond_xx1, visit);  break;
    case 6:
        cout << "Distance <=2 :" << endl;
        perm = new perm_restrpref(n, cond_dist, visit);  break;
    default:
        cout << "Error!" << endl;
        return 1;
    }

    ulong ct = perm->all();
    cout << " ct = " << ct << endl;

    delete perm;
    delete [] z;
    delete [] fc;

    return 0;
}
// -------------------------

// involutions:
//   1 #perm = 1
//   2 #perm = 2
//   3 #perm = 4
//   4 #perm = 10
//   5 #perm = 26
//   6 #perm = 76
//   7 #perm = 232
//   8 #perm = 764
//   9 #perm = 2620
//  10 #perm = 9496
//  11 #perm = 35696
//  12 #perm = 140152
//  13 #perm = 568504

// up-down:
//   1 #perm = 1
//   2 #perm = 1
//   3 #perm = 2
//   4 #perm = 5
//   5 #perm = 16
//   6 #perm = 61
//   7 #perm = 272
//   8 #perm = 1385
//   9 #perm = 7936
//  10 #perm = 50521
//  11 #perm = 353792

// indecomp:
//   1 #perm = 1
//   2 #perm = 1
//   3 #perm = 3
//   4 #perm = 13
//   5 #perm = 71
//   6 #perm = 461
//   7 #perm = 3447
//   8 #perm = 29093
//   9 #perm = 273343
//  10 #perm = 2829325

// derangement:
//   1 #perm = 0
//   2 #perm = 1
//   3 #perm = 2
//   4 #perm = 9
//   5 #perm = 44
//   6 #perm = 265
//   7 #perm = 1854
//   8 #perm = 14833
//   9 #perm = 133496
//  10 #perm = 1334961


/// Emacs:
/// Local Variables:
/// MyRelDir: "demo/comb"
/// makefile-dir: "../../"
/// make-target: "1demo DSRC=demo/comb/perm-restrpref-demo.cc"
/// make-target2: "1demo DSRC=demo/comb/perm-restrpref-demo.cc DEMOFLAGS=-DTIMING"
/// End:

