
#include "comb/stringsubst.h"

#include "matrix/vector.h"
#include "matrix/matrix.h"

#include "fxtiomanip.h"
#include "jjassert.h"

#include "demo/nextarg.h"

//% Hilbert's (3-dimensional) space filling curve via string substitution


typedef vector<int> V3;
V3 Pos(3);

typedef matrix<int> M3;
M3 Dir(3);
M3 Rxp(3);  M3 Rxm(3);
M3 Ryp(3);  M3 Rym(3);
M3 Rzp(3);  M3 Rzm(3);


void
make_axis_rotation(int axisno, int phi, M3 &m)
// create matrix that rotates by phi*90 degree (phi == +-1)
// around axis axisno (== 0, 1 or 3)
{
    int c = 0;    // cos(phi) == 0
    int ps = phi; // sin(phi) == +-1
    int ms = -ps; // -sin(phi)
    int n = axisno;

    m[0][0] = m[1][1] = m[2][2] = c;
    m[1][0] = m[0][2] = m[2][1] = ms;
    m[0][1] = m[2][0] = m[1][2] = ps;

    m[0][n] = m[1][n] = m[2][n] = 0;
    m[n][0] = m[n][1] = m[n][2] = 0;

    m[n][n] = 1;
}
// -------------------------


void setup()
{
    Dir.unit();  // unit matrix
    Dir[1][1] = -1;  Dir[2][2] = -1;  // ugly fix
    make_axis_rotation(0, +1, Rxp);
    make_axis_rotation(0, -1, Rxm);
    make_axis_rotation(1, +1, Ryp);
    make_axis_rotation(1, -1, Rym);
    make_axis_rotation(2, +1, Rzp);
    make_axis_rotation(2, -1, Rzm);
}
// -------------------------

void print_spmat(const M3 &m)
{
    cout << " [ ";
    for (ulong r=0; r<3; ++r)
    {
        for (ulong c=0; c<3; ++c)
        {
            int x = m[r][c];
            if ( x )
            {
                cout << (x>0?'+':'-') << c << " ";
                break;
            }
        }
    }
    cout << "]";
}
// -------------------------


int
main(int argc, char **argv)
{
    ulong ldn = 2;
    NXARG(ldn, "Number of recursions, will create 8**ldn points");
    int cmax = (1<<(ldn*3)) + 4096;
    NXARG(cmax, "Max length of string");

    const char * const h3d_rules[] = {
        // recursion:
        "a", "^<aF^<aFa-F^>>aFavF+>>aFa-F>a->",
        // "draw":
        "F", "F",
        // rotations:
        "+", "+",  // +z
        "-", "-",  // -z
        "^", "^",  // +y
        "v", "v",  // -y
        "<", "<",  // +x
        ">", ">"   // -x
    };
    const char *start = "a";
    int nsym = sizeof(h3d_rules)/sizeof(char*)/2;
//    cout << "nsym=" << nsym << endl;

    string_subst strs(cmax, nsym, (char* const*)h3d_rules);  // jjcast
    if ( 0!=strs.verify(start) )  return 1;

    ulong ctc = 0;
    for (ulong j=0; j<=ldn; ++j)
    {
        ctc = strs.subst(j, start);
        cout << j << ": ";
        cout << "  (#=" << ctc << ")" << endl;
        if ( ctc<1000 )  cout << "  " << strs.string();
        cout << endl;
    }


    M3 Ri(3);  Ri.unit();

    setup();
    int x=0, y=0, z=0;
    int dx, dy, dz;
    ulong ct = 1;
    const char *c = strs.string();
    ulong t=0;
//    while ( (0!=c[t]) && (c[t]!='F') )  ++t;  // skip leading rotations (optional)
    for (  ; t<ctc; ++t)
    {
        switch ( c[t] )
        {
        case 'a': break;
        case 'F':
            dx = Dir[0][0];  dy = Dir[1][0];  dz = Dir[2][0];
            x += dx;  y += dy;  z += dz;

            if ( 0==(ct%8) )  cout << endl;
            cout << setw(4) << ct << ":";
            ++ct;
//            print_spmat(Dir);

            cout << "    dir=[";
            cout << setw(2) << dx << ", ";
            cout << setw(2) << dy << ", ";
            cout << setw(2) << dz << "]  ";
            if ( dx )  cout << (dx>0?'>':'<');
            if ( dy )  cout << (dy>0?'^':'v');
            if ( dz )  cout << (dz>0?'+':'-');

            cout << "    pos=[";
            cout << setw(2) << x << ", ";
            cout << setw(2) << y << ", ";
            cout << setw(2) << z << "]";

//            cout << "    pos%2=[";
//            cout << setw(2) << x%2 << ", ";
//            cout << setw(2) << y%2 << ", ";
//            cout << setw(2) << z%2 << "]";

            cout << endl;
            break;

#if 1
#define  CASE(s, R)  case s : Ri*=R; Dir*=R;  break;
#else
#define  CASE(s, R) \
 case s : Ri*=R; Dir*=R;  \
   cout << s << " "; print_spmat(R); print_spmat(Dir); cout << endl;  break;
#endif
        CASE( '+', Rzp);
        CASE( '-', Rzm);
        CASE( '^', Rym);
        CASE( 'v', Ryp);
        CASE( '<', Rxp);
        CASE( '>', Rxm);
        }
    }

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

/// Emacs:
/// Local Variables:
/// MyRelDir: "demo/ds"
/// makefile-dir: "../../"
/// make-target: "1demo DSRC=demo/ds/stringsubst-hilbert3d-demo.cc"
/// make-target2: "1demo DSRC=demo/ds/stringsubst-hilbert3d-demo.cc DEMOFLAGS=-DTIMING"
/// End:

