
#include "bits/graycode.h"
#include "bits/parity.h"

#include "aux0/swap.h"
#include "fxtio.h"
#include "demo/nextarg.h"
#include "fxttypes.h"

#include "jjassert.h"

//% Multiplication table with hypercomplex numbers (Cayley-Dickson construction)


//#define TIMING // uncomment to disable printing

//#define USE_REC // define to use recursive version

//#define USE_REC_OPT // define to use optimization with recursive version
// note: makes routine slower with large n

int CD_sign_rec(ulong r, ulong c, ulong n)
// Signs in the multiplication table for the
//   algebra of n-ions (where n is a power of 2)
//   that is obtained by the Cayley-Dickson construction:
// If component r is multiplied with component c, then the
//   result is CD_sign_rec(r,c,n) * (r XOR c).
// Multiplication rule is
//   (a,b)*(A,b) = (a*A - B*conj(b),  conj(a)*B + A*b)
//   where conj(a,b) := (conj(a), -b) and conj(x):=x for x real
// [ Transposed rule/table is obtained if rule is changed to
//    (a,b)*(A,b) = (a*A - conj(B)*b,  b*conj(A) + B*a)  ]
// Must have: r<n, c<n.
//  ex*ey == -ey*ex (unless one of ex and ey is e0)
// Example (octionions, n==8):
// (er*ec) e0   e1   e2   e3    e4   e5   e6   e7
//   e0:  +e0  +e1  +e2  +e3   +e4  +e5  +e6  +e7
//   e1:  +e1  -e0  -e3  +e2   -e5  +e4  +e7  -e6
//   e2:  +e2  +e3  -e0  -e1   -e6  -e7  +e4  +e5
//   e3:  +e3  -e2  +e1  -e0   -e7  +e6  -e5  +e4
//
//   e4:  +e4  +e5  +e6  +e7   -e0  -e1  -e2  -e3
//   e5:  +e5  -e4  +e7  -e6   +e1  -e0  +e3  -e2
//   e6:  +e6  -e7  -e4  +e5   +e2  -e3  -e0  +e1
//   e7:  +e7  +e6  -e5  -e4   +e3  +e2  -e1  -e0
// Signs at row r, column c equal CD_sign_rec(r,c,8):
//   + + + + + + + +
//   + - - + - + + -
//   + + - - - - + +
//   + - + - - + - +
//   + + + + - - - -
//   + - + - + - + -
//   + - - + + - - +
//   + + - - + + - -
// This is a (8 x 8) Hadamard matrix.
// The second row is the (signed) Thue-Morse sequence.
// We have: er*ec = -ec*er iff er!=e0 and ec!=e0
{
    if ( (r==0) || (c==0) )  return +1;

#ifdef USE_REC_OPT
    if ( n<=8 )
    {
        unsigned long long t = 0xcc66aaf05a3c9600ULL;
        t >>= (r*8+c);
        t &= 1ULL;
        t *= 2;
        return  1 - (int)t;
    }
#endif // USE_REC_OPT

    if ( c>=r )
    {
        if ( c>r )   return  -CD_sign_rec(c, r, n);
        else  return -1;  // r==c
    }
    // here r>c (triangle below diagonal)

    ulong h = n>>1;
    if ( c>=h )  // right
    {
        // (upper right not reached)
        return   CD_sign_rec(c-h, r-h, h); // lower right
    }
    else  // left
    {
        if ( r>=h )  return   CD_sign_rec(c, r-h, h); // lower left
        else         return   CD_sign_rec(r, c, h); // upper left
    }
}
// -------------------------

//void gen_table()
//// Create 64-bit table for octonions.
//// Output is 0xcc66aaf05a3c9600ULL
//{
//    unsigned long long t = 0;
//    for (ulong r=0; r<8; ++r)
//    {
//        for (ulong c=0; c<8; ++c)
//        {
//            int s = CD_sign_rec(r, c, 8);
//            if ( s==-1 )  t |= (1ULL << (r*8+c));
//        }
//    }
//    cout << "unsigned long long t = 0x" << hex << t << dec << "ULL;" << endl;
//    // unsigned long long t = 0xcc66aaf05a3c9600ULL;
//}
//// -------------------------


inline void cp2(ulong a, ulong b, ulong &u, ulong &v)  { u=a; v=b; }
//
inline int CD_sign_it(ulong r, ulong c, ulong n)
{
    int s = +1;
    while ( true )
    {
        if ( (r==0) || (c==0) )  return s;
        if ( c==r )  return -s;
        if ( c>r )   { swap2(r,c); s=-s; }
        n >>= 1;
        if ( c>=n )  cp2(c-n, r-n, r, c);
        else if ( r>=n )  cp2(c, r-n, r, c);
    }
}
// -------------------------


#ifdef USE_REC
#define CD_sign CD_sign_rec
#else
#define CD_sign CD_sign_it
#endif

int
main(int argc, char **argv)
{
//    gen_table();

    ulong ldn = 5;
    NXARG(ldn, "Multiplication table for (2**ldn)-ions");
    ulong n = 1UL << ldn;
    bool q = 0;
    NXARG(q, "Whether to include indices of components");
    // Note: with TIMING  q==1 ==> iterative routine, else use recursion

#ifdef TIMING

    int x = 0;
    if ( q )
    {
        cout << "iterative routine:" << endl;
        for (ulong r=0; r<n; ++r)
            for (ulong c=0; c<n; ++c) { x ^= CD_sign_it(r, c, n); }
    }
    else
    {
        cout << "recursive routine:" << endl;
        for (ulong r=0; r<n; ++r)
            for (ulong c=0; c<n; ++c) { x ^= CD_sign_rec(r, c, n); }
    }
    return x;

#else

    for (ulong r=0; r<n; ++r)
    {
        cout << setw(3) << r << ":  ";
        for (ulong c=0; c<n; ++c)
        {
            int s = CD_sign_it(r, c, n);
            if ( 0==q )  cout << " " << (s<0 ? '-' : '+');
            else
            {
                cout << setw(3) << (r^c) << (s<0 ? '-' : '+');
            }

            jjassert( s == CD_sign_rec(r,c,n) );
        }
        cout << endl;
    }

    return 0;
#endif
}
// -------------------------

/*

Timing:

 time ./bin 12 1
iterative routine:
./bin 12 1  1.46s user 0.00s system 99% cpu 1.464 total
 ==> 2^24/1.46  == 11,491,243 per second

 time ./bin 12 0
recursive routine:
./bin 12 0  1.88s user 0.01s system 99% cpu 1.884 total
 ==> 2^24/1.88  == 8,924,051 per second


time ./bin 13 1
iterative routine:
./bin 13 1  6.45s user 0.01s system 99% cpu 6.462 total
 ==> 2^26/6.45  == 10,404,475 per second

 time ./bin 13 0
./bin 13 0  8.30s user 0.02s system 100% cpu 8.316 total
 ==> 2^26/8.30  == 8,085,405 per second


 time ./bin 14 1
iterative routine:
./bin 14 1  28.26s user 0.03s system 100% cpu 28.288 total
 ==> 2^28/28.26  == 9,498,777 per second

 time ./bin 14 0
recursive routine:
./bin 14 0  36.38s user 0.06s system 99% cpu 36.439 total
 ==> 2^28/36.38  == 7,378,654 per second


BENCHARGS=13 1
BENCHARGS=13 0

*/


/// Emacs:
/// Local Variables:
/// MyRelDir: "demo/arith"
/// makefile-dir: "../../"
/// make-target: "1demo DSRC=demo/arith/cayley-dickson-demo.cc"
/// make-target2: "1demo DSRC=demo/arith/cayley-dickson-demo.cc DEMOFLAGS=-DTIMING"
/// End:

