
// demo-is-self-contained

#include "comb/comb-print.h"
#include "fxtio.h"
#include "demo/nextarg.h"

//% Minimal-change order for combinations with k>=2 elements
//% Good performance for small k

// Code adapted from:
//  Clement W.\ H.\ Lam, Leonard H.\ Soicher:
//  "Three new combination algorithms with the minimal change property",
//  Communications of the ACM, vol.25, no.8, pp.555-559, August-1982.

//#define TIMING // uncomment to disable printing

ulong ct = 0;
void Print(const ulong *a, ulong n, ulong k)
{
    ++ct;
#ifndef TIMING
    cout << setw(3) << ct << ":  ";
    print_set("    ", a+1, k);
    print_set1_as_deltaset("    ", a+1, k, n);
    cout << endl;
#endif // TIMING
}
// -------------------------

#define PROCESS Print(a, n, k)

int main(int argc, char **argv)
{
    ulong top = 0; //(* clear the stack *)
    ulong n = 7;
    NXARG(n,"Combinations (n choose k)");
    ulong k = 3;
    NXARG(k,"");
    ulong a[k+1];  // one-based
    ulong t[k+1];


    if ( k % 2 == 0  )
    {
        a [ k + 1 ] = n + 1; a[k] = k;
        if ( k < n  ) top = k;
    }
    else
    {
        a[k] = n;
        if ( k<n ) top=k-1;
    }

    a[1] = 1;
    t[k] = 0;
    ulong i;
    for (i=2; i<k; ++i)  { a[i] = i;  t[i] = i + 1; }

    PROCESS; //(* first combination *)
    //(* main loop to generate all other combinations *)
    while ( top != 0 )
    {
        if ( top == 2  )
        { //(* special handling for a[2] and a[1] *)
            top = t[2];  t[2] = 3;
            do
            {
                a[1] = a[2];  a[2] = a[2] + 1;
                PROCESS;
                do
                {
                    a[1] = a[1] - 1;
                    PROCESS;
                }
                while ( ! ( a[1] == 1 ) );
            }
            while ( ! ( a[2] == a[3] - 1) );
        }
        else
        {
            if ( top % 2 == 0  )
            {
                a[top-1] = a[top];  a[top] = a[top] + 1;
                if ( a[top] == a[top+1] - 1  )
                {
                    t[top-1] = t[top];  t[top] = top + 1;
                }
                top = top - 2;
            }
            else
            {
                a[top] = a[top]-1;
                if ( a[top] > top  )
                {
                    top = top-1;  a[top] = top;
                }
                else
                {
                    a[top-1] = top-1;  i = top;
                    top = t[top];  t[i] = i + 1;
                }
            }
            PROCESS;
        }
    }

    cout << "ct=" << ct << endl;
}
// -------------------------

/*
Timing:
% time ./bin 32 20
binomial(32, 20)=225792840
ct=225792840
./bin 32 20  1.49s user 0.00s system 99% cpu 1.494 total
 ==> 225792840/1.49 == 151,538,818 objects per second

% time ./bin 32 12
ct=225792840
./bin 32 12  1.17s user 0.00s system 99% cpu 1.172 total
 ==> 225792840/1.17 == 192,985,333 objects per second

% time ./bin 64 7
ct=621216192
./bin 64 7  2.44s user 0.00s system 99% cpu 2.448 total
 ==> 621216192/2.44 == 254,596,800
*/


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