/****************************************************************************
**
**
** This file is part of the test suite of the Qt Toolkit.
**
**
****************************************************************************/

#include <qtest.h>
#include <QtQuick>
#include <QtQuick/private/qquickmousearea_p.h>
#include <QDebug>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/viewtestutils_p.h>

class TestView : public QQuickView
{
public:
    void handleEvent(QEvent *ev) { event(ev); }
};

class tst_events : public QQmlDataTest
{
    Q_OBJECT

public:
    tst_events();

private slots:
    void mousePressRelease();
    void mouseMove();
    void touchToMousePressRelease();
    void touchToMousePressMove();

public slots:
    void initTestCase() override {
        QQmlDataTest::initTestCase();
        window.setBaseSize(QSize(400, 400));
        window.setSource(testFileUrl("mouseevent.qml"));
        window.show();
        QVERIFY(QTest::qWaitForWindowExposed(&window));
    }

private:
    TestView window;
};

tst_events::tst_events()
    : QQmlDataTest(QT_QMLTEST_DATADIR)
{
}

{
    QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>("mouseArea");
    QCOMPARE(mouseArea->pressed(), false);

    QBENCHMARK {
        QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
        window.handleEvent(&pressEvent);
        QCOMPARE(mouseArea->pressed(), true);
        QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
        window.handleEvent(&releaseEvent);
    }
    QCOMPARE(mouseArea->pressed(), false);
}

void tst_events::mouseMove()
{
    QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>("mouseArea");
    QCOMPARE(mouseArea->pressed(), false);

    QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
    window.handleEvent(&pressEvent);
    QCOMPARE(mouseArea->pressed(), true);
    QMouseEvent moveEvent1(QEvent::MouseMove, QPoint(101, 100), Qt::LeftButton, Qt::LeftButton, {});
    QMouseEvent moveEvent2(QEvent::MouseMove, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
    QBENCHMARK {
        window.handleEvent(&moveEvent1);
        window.handleEvent(&moveEvent2);
    }
    QCOMPARE(mouseArea->pressed(), true);
    QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
    window.handleEvent(&releaseEvent);
    QCOMPARE(mouseArea->pressed(), false);
}

void tst_events::touchToMousePressRelease()
{
    QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>("mouseArea");
    QCOMPARE(mouseArea->pressed(), false);

    auto device = QTest::createTouchDevice();
    auto p = QPoint(80, 80);

    QBENCHMARK {
        QTest::touchEvent(&window, device).press(0, p, &window).commit();
        QCOMPARE(mouseArea->pressed(), true);
        QTest::touchEvent(&window, device).release(0, p, &window).commit();
    }
    QCOMPARE(mouseArea->pressed(), false);
}

void tst_events::touchToMousePressMove()
{
    QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>("mouseArea");
    QCOMPARE(mouseArea->pressed(), false);

    auto device = QTest::createTouchDevice();
    auto p = QPoint(80, 80);
    auto p2 = QPoint(81, 80);

    QTest::touchEvent(&window, device).press(0, p, &window).commit();
    QQuickTouchUtils::flush(&window);
    QCOMPARE(mouseArea->pressed(), true);

    QBENCHMARK {
        QTest::touchEvent(&window, device).move(0, p, &window).commit();
        QCOMPARE(mouseArea->pressed(), true);
        QTest::touchEvent(&window, device).move(0, p2, &window).commit();
    }
    QCOMPARE(mouseArea->pressed(), true);
    QTest::touchEvent(&window, device).release(0, p, &window).commit();
    QQuickTouchUtils::flush(&window);
    QCOMPARE(mouseArea->pressed(), false);
}

QTEST_MAIN(tst_events)
#include "tst_events.moc"


{
    QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>("mouseArea");
    QCOMPARE(mouseArea->pressed(), false);

    auto device = QTest::createTouchDevice();
    auto p = QPoint(80, 80);

    QBENCHMARK {
        QTest::touchEvent(&window, device).press(0, p, &window).commit();
        QCOMPARE(mouseArea->pressed(), true);
        QTest::touchEvent(&window, device).release(0, p, &window).commit();
    }

/****************************************************************************
**
**
** This file is part of the test suite of the Qt Toolkit.
**
**
****************************************************************************/

#include <qtest.h>
#include <QtQuick>
#include <QtQuick/private/qquickmousearea_p.h>
#include <QDebug>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/viewtestutils_p.h>

class TestView : public QQuickView
{
public:
    void handleEvent(QEvent *ev) { event(ev); }
};

class tst_events : public QQmlDataTest
{
    Q_OBJECT

public:
    tst_events();

private slots:
    void mousePressRelease();
    void mouseMove();
    void touchToMousePressRelease();
    void touchToMousePressMove();

public slots:
    void initTestCase() override {
        QQmlDataTest::initTestCase();
        window.setBaseSize(QSize(400, 400));
        window.setSource(testFileUrl("mouseevent.qml"));
        window.show();
        QVERIFY(QTest::qWaitForWindowExposed(&window));
    }

private:
    TestView window;
};

tst_events::tst_events()
    : QQmlDataTest(QT_QMLTEST_DATADIR)
{
}

void tst_events::mousePressRelease()
{
    QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>("mouseArea");
    QCOMPARE(mouseArea->pressed(), false);

    QBENCHMARK {
        QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
        window.handleEvent(&pressEvent);
        QCOMPARE(mouseArea->pressed(), true);
        QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
        window.handleEvent(&releaseEvent);
    }
    QCOMPARE(mouseArea->pressed(), false);
}

void tst_events::mouseMove()
{
    QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>("mouseArea");
    QCOMPARE(mouseArea->pressed(), false);

    QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
    window.handleEvent(&pressEvent);
    QCOMPARE(mouseArea->pressed(), true);
    QMouseEvent moveEvent1(QEvent::MouseMove, QPoint(101, 100), Qt::LeftButton, Qt::LeftButton, {});
    QMouseEvent moveEvent2(QEvent::MouseMove, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
    QBENCHMARK {
        window.handleEvent(&moveEvent1);
        window.handleEvent(&moveEvent2);
    }
    QCOMPARE(mouseArea->pressed(), true);
    QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
    window.handleEvent(&releaseEvent);
    QCOMPARE(mouseArea->pressed(), false);
}

void tst_events::touchToMousePressRelease()

    QCOMPARE(mouseArea->pressed(), false);
}

void tst_events::touchToMousePressMove()
{
    QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>("mouseArea");
    QCOMPARE(mouseArea->pressed(), false);

    auto device = QTest::createTouchDevice();
    auto p = QPoint(80, 80);
    auto p2 = QPoint(81, 80);

    QTest::touchEvent(&window, device).press(0, p, &window).commit();
    QQuickTouchUtils::flush(&window);
    QCOMPARE(mouseArea->pressed(), true);

    QBENCHMARK {
        QTest::touchEvent(&window, device).move(0, p, &window).commit();
        QCOMPARE(mouseArea->pressed(), true);
        QTest::touchEvent(&window, device).move(0, p2, &window).commit();
    }
    QCOMPARE(mouseArea->pressed(), true);
    QTest::touchEvent(&window, device).release(0, p, &window).commit();
    QQuickTouchUtils::flush(&window);
    QCOMPARE(mouseArea->pressed(), false);
}

QTEST_MAIN(tst_events)
#include "tst_events.moc"


/****************************************************************************
**
**
** This file is part of the test suite of the Qt Toolkit.
**
**
****************************************************************************/

#include <qtest.h>
#include <QtQuick>
#include <QtQuick/private/qquickmousearea_p.h>
#include <QDebug>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/viewtestutils_p.h>

class TestView : public QQuickView
{
public:
    void handleEvent(QEvent *ev) { event(ev); }
};

class tst_events : public QQmlDataTest
{
    Q_OBJECT

public:
    tst_events();

private slots:
    void mousePressRelease();
    void mouseMove();
    void touchToMousePressRelease();
    void touchToMousePressMove();

public slots:
    void initTestCase() override {
        QQmlDataTest::initTestCase();
        window.setBaseSize(QSize(400, 400));
        window.setSource(testFileUrl("mouseevent.qml"));
        window.show();
        QVERIFY(QTest::qWaitForWindowExposed(&window));
    }

private:
    TestView window;
};

tst_events::tst_events()
    : QQmlDataTest(QT_QMLTEST_DATADIR)
{
}

void tst_events::mousePressRelease()
{
    QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>("mouseArea");
    QCOMPARE(mouseArea->pressed(), false);

    QBENCHMARK {
        QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
        window.handleEvent(&pressEvent);
        QCOMPARE(mouseArea->pressed(), true);
        QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
        window.handleEvent(&releaseEvent);
    }
    QCOMPARE(mouseArea->pressed(), false);
}

void tst_events::mouseMove()
{
    QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>("mouseArea");
    QCOMPARE(mouseArea->pressed(), false);

    QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
    window.handleEvent(&pressEvent);
    QCOMPARE(mouseArea->press

    QMouseEvent moveEvent1(QEvent::MouseMove, QPoint(101, 100), Qt::LeftButton, Qt::LeftButton, {});
    QMouseEvent moveEvent2(QEvent::MouseMove, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
    QBENCHMARK {
        window.handleEvent(&moveEvent1);
        window.handleEvent(&moveEvent2);
    }
    QCOMPARE(mouseArea->pressed(), true);
    QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
    window.handleEvent(&releaseEvent);
    QCOMPARE(mouseArea->pressed(), false);
}

void tst_events::touchToMousePressRelease()
{
    QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>("mouseArea");
    QCOMPARE(mouseArea->pressed(), false);

    auto device = QTest::createTouchDevice();
    auto p = QPoint(80, 80);

    QBENCHMARK {
        QTest::touchEvent(&window, device).press(0, p, &window).commit();
        QCOMPARE(mouseArea->pressed(), true);
        QTest::touchEvent(&window, device).release(0, p, &window).commit();
    }
    QCOMPARE(mouseArea->pressed(), false);
}

void tst_events::touchToMousePressMove()
{
    QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>("mouseArea");
    QCOMPARE(mouseArea->pressed(), false);

    auto device = QTest::createTouchDevice();
    auto p = QPoint(80, 80);
    auto p2 = QPoint(81, 80);

    QTest::touchEvent(&window, device).press(0, p, &window).commit();
    QQuickTouchUtils::flush(&window);
    QCOMPARE(mouseArea->pressed(), true);

    QBENCHMARK {
        QTest::touchEvent(&window, device).move(0, p, &window).commit();
        QCOMPARE(mouseArea->pressed(), true);
        QTest::touchEvent(&window, device).move(0, p2, &window).commit();
    }
    QCOMPARE(mouseArea->pressed(), true);
    QTest::touchEvent(&window, device).release(0, p, &window).commit();
    QQuickTouchUtils::flush(&window);
    QCOMPARE(mouseArea->pressed(), false);
}

QTEST_MAIN(tst_events)
#include "tst_events.moc"


void collide(double *f, double *r, double *u, double *v)

/* This code accompanies
 *   The Lattice Boltzmann Method: Principles and Practice
 *   T. Krüger, H. Kusumaatmaja, A. Kuzmin, O. Shardt, G. Silva, E.M. Viggen
 *   ISBN 978-3-319-44649-3 (Electronic)
 *        978-3-319-44647-9 (Print)
 *   http://example.com
 *
 *
 * Author: Orest Shardt
 *
 */
#include <stdio.h>
#include <stdlib.h>
#define _USE_MATH_DEFINES
#include <math.h>

#include "seconds.h"

const unsigned int scale = 2;
const unsigned int NX = 32*scale;
const unsigned int NY = NX;

const unsigned int ndir = 9;
const size_t mem_size_ndir   = sizeof(double)*NX*NY*ndir;
const size_t mem_size_scalar = sizeof(double)*NX*NY;

const double w0 = 4.0/9.0;  // zero weight
const double ws = 1.0/9.0;  // adjacent weight
const double wd = 1.0/36.0; // diagonal weight
const double wi[] = {w0,ws,ws,ws,ws,wd,wd,wd,wd};
const int dirx[] = {0,1,0,-1, 0,1,-1,-1, 1};
const int diry[] = {0,0,1, 0,-1,1, 1,-1,-1};

const double nu = 1.0/6.0;
const double tau = 3.0*nu+0.5;

// Taylor-Green parameters
const double u_max = 0.04/scale;
const double rho0 = 1.0;

const unsigned int NSTEPS = 200*scale*scale;
const unsigned int NSAVE  =  50*scale*scale;
const unsigned int NMSG   =  50*scale*scale;

// compute L2 error and energy?
// disable for speed testing
const bool computeFlowProperties = true;

// suppress verbose output
const bool quiet = true;

// Declarations of functions used in main(). Definitions follow after main().
void taylor_green(unsigned int,unsigned int,unsigned int,double*,double*,double*);
void taylor_green(unsigned int,double*,double*,double*);
void stream(double*,double*);
void compute_rho_u(double*,double*,double*,double*);
void collide(double*,double*,double*,double*);
void init_equilibrium(double*,double*,double*,double*);
void compute_flow_properties(unsigned int,double*,double*,double*,double*);
void report_flow_properties(unsigned int,double*,double*,double*);
void save_scalar(const char*,double*,unsigned int);

inline size_t scalar_index(unsigned int x, unsigned int y)
{
    return NX*y+x;
}

inline size_t field_index(unsigned int x, unsigned int y, unsigned int d)
{
    return NX*(NY*d+y)+x;
}

int main(int argc, char* argv[])
{
    printf("Simulating Taylor-Green vortex decay\n");
    printf("      domain size: %ux%u\n",NX,NY);
    printf("               nu: %g\n",nu);
    printf("              tau: %g\n",tau);
    printf("            u_max: %g\n",u_max);
    printf("             rho0: %g\n",rho0);
    printf("        timesteps: %u\n",NSTEPS);
    printf("       save every: %u\n",NSAVE);
    printf("    message every: %u\n",NMSG);
    printf("\n");

    double bytesPerMiB = 1024.0*1024.0;

    double *f1  = (double*) malloc(mem_size_ndir);
    double *f2  = (double*) malloc(mem_size_ndir);
    double *rho = (double*) malloc(mem_size_scalar);
    double *ux  = (double*) malloc(mem_size_scalar);
    double *uy  = (double*) malloc(mem_size_scalar);

    size_t total_mem_bytes = 2*mem_size_ndir + 3*mem_size_scalar;

    if(f1 == NULL || f2 == NULL || rho == NULL || ux == NULL || uy == NULL)
    {
        fprintf(stderr,"Error: unable to allocate required memory (%.1f MiB).\n",total_mem_bytes/bytesPerMiB);
        exit(-1);
    }

    // compute Taylor-Green flow at t=0
    // to initialise rho, ux, uy fields.
    taylor_green(0,rho,ux,uy);

    // initialise f1 as equilibrium for rho, ux, uy
    init_equilibrium(f1,rho,ux,uy);

    save_scalar("rho",rho,0);
    save_scalar("ux", ux, 0);
    save_scalar("uy", uy, 0);
    if(computeFlowProperties)
    {
        report_flow_properties(0,rho,ux,uy);
    }

    double start = seconds();

    // main simulation loop; take NSTEPS time steps
    for(unsigned int n = 0; n < NSTEPS; ++n)
    {
        // stream from f1 storing to f2
        stream(f1,f2);

        // calculate post-streaming density and velocity
        compute_rho_u(f2,rho,ux,uy);

        // perform collision on f2
        collide(f2,rho,ux,uy);

        if((n+1)%NSAVE == 0)
        {
            save_scalar("rho",rho,n+1);
            save_scalar("ux", ux, n+1);
            save_scalar("uy", uy, n+1);
        }

        // swap pointers
        double *temp = f1;
        f1 = f2;
        f2 = temp;

        if((n+1)%NMSG == 0)
        {
            if(computeFlowProperties)
            {
                report_flow_properties(n+1,rho,ux,uy);
            }

            if(!quiet)
                printf("completed timestep %d\n",n+1);
        }
    }
    double end = seconds();
    double runtime = end-start;

    // note NX*NY overflows when NX=NY=65536
    size_t nodes_updated = NSTEPS*size_t(NX*NY);
    double speed = nodes_updated/(1e6*runtime);

    printf(" ----- performance information -----\n");
    printf(" memory allocated: %.1f (MiB)\n",total_mem_bytes/bytesPerMiB);
    printf("        timesteps: %u\n",NSTEPS);
    printf("          runtime: %.3f (s)\n",runtime);
    printf("            speed: %.2f (Mlups)\n",speed);

    // deallocate memory
    free(f1);  free(f2);
    free(rho); free(ux); free(uy);

    return 0;
}

void taylor_green(unsigned int t, unsigned int x, unsigned int y, double *r, double *u, double *v)
{
    double kx = 2.0*M_PI/NX;
    double ky = 2.0*M_PI/NY;
    double td = 1.0/(nu*(kx*kx+ky*ky));

    double X = x+0.5;
    double Y = y+0.5;
    double ux = -u_max*sqrt(ky/kx)*cos(kx*X)*sin(ky*Y)*exp(-1.0*t/td);
    double uy =  u_max*sqrt(kx/ky)*sin(kx*X)*cos(ky*Y)*exp(-1.0*t/td);
    double P = -0.25*rho0*u_max*u_max*((ky/kx)*cos(2.0*kx*X)+(kx/ky)*cos(2.0*ky*Y))*exp(-2.0*t/td);
    double rho = rho0+3.0*P;

    *r = rho;
    *u = ux;
    *v = uy;
}

void taylor_green(unsigned int t, double *r, double *u, double *v)
{
    for(unsigned int y = 0; y < NY; ++y)
    {
        for(unsigned int x = 0; x < NX; ++x)
        {
            size_t sidx = scalar_index(x,y);

            taylor_green(t,x,y,&r[sidx],&u[sidx],&v[sidx]);
        }
    }
}

void init_equilibrium(double *f, double *r, double *u, double *v)
{
    for(unsigned int y = 0; y < NY; ++y)
    {
        for(unsigned int x = 0; x < NX; ++x)
        {
            double rho = r[scalar_index(x,y)];
            double ux  = u[scalar_index(x,y)];
            double uy  = v[scalar_index(x,y)];

            for(unsigned int i = 0; i < ndir; ++i)
            {
                double cidotu = dirx[i]*ux + diry[i]*uy;
                f[field_index(x,y,i)] = wi[i]*rho*(1.0 + 3.0*cidotu + 4.5*cidotu*cidotu - 1.5*(ux*ux+uy*uy));
            }
        }
    }
}

void stream(double *f_src, double *f_dst)
{
    for(unsigned int y = 0; y < NY; ++y)
    {
        for(unsigned int x = 0; x < NX; ++x)
        {
            for(unsigned int i = 0; i < ndir; ++i)
            {
                // enforce periodicity
                // add NX to ensure that value is positive
                unsigned int xmd = (NX+x-dirx[i])%NX;
                unsigned int ymd = (NY+y-diry[i])%NY;

                f_dst[field_index(x,y,i)] = f_src[field_index(xmd,ymd,i)];
            }
        }
    }
}

void compute_rho_u(double *f, double *r, double *u, double *v)
{
    for(unsigned int y = 0; y < NY; ++y)
    {
        for(unsigned int x = 0; x < NX; ++x)
        {
            double rho = 0.0;
            double ux  = 0.0;
            double uy  = 0.0;

            for(unsigned int i = 0; i < ndir; ++i)
            {
                rho += f[field_index(x,y,i)];
                ux  += dirx[i]*f[field_index(x,y,i)];
                uy  += diry[i]*f[field_index(x,y,i)];
            }

            r[scalar_index(x,y)] = rho;
            u[scalar_index(x,y)] = ux/rho;
            v[scalar_index(x,y)] = uy/rho;
        }
    }
}

{
    // useful constants
    const double tauinv = 2.0/(6.0*nu+1.0); // 1/tau
    const double omtauinv = 1.0-tauinv;     // 1 - 1/tau

    for(unsigned int y = 0; y < NY; ++y)
    {
        for(unsigned int x = 0; x < NX; ++x)
        {
            double rho = r[scalar_index(x,y)];
            double ux  = u[scalar_index(x,y)];
            double uy  = v[scalar_index(x,y)];

            for(unsigned int i = 0; i < ndir; ++i)
            {
                // calculate dot product
                double cidotu = dirx[i]*ux + diry[i]*uy;

                // calculate equilibrium
                double feq = wi[i]*rho*(1.0 + 3.0*cidotu + 4.5*cidotu*cidotu - 1.5*(ux*ux+uy*uy));

                // relax to equilibrium
                f[field_index(x,y,i)]  = omtauinv*f[field_index(x,y,i)] + tauinv*feq;
            }
        }
    }
}

void compute_flow_properties(unsigned int t, double *r, double *u, double *v, double *prop)
{
    // prop must point to space for 4 doubles:
    // 1: L2 error in rho
    // 2: L2 error in ux
    // 3: L2 error in uy

    double E = 0.0; // kinetic energy

    double sumrhoe2 = 0.0; // sum of error squared in rho
    double sumuxe2 = 0.0;  //                         ux
    double sumuye2 = 0.0;  //                         uy

    double sumrhoa2 = 0.0; // sum of analytical rho squared
    double sumuxa2 = 0.0;  //                   ux
    double sumuya2 = 0.0;  //                   uy

    for(unsigned int y = 0; y < NY; ++y)
    {
        for(unsigned int x = 0; x < NX; ++x)
        {
            double rho = r[scalar_index(x,y)];
            double ux  = u[scalar_index(x,y)];
            double uy  = v[scalar_index(x,y)];
            E += rho*(ux*ux + uy*uy);

            double rhoa, uxa, uya;
            taylor_green(t,x,y,&rhoa,&uxa,&uya);

            sumrhoe2 += (rho-rhoa)*(rho-rhoa);
            sumuxe2  += (ux-uxa)*(ux-uxa);
            sumuye2  += (uy-uya)*(uy-uya);

            sumrhoa2 += (rhoa-rho0)*(rhoa-rho0);
            sumuxa2  += uxa*uxa;
            sumuya2  += uya*uya;
        }
    }

    prop[0] = E;
    prop[1] = sqrt(sumrhoe2/sumrhoa2);
    prop[2] = sqrt(sumuxe2/sumuxa2);
    prop[3] = sqrt(sumuye2/sumuya2);
}

void report_flow_properties(unsigned int t, double *rho, double *ux, double *uy)
{
    double prop[4];
    compute_flow_properties(t,rho,ux,uy,prop);
    printf("%u,%g,%g,%g,%g\n",t,prop[0],prop[1],prop[2],prop[3]);
}

void save_scalar(const char* name, double *scalar, unsigned int n)
{
    // assume reasonably-sized file names
    char filename[128];
    char format[16];

    // compute maximum number of digits
    int ndigits = floor(log10((double)NSTEPS)+1.0);

    // generate format string
    // file name format is name0000nnn.bin
    sprintf(format,"%%s%%0%dd.bin",ndigits);
    sprintf(filename,format,name,n);

    // open file for writing
    FILE *fout = fopen(filename,"wb+");

    // write data
    fwrite(scalar,1,mem_size_scalar,fout);

    // close file
    fclose(fout);

    if(ferror(fout))
    {
        fprintf(stderr,"Error saving to %s\n",filename);
        perror("");
    }
    else
    {
        if(!quiet)
            printf("Saved to %s\n",filename);
    }
}


/* This code accompanies
 *   The Lattice Boltzmann Method: Principles and Practice
 *   T. Krüger, H. Kusumaatmaja, A. Kuzmin, O. Shardt, G. Silva, E.M. Viggen
 *   ISBN 978-3-319-44649-3 (Electronic)
 *        978-3-319-44647-9 (Print)
 *   http://example.com
 *
 *
 * Author: Orest Shardt
 *
 */
#include <stdio.h>
#include <stdlib.h>
#define _USE_MATH_DEFINES
#include <math.h>

#include "seconds.h"

const unsigned int scale = 2;
const unsigned int NX = 32*scale;
const unsigned int NY = NX;

const unsigned int ndir = 9;
const size_t mem_size_ndir   = sizeof(double)*NX*NY*ndir;
const size_t mem_size_scalar = sizeof(double)*NX*NY;

const double w0 = 4.0/9.0;  // zero weight
const double ws = 1.0/9.0;  // adjacent weight
const double wd = 1.0/36.0; // diagonal weight
const double wi[] = {w0,ws,ws,ws,ws,wd,wd,wd,wd};
const int dirx[] = {0,1,0,-1, 0,1,-1,-1, 1};
const int diry[] = {0,0,1, 0,-1,1, 1,-1,-1};

const double nu = 1.0/6.0;
const double tau = 3.0*nu+0.5;

// Taylor-Green parameters
const double u_max = 0.04/scale;
const double rho0 = 1.0;

const unsigned int NSTEPS = 200*scale*scale;
const unsigned int NSAVE  =  50*scale*scale;
const unsigned int NMSG   =  50*scale*scale;

// compute L2 error and energy?
// disable for speed testing
const bool computeFlowProperties = true;

// suppress verbose output
const bool quiet = true;

// Declarations of functions used in main(). Definitions follow after main().
void taylor_green(unsigned int,unsigned int,unsigned int,double*,double*,double*);
void taylor_green(unsigned int,double*,double*,double*);
void stream(double*,double*);
void compute_rho_u(double*,double*,double*,double*);
void collide(double*,double*,double*,double*);
void init_equilibrium(double*,double*,double*,double*);
void compute_flow_properties(unsigned int,double*,double*,double*,double*);
void report_flow_properties(unsigned int,double*,double*,double*);
void save_scalar(const char*,double*,unsigned int);

inline size_t scalar_index(unsigned int x, unsigned int y)
{
    return NX*y+x;
}

inline size_t field_index(unsigned int x, unsigned int y, unsigned int d)

int main(int argc, char* argv[])
{
    printf("Simulating Taylor-Green vortex decay\n");
    printf("      domain size: %ux%u\n",NX,NY);
    printf("               nu: %g\n",nu);
    printf("              tau: %g\n",tau);
    printf("            u_max: %g\n",u_max);
    printf("             rho0: %g\n",rho0);
    printf("        timesteps: %u\n",NSTEPS);
    printf("       save every: %u\n",NSAVE);
    printf("    message every: %u\n",NMSG);
    printf("\n");

    double bytesPerMiB = 1024.0*1024.0;

    double *f1  = (double*) malloc(mem_size_ndir);
    double *f2  = (double*) malloc(mem_size_ndir);
    double *rho = (double*) malloc(mem_size_scalar);
    double *ux  = (double*) malloc(mem_size_scalar);
    double *uy  = (double*) malloc(mem_size_scalar);

    size_t total_mem_bytes = 2*mem_size_ndir + 3*mem_size_scalar;

    if(f1 == NULL || f2 == NULL || rho == NULL || ux == NULL || uy == NULL)
    {
        fprintf(stderr,"Error: unable to allocate required memory (%.1f MiB).\n",total_mem_bytes/bytesPerMiB);
        exit(-1);
    }

    // compute Taylor-Green flow at t=0
    // to initialise rho, ux, uy fields.
    taylor_green(0,rho,ux,uy);

    // initialise f1 as equilibrium for rho, ux, uy
    init_equilibrium(f1,rho,ux,uy);

    save_scalar("rho",rho,0);
    save_scalar("ux", ux, 0);
    save_scalar("uy", uy, 0);
    if(computeFlowProperties)
    {
        report_flow_properties(0,rho,ux,uy);
    }

    double start = seconds();

    // main simulation loop; take NSTEPS time steps
    for(unsigned int n = 0; n < NSTEPS; ++n)
    {
        // stream from f1 storing to f2
        stream(f1,f2);

        // calculate post-streaming density and velocity
        compute_rho_u(f2,rho,ux,uy);

        // perform collision on f2
        collide(f2,rho,ux,uy);

        if((n+1)%NSAVE == 0)
        {
            save_scalar("rho",rho,n+1);
            save_scalar("ux", ux, n+1);
            save_scalar("uy", uy, n+1);
        }

        // swap pointers
        double *temp = f1;
        f1 = f2;
        f2 = temp;

        if((n+1)%NMSG == 0)
        {
            if(computeFlowProperties)
            {
                report_flow_properties(n+1,rho,ux,uy);
            }

            if(!quiet)
                printf("completed timestep %d\n",n+1);
        }
    }
    double end = seconds();
    double runtime = end-start;

    // note NX*NY overflows when NX=NY=65536
    size_t nodes_updated = NSTEPS*size_t(NX*NY);
    double speed = nodes_updated/(1e6*runtime);

    printf(" ----- performance information -----\n");
    printf(" memory allocated: %.1f (MiB)\n",total_mem_bytes/bytesPerMiB);
    printf("        timesteps: %u\n",NSTEPS);
    printf("          runtime: %.3f (s)\n",runtime);
    printf("            speed: %.2f (Mlups)\n",speed);

    // deallocate memory
    free(f1);  free(f2);
    free(rho); free(ux); free(uy);

    return 0;
}

void taylor_green(unsigned int t, unsigned int x, unsigned int y, double *r, double *u, double *v)
{
    double kx = 2.0*M_PI/NX;
    double ky = 2.0*M_PI/NY;
    double td = 1.0/(nu*(kx*kx+ky*ky));

    double X = x+0.5;
    double Y = y+0.5;
    double ux = -u_max*sqrt(ky/kx)*cos(kx*X)*sin(ky*Y)*exp(-1.0*t/td);
    double uy =  u_max*sqrt(kx/ky)*sin(kx*X)*cos(ky*Y)*exp(-1.0*t/td);
    double P = -0.25*rho0*u_max*u_max*((ky/kx)*cos(2.0*kx*X)+(kx/ky)*cos(2.0*ky*Y))*exp(-2.0*t/td);
    double rho = rho0+3.0*P;

    *r = rho;
    *u = ux;
    *v = uy;
}

void taylor_green(unsigned int t, double *r, double *u, double *v)
{
    for(unsigned int y = 0; y < NY; ++y)
    {
        for(unsigned int x = 0; x < NX; ++x)
        {
            size_t sidx = scalar_index(x,y);

            taylor_green(t,x,y,&r[sidx],&u[sidx],&v[sidx]);
        }
    }
}

void init_equilibrium(double *f, double *r, double *u, double *v)
{
    for(unsigned int y = 0; y < NY; ++y)
    {
        for(unsigned int x = 0; x < NX; ++x)
        {
            double rho = r[scalar_index(x,y)];
            double ux  = u[scalar_index(x,y)];
            double uy  = v[scalar_index(x,y)];

            for(unsigned int i = 0; i < ndir; ++i)
            {
                double cidotu = dirx[i]*ux + diry[i]*uy;
                f[field_index(x,y,i)] = wi[i]*rho*(1.0 + 3.0*cidotu + 4.5*cidotu*cidotu - 1.5*(ux*ux+uy*uy));
            }
        }
    }
}

void stream(double *f_src, double *f_dst)
{
    for(unsigned int y = 0; y < NY; ++y)
    {
        for(unsigned int x = 0; x < NX; ++x)
        {
            for(unsigned int i = 0; i < ndir; ++i)
            {
                // enforce periodicity
                // add NX to ensure that value is positive
                unsigned int xmd = (NX+x-dirx[i])%NX;
                unsigned int ymd = (NY+y-diry[i])%NY;

                f_dst[field_index(x,y,i)] = f_src[field_index(xmd,ymd,i)];
            }
        }
    }
}

void compute_rho_u(double *f, double *r, double *u, double *v)
{
    for(unsigned int y = 0; y < NY; ++y)
    {
        for(unsigned int x = 0; x < NX; ++x)
        {
            double rho = 0.0;
            double ux  = 0.0;
            double uy  = 0.0;

            for(unsigned int i = 0; i < ndir; ++i)
            {
                rho += f[field_index(x,y,i)];
                ux  += dirx[i]*f[field_index(x,y,i)];
                uy  += diry[i]*f[field_index(x,y,i)];
            }

            r[scalar_index(x,y)] = rho;
            u[scalar_index(x,y)] = ux/rho;
            v[scalar_index(x,y)] = uy/rho;
        }
    }
}

void collide(double *f, double *r, double *u, double *v)
{
    // useful constants
    const double tauinv = 2.0/(6.0*nu+1.0); // 1/tau
    const double omtauinv = 1.0-tauinv;     // 1 - 1/tau

    for(unsigned int y = 0; y < NY; ++y)
    {
        for(unsigned int x = 0; x < NX; ++x)
        {
            double rho = r[scalar_index(x,y)];
            double ux  = u[scalar_index(x,y)];
            double uy  = v[scalar_index(x,y)];

            for(unsigned int i = 0; i < ndir; ++i)
            {
                // calculate dot product
                double cidotu = dirx[i]*ux + diry[i]*uy;

                // calculate equilibrium
                double feq = wi[i]*rho*(1.0 + 3.0*cidotu + 4.5*cidotu*cidotu - 1.5*(ux*ux+uy*uy));

                // relax to equilibrium
                f[field_index(x,y,i)]  = omtauinv*f[field_index(x,y,i)] + tauinv*feq;
            }
        }
    }
}

void compute_flow_properties(unsigned int t, double *r, double *u, double *v, double *prop)
{
    // prop must point to space for 4 doubles:
    // 1: L2 error in rho
    // 2: L2 error in ux
    // 3: L2 error in uy

    double E = 0.0; // kinetic energy

    double sumrhoe2 = 0.0; // sum of error squared in rho
    double sumuxe2 = 0.0;  //                         ux
    double sumuye2 = 0.0;  //                         uy

    double sumrhoa2 = 0.0; // sum of analytical rho squared
    double sumuxa2 = 0.0;  //                   ux
    double sumuya2 = 0.0;  //                   uy

    for(unsigned int y = 0; y < NY; ++y)
    {
        for(unsigned int x = 0; x < NX; ++x)
        {
            double rho = r[scalar_index(x,y)];
            double ux  = u[scalar_index(x,y)];
            double uy  = v[scalar_index(x,y)];
            E += rho*(ux*ux + uy*uy);

            double rhoa, uxa, uya;
            taylor_green(t,x,y,&rhoa,&uxa,&uya);

            sumrhoe2 += (rho-rhoa)*(rho-rhoa);
            sumuxe2  += (ux-uxa)*(ux-uxa);
            sumuye2  += (uy-uya)*(uy-uya);

            sumrhoa2 += (rhoa-rho0)*(rhoa-rho0);
            sumuxa2  += uxa*uxa;
            sumuya2  += uya*uya;
        }
    }

    prop[0] = E;
    prop[1] = sqrt(sumrhoe2/sumrhoa2);
    prop[2] = sqrt(sumuxe2/sumuxa2);
    prop[3] = sqrt(sumuye2/sumuya2);
}

void report_flow_properties(unsigned int t, double *rho, double *ux, double *uy)
{
    double prop[4];
    compute_flow_properties(t,rho,ux,uy,prop);
    printf("%u,%g,%g,%g,%g\n",t,prop[0],prop[1],prop[2],prop[3]);
}

void save_scalar(const char* name, double *scalar, unsigned int n)
{
    // assume reasonably-sized file names
    char filename[128];
    char format[16];

    // compute maximum number of digits
    int ndigits = floor(log10((double)NSTEPS)+1.0);

    // generate format string
    // file name format is name0000nnn.bin
    sprintf(format,"%%s%%0%dd.bin",ndigits);
    sprintf(filename,format,name,n);

    // open file for writing
    FILE *fout = fopen(filename,"wb+");

    // write data
    fwrite(scalar,1,mem_size_scalar,fout);

    // close file
    fclose(fout);

    if(ferror(fout))
    {
        fprintf(stderr,"Error saving to %s\n",filename);
        perror("");
    }
    else
    {
        if(!quiet)
            printf("Saved to %s\n",filename);
    }
}


/* This code accompanies
 *   The Lattice Boltzmann Method: Principles and Practice
 *   T. Krüger, H. Kusumaatmaja, A. Kuzmin, O. Shardt, G. Silva, E.M. Viggen
 *   ISBN 978-3-319-44649-3 (Electronic)
 *        978-3-319-44647-9 (Print)
 *   http://example.com
 *
 *
 * Author: Orest Shardt
 *
 */
#include <stdio.h>
#include <stdlib.h>
#define _USE_MATH_DEFINES
#include <math.h>

#include "seconds.h"

const unsigned int scale = 2;
const unsigned int NX = 32*scale;
const unsigned int NY = NX;

const unsigned int ndir = 9;
const size_t mem_size_ndir   = sizeof(double)*NX*NY*ndir;
const size_t mem_size_scalar = sizeof(double)*NX*NY;

const double w0 = 4.0/9.0;  // zero weight
const double ws = 1.0/9.0;  // adjacent weight
const double wd = 1.0/36.0; // diagonal weight
const double wi[] = {w0,ws,ws,ws,ws,wd,wd,wd,wd};
const int dirx[] = {0,1,0,-1, 0,1,-1,-1, 1};
const int diry[] = {0,0,1, 0,-1,1, 1,-1,-1};

const double nu = 1.0/6.0;
const double tau = 3.0*nu+0.5;

// Taylor-Green parameters
const double u_max = 0.04/scale;
const double rho0 = 1.0;

const unsigned int NSTEPS = 200*scale*scale;
const unsigned int NSAVE  =  50*scale*scale;
const unsigned int NMSG   =  50*scale*scale;

// compute L2 error and energy?
// disable for speed testing
const bool computeFlowProperties = true;

// suppress verbose output
const bool quiet = true;

// Declarations of functions used in main(). Definitions follow after main().
void taylor_green(unsigned int,unsigned int,unsigned int,double*,double*,double*);
void taylor_green(unsigned int,double*,double*,double*);
void stream(double*,double*);
void compute_rho_u(double*,double*,double*,double*);
void collide(double*,double*,double*,double*);
void init_equilibrium(double*,double*,double*,double*);
void compute_flow_properties(unsigned int,double*,double*,double*,double*);
void report_flow_properties(unsigned int,double*,double*,double*);
void save_scalar(const char*,double*,unsigned int);

inline size_t scalar_index(unsigned int x, unsigned int y)
{
    return NX*y+x;
}

inline size_t field_index(unsigned int x, unsigned int y, unsigned int d)
{
    return NX*(NY*d+y)+x;
}

int main(int argc, char* argv[])
{
    printf("Simulating Taylor-Green vortex decay\n");
    printf("      domain size: %ux%u\n",NX,NY);
    printf("               nu: %g\n",nu);
    printf("              tau: %g\n",tau);
    printf("            u_max: %g\n",u_max);
    printf("             rho0: %g\n",rho0);
    printf("        timesteps: %u\n",NSTEPS);
    printf("       save every: %u\n",NSAVE);
    printf("    message every: %u\n",NMSG);
    printf("\n");

    double bytesPerMiB = 1024.0*1024.0;

    double *f1  = (double*) malloc(mem_size_ndir);
    double *f2  = (double*) malloc(mem_size_ndir);
    double *rho = (double*) malloc(mem_size_scalar);
    double *ux  = (double*) malloc(mem_size_scalar);
    double *uy  = (double*) malloc(mem_size_scalar);

    size_t total_mem_bytes = 2*mem_size_ndir + 3*mem_size_scalar;

    if(f1 == NULL || f2 == NULL || rho == NULL || ux == NULL || uy == NULL)
    {
        fprintf(stderr,"Error: unable to allocate required memory (%.1f MiB).\n",total_mem_bytes/bytesPerMiB);
        exit(-1);
    }

    // compute Taylor-Green flow at t=0
    // to initialise rho, ux, uy fields.
    taylor_green(0,rho,ux,uy);

    // initialise f1 as equilibrium for rho, ux, uy
    init_equilibrium(f1,rho,ux,uy);

    save_scalar("rho",rho,0);
    save_scalar("ux", ux, 0);
    save_scalar("uy", uy, 0);
    if(computeFlowProperties)
    {
        report_flow_properties(0,rho,ux,uy);
    }

    double start = seconds();

    // main simulation loop; take NSTEPS time steps
    for(unsigned int n = 0; n < NSTEPS; ++n)
    {
        // stream from f1 storing to f2
        stream(f1,f2);

        // calculate post-streaming density and velocity
        compute_rho_u(f2,rho,ux,uy);

        // perform collision on f2
        collide(f2,rho,ux,uy);

        if((n+1)%NSAVE == 0)
        {
            save_scalar("rho",rho,n+1);
            save_scalar("ux", ux, n+1);
            save_scalar("uy", uy, n+1);
        }

        // swap pointers
        double *temp = f1;
        f1 = f2;
        f2 = temp;

        if((n+1)%NMSG == 0)
        {
            if(computeFlowProperties)
            {
                report_flow_properties(n+1,rho,ux,uy);
            }

            if(!quiet)
                printf("completed timestep %d\n",n+1);
        }
    }
    double end = seconds();
    double runtime = end-start;

    // note NX*NY overflows when NX=NY=65536
    size_t nodes_updated = NSTEPS*size_t(NX*NY);
    double speed = nodes_updated/(1e6*runtime);

    printf(" ----- performance information -----\n");
    printf(" memory allocated: %.1f (MiB)\n",total_mem_bytes/bytesPerMiB);
    printf("        timesteps: %u\n",NSTEPS);
    printf("          runtime: %.3f (s)\n",runtime);
    printf("            speed: %.2f (Mlups)\n",speed);

    // deallocate memory
    free(f1);  free(f2);
    free(rho); free(ux); free(uy);

    return 0;
}

void taylor_green(unsigned int t, unsigned int x, unsigned int y, double *r, double *u, double *v)
{
    double kx = 2.0*M_PI/NX;
    double ky = 2.0*M_PI/NY;
    double td = 1.0/(nu*(kx*kx+ky*ky));

    double X = x+0.5;
    double Y = y+0.5;
    double ux = -u_max*sqrt(ky/kx)*cos(kx*X)*sin(ky*Y)*exp(-1.0*t/td);
    double uy =  u_max*sqrt(kx/ky)*sin(kx*X)*cos(ky*Y)*exp(-1.0*t/td);
    double P = -0.25*rho0*u_max*u_max*((ky/kx)*cos(2.0*kx*X)+(kx/ky)*cos(2.0*ky*Y))*exp(-2.0*t/td);
    double rho = rho0+3.0*P;

    *r = rho;
    *u = ux;
    *v = uy;
}

void taylor_green(unsigned int t, double *r, double *u, double *v)
{
    for(unsigned int y = 0; y < NY; ++y)
    {
        for(unsigned int x = 0; x < NX; ++x)
        {
            size_t sidx = scalar_index(x,y);

            taylor_green(t,x,y,&r[sidx],&u[sidx],&v[sidx]);
        }
    }
}

void init_equilibrium(double *f, double *r, double *u, double *v)
{
    for(unsigned int y = 0; y < NY; ++y)
    {
        for(unsigned int x = 0; x < NX; ++x)
        {
            double rho = r[scalar_index(x,y)];
            double ux  = u[scalar_index(x,y)];
            double uy  = v[scalar_index(x,y)];

            for(unsigned int i = 0; i < ndir; ++i)
            {
                double cidotu = dirx[i]*ux + diry[i]*uy;
                f[field_index(x,y,i)] = wi[i]*rho*(1.0 + 3.0*cidotu + 4.5*cidotu*cidotu - 1.5*(ux*ux+uy*uy));
            }
        }
    }
}

void stream(double *f_src, double *f_dst)
{
    for(unsigned int y = 0; y < NY; ++y)
    {
        for(unsigned int x = 0; x < NX; ++x)
        {
            for(unsigned int i = 0; i < ndir; ++i)
            {
                // enforce periodicity
                // add NX to ensure that value is positive
                unsigned int xmd = (NX+x-dirx[i])%NX;
                unsigned int ymd = (NY+y-diry[i])%NY;

                f_dst[field_index(x,y,i)] = f_src[field_index(xmd,ymd,i)];
            }
        }
    }
}

void compute_rho_u(double *f, double *r, double *u, double *v)
{
    for(unsigned int y = 0; y < NY; ++y)
    {
        for(unsigned int x = 0; x < NX; ++x)
        {
            double rho = 0.0;
            double ux  = 0.0;
            double uy  = 0.0;

            for(unsigned int i = 0; i < ndir; ++i)
            {
                rho += f[field_index(x,y,i)];
                ux  += dirx[i]*f[field_index(x,y,i)];
                uy  += diry[i]*f[field_index(x,y,i)];
            }

            r[scalar_index(x,y)] = rho;
            u[scalar_index(x,y)] = ux/rho;
            v[scalar_index(x,y)] = uy/rho;
        }
    }
}

void collide(double *f, double *r, double *u, double *v)
{
    // useful constants
    const double tauinv = 2.0/(6.0*nu+1.0); // 1/tau
    const double omtauinv = 1.0-tauinv;     // 1 - 1/tau

    for(unsigned int y = 0; y < NY; ++y)
    {
        for(unsigned int x = 0; x < NX; ++x)
        {
            double rho = r[scalar_index(x,y)];
            double ux  = u[scalar_index(x,y)];
            double uy  = v[scalar_index(x,y)];

            for(unsigned int i = 0; i < ndir; ++i)
            {
                // calculate dot product
                double cidotu = dirx[i]*ux + diry[i]*uy;

                // calculate equilibrium
                double feq = wi[i]*rho*(1.0 + 3.0*cidotu + 4.5*cidotu*cidotu - 1.5*(ux*ux+uy*uy));

                // relax to equilibrium
                f[field_index(x,y,i)]  = omtauinv*f[field_index(x,y,i)] + tauinv*feq;
            }
        }
    }
}

void compute_flow_properties(unsigned int t, double *r, double *u, double *v, double *prop)
{
    // prop must point to space for 4 doubles:
    // 1: L2 error in rho
    // 2: L2 error in ux
    // 3: L2 error in uy

    double E = 0.0; // kinetic energy

    double sumr

 // sum of error squared in rho
    double sumuxe2 = 0.0;  //                         ux
    double sumuye2 = 0.0;  //                         uy

    double sumrhoa2 = 0.0; // sum of analytical rho squared
    double sumuxa2 = 0.0;  //                   ux
    double sumuya2 = 0.0;  //                   uy

    for(unsigned int y = 0; y < NY; ++y)
    {
        for(unsigned int x = 0; x < NX; ++x)
        {
            double rho = r[scalar_index(x,y)];
            double ux  = u[scalar_index(x,y)];
            double uy  = v[scalar_index(x,y)];
            E += rho*(ux*ux + uy*uy);

            double rhoa, uxa, uya;
            taylor_green(t,x,y,&rhoa,&uxa,&uya);

            sumrhoe2 += (rho-rhoa)*(rho-rhoa);
            sumuxe2  += (ux-uxa)*(ux-uxa);
            sumuye2  += (uy-uya)*(uy-uya);

            sumrhoa2 += (rhoa-rho0)*(rhoa-rho0);
            sumuxa2  += uxa*uxa;
            sumuya2  += uya*uya;
        }
    }

    prop[0] = E;
    prop[1] = sqrt(sumrhoe2/sumrhoa2);
    prop[2] = sqrt(sumuxe2/sumuxa2);
    prop[3] = sqrt(sumuye2/sumuya2);
}

void report_flow_properties(unsigned int t, double *rho, double *ux, double *uy)
{
    double prop[4];
    compute_flow_properties(t,rho,ux,uy,prop);
    printf("%u,%g,%g,%g,%g\n",t,prop[0],prop[1],prop[2],prop[3]);
}

void save_scalar(const char* name, double *scalar, unsigned int n)
{
    // assume reasonably-sized file names
    char filename[128];
    char format[16];

    // compute maximum number of digits
    int ndigits = floor(log10((double)NSTEPS)+1.0);

    // generate format string
    // file name format is name0000nnn.bin
    sprintf(format,"%%s%%0%dd.bin",ndigits);
    sprintf(filename,format,name,n);

    // open file for writing
    FILE *fout = fopen(filename,"wb+");

    // write data
    fwrite(scalar,1,mem_size_scalar,fout);

    // close file
    fclose(fout);

    if(ferror(fout))
    {
        fprintf(stderr,"Error saving to %s\n",filename);
        perror("");
    }
    else
    {
        if(!quiet)
            printf("Saved to %s\n",filename);
    }
}


/**
 */

#include <aws/ds/model/ListTagsForResourceRequest.h>
#include <aws/core/utils/json/JsonSerializer.h>

#include <utility>

using namespace Aws::DirectoryService::Model;
using namespace Aws::Utils::Json;
using namespace Aws::Utils;

ListTagsForResourceRequest::ListTagsForResourceRequest() :
    m_resourceIdHasBeenSet(false),
    m_nextTokenHasBeenSet(false),
    m_limit(0),
    m_limitHasBeenSet(false)
{
}

Aws::String ListTagsForResourceRequest::SerializePayload() const
{
  JsonValue payload;

  if(m_resourceIdHasBeenSet)
  {
   payload.WithString("ResourceId", m_resourceId);

  }

  if(m_nextTokenHasBeenSet)
  {
   payload.WithString("NextToken", m_nextToken);

  }

  if(m_limitHasBeenSet)
  {
   payload.WithInteger("Limit", m_limit);

  }

  return payload.View().WriteReadable();
}

{
  Aws::Http::HeaderValueCollection headers;
  headers.insert(Aws::Http::HeaderValuePair("X-Amz-Target", "DirectoryService_20150416.ListTagsForResource"));
  return headers;

}


{
  JsonValue payload;

  if(m_resourceIdHasBeenSet)
  {
   payload.WithString("ResourceId", m_resourceId);

  }

  if(m_nextTokenHasBeenSet)
  {
   payload.WithString("NextToken", m_nextToken);

  }

  if(m_limitHasBeenSet)
  {
   payload.WithInteger("Limit", m_limit);

  }

  return payload.View().WriteReadable();
}

/**
 */

#include <aws/ds/model/ListTagsForResourceRequest.h>
#include <aws/core/utils/json/JsonSerializer.h>

#include <utility>

using namespace Aws::DirectoryService::Model;
using namespace Aws::Utils::Json;
using namespace Aws::Utils;

ListTagsForResourceRequest::ListTagsForResourceRequest() :
    m_resourceIdHasBeenSet(false),
    m_nextTokenHasBeenSet(false),
    m_limit(0),
    m_limitHasBeenSet(false)
{
}

Aws::String ListTagsForResourceRequest::SerializePayload() const

Aws::Http::HeaderValueCollection ListTagsForResourceRequest::GetRequestSpecificHeaders() const
{
  Aws::Http::HeaderValueCollection headers;
  headers.insert(Aws::Http::HeaderValuePair("X-Amz-Target", "DirectoryService_20150416.ListTagsForResource"));
  return headers;

}


/**
 */

#include <aws/ds/model/ListTagsForResourceRequest.h>
#include <aws/core/utils/json/JsonSerializer.h>

#include <utility>

using namespace Aws::DirectoryService::Model;
using namespace Aws::Utils::Json;
using namespace Aws::Utils;

ListTagsForResourceRequest::ListTagsForResourceRequest() :
    m_resourceIdHasBeenSet(false),
    m_nextTokenHasBeenSet(false),
    m_limit(0),
    m_limitHasBeenSet(false)
{
}

Aws::String ListTagsForResourceRequest::SerializePayload() const
{
  JsonValue payload;

  if(m_resourceIdHasBeenSet)
  {
   payload.WithString("ResourceId", m_resourceId);

  }

  if(m_nextTokenHasBeenSet)
  {
   payload.WithString("NextToken", m_nextToken);

  }

  if(m_limitHasBeenSet)
  {
   payload.WithInteger("Limit", m_limit);

  }

  return payload.View().WriteReadable();
}

Aws::Http::HeaderValueCollection ListTagsForResourceRequest::GetRequestSpecificHeaders() const
{
  Aws::Http::HeaderValueCollection headers;
  headers.insert(Aws::Http::HeaderValue


DLLEXPORT miBoolean mib_geo_add_uv_texsurf(
	miTag		*result,
	miState		*state,
	miMIBAddTex	*paras)

/******************************************************************************
 ******************************************************************************
 * Created:	9.3.99
 * Module:	baseshader
 * Purpose:	base shaders for Phenomenon writers
 *
 * Exports:
 *	mib_geo_add_uv_texsurf
 *	mib_geo_add_uv_texsurf_version
 *
 * History:
 *
 * Description:
 *	This shader returns a copy of the input object, which must be
 *	of freeform-surface type. It loops over all faces and adds a
 *	Bezier texture surface of degree 1 to each face as the last
 *	texture surface. The parameters and control points of the texture
 *	surfaces are chosen in such a way that there is an exact mapping
 *	of the uv parametric coordinates of the geometric approximation to
 *	the texture vertex coordinates, i.e. the texture coordinates of the
 *	triangle vertices are the uv coordinates of the triangle vertex
 *	positions.
 *	The Bezier-1 basis is always added to the copy of the object.
 *****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include <shader.h>
#include <geoshader.h>

/* should rewrite this without need for integration API. Should use mi_api_. */
extern "C" void *mi_scene_create(miTag * const tag, const miScene_types type, ...);

typedef struct {
	miTag		object;
} miMIBAddTex;

/*
 * This function initializes a bezier degree 1 texture surface for
 * a geometric surface. It uses the parameter range of the face
 * for texture vertex assignment. The face is updated since a
 * surface was added.
 */

static void create_uvtexsurface(
	miSurface	*rsurf,		/* result surface */
	miFace		*face,		/* in: face */
	miSurface	*surf,		/* in: geometric surface 0 */
	miGeoScalar	*scalars,	/* surface scalars */
	miGeoIndex	scalar_idx,	/* start index for texsurf scalars */
	int		b1index)	/* basis index to be used */
{
	miGeoScalar	*ctls;
	miGeoScalar	*params;
	miUchar		offset = 1;
	miVertex_content  svi =  face->gap_vert_info;
	miVertex_content *rvi = &face->gap_vert_info;

	memcpy(&rsurf->approx, &surf->approx, sizeof(miApprox));
	memcpy(&rsurf->disp_approx, &surf->disp_approx, sizeof(miApprox));

	rsurf->no_parms[miU]  = 2;
	rsurf->no_parms[miV]  = 2;
	rsurf->no_ctls	      = 4;
	rsurf->no_specpnts    = 0;
	rsurf->scalar_idx     = scalar_idx;
	rsurf->specpnt_idx    = 0;
	rsurf->basis_idx[miU] = b1index;
	rsurf->basis_idx[miV] = b1index;
	rsurf->type	      = miSURFACE_TEXTURE_2D;
	rsurf->degree[miU]    = 1;
	rsurf->degree[miV]    = 1;
	rsurf->ctl_dim	      = 3;

	memset(rvi, 0, sizeof(miVertex_content));
	if (svi.normal_offset)
		rvi->normal_offset = offset++;
	if (svi.motion_offset)
		rvi->motion_offset = offset++;
	if (svi.derivs_offset) {
		rvi->derivs_offset = offset;
		offset += 2;
	}
	if (svi.derivs2_offset) {
		rvi->derivs2_offset = offset;
		offset += 3;
	}
	rvi->texture_offset = offset;
	rvi->no_textures = svi.no_textures + 1;
	offset += rvi->no_textures;
	if (svi.bump_offset) {
		rvi->bump_offset = offset;
		rvi->no_bumps	 = rvi->no_textures * 2;
		offset += rvi->no_textures * 2;
	}
	if (svi.user_offset) {
		rvi->user_offset = offset;
		rvi->no_users	 = svi.no_users;
		offset += rvi->no_users;
	}
	rvi->sizeof_vertex = offset;

	params = scalars + scalar_idx;

	*params++ = face->range[miU].min;
	*params++ = face->range[miU].max;
	*params++ = face->range[miV].min;
	*params   = face->range[miV].max;

	ctls = scalars + miSURFACE_CTLS(rsurf);

	*ctls++ = face->range[miU].min;
	*ctls++ = face->range[miU].max;
	*ctls++ = face->range[miU].min;
	*ctls++ = face->range[miU].max;

	*ctls++ = face->range[miV].min;
	*ctls++ = face->range[miV].min;
	*ctls++ = face->range[miV].max;
	*ctls++ = face->range[miV].max;

	*ctls++ = 0;
	*ctls++ = 0;
	*ctls++ = 0;
	*ctls   = 0;
}

/*
 * See description above.
 */

extern "C" DLLEXPORT int mib_geo_add_uv_texsurf_version(void) { return(1); }

extern "C"

{
	miTag		src_obj_tag = *mi_eval_tag(&paras->object);
	miObject	*src_obj, *object;
	miTag		obj_tag;
	int		i, k;
	miFace_list	*fl, *src_fl;
	miFace		*faces;
	miSurface	*surfaces, *src_surfaces;
	miBasis_list	*basis_list, *src_basis_list;
	miBasis		*bbasis;
	miGeoScalar	*surf_scalars, *src_surf_scalars;
	int		cur_surf=0, add_scalars, b1index, sbase;

	src_obj = (miObject*)mi_db_access(src_obj_tag);
	if (src_obj->type != miOBJECT_FACES) {
		mi_error("mib_geo_add_uv_texsurf: wrong input object type");
		mi_db_unpin(src_obj_tag);
		return(miFALSE);
	}
	src_fl = &src_obj->geo.face_list;
	if (!src_fl->faces || !src_fl->surfaces || !src_fl->basis_list ||
	    !src_fl->surf_scalars) {
		mi_error("mib_geo_add_uv_texsurf: invalid object");
		mi_db_unpin(src_obj_tag);
		return(miFALSE);
	}

	/* create object */
	object = (miObject*)mi_scene_create(&obj_tag, miSCENE_OBJECT);
	memcpy(object, src_obj, sizeof(miObject));
	fl = &object->geo.face_list;

	/* copy constant geometry */
	if (src_fl->curves)
		fl->curves = mi_db_copy(src_fl->curves);
	if (src_fl->specpnts)
		fl->specpnts = mi_db_copy(src_fl->specpnts);
	if (src_fl->curve_scalars)
		fl->curve_scalars = mi_db_copy(src_fl->curve_scalars);

	/* some fields in each face will be edited later below */
	fl->faces = mi_db_copy(src_fl->faces);

	/* ----- one basis added at the end */
	src_basis_list = (miBasis_list*)mi_db_access(src_fl->basis_list);
	basis_list = (miBasis_list*)mi_scene_create(&fl->basis_list,
						miSCENE_BASIS_LIST,
						src_basis_list->no_bases + 1,
						src_basis_list->no_scalars);
	b1index = src_basis_list->no_bases;
	memcpy(basis_list->bases, src_basis_list->bases,
		src_basis_list->no_bases * sizeof(miBasis) +
		src_basis_list->no_scalars * sizeof(miGeoScalar));
	mi_db_unpin(src_fl->basis_list);
	bbasis = &basis_list->bases[b1index];
	bbasis->type = miBASIS_BEZIER;
	bbasis->degree = 1;
	mi_scene_edit_end(fl->basis_list);

	/* ----- several texture surfaces added (one for each face) */
	src_surfaces = (miSurface*)mi_db_access(src_fl->surfaces);
	surfaces = (miSurface*)mi_scene_create(&fl->surfaces,
						miSCENE_SURFACE,
						src_fl->no_surfaces +
						src_fl->no_faces);
	fl->no_surfaces += src_fl->no_faces;

	/* ----- several scalars for the texsurfaces added at the end */
	/* 4 parameter scalars + 4 3d control point scalar vectors */
	add_scalars = fl->no_faces * (4 + 4 * 3);
	src_surf_scalars = (miGeoScalar*)mi_db_access(src_fl->surf_scalars);
	surf_scalars = (miGeoScalar*)mi_scene_create(&fl->surf_scalars,
						miSCENE_GEOSCALAR,
						src_fl->no_surf_scalars +
						add_scalars);
	memcpy(surf_scalars, src_surf_scalars,
		sizeof(miGeoScalar) * src_fl->no_surf_scalars);
	mi_db_unpin(src_fl->surf_scalars);
	/* base index for new scalars */
	sbase = src_fl->no_surf_scalars;
	fl->no_surf_scalars += add_scalars;

	faces = (miFace*)mi_scene_edit(fl->faces);

	for (i=0; i < src_fl->no_faces; i++) {
		miFace *face = &faces[i];
		/* copy original surfaces */
		int f1 = cur_surf;
		for (k=0; k < face->no_surfaces; k++) {
			memcpy(&surfaces[cur_surf++],
			       &src_surfaces[face->surface_idx+k],
			       sizeof(miSurface));
		}
		face->surface_idx = f1;
		/* create a texture surface for the first (geometric)
		   surface */
		create_uvtexsurface(&surfaces[cur_surf++],	/* result */
				face,				/* modify */
				&surfaces[face->surface_idx],	/* input  */
				surf_scalars,			/* result */
				sbase+i*(4+4*3),		/* input  */
				b1index);			/* input  */
		face->no_surfaces += 1;
	}

	mi_scene_edit_end(fl->faces);
	mi_scene_edit_end(fl->surf_scalars);
	mi_scene_edit_end(fl->surfaces);
	mi_db_unpin(src_fl->surfaces);

	mi_scene_edit_end(obj_tag);
	mi_db_unpin(src_obj_tag);

	*result = obj_tag;
	return(miTRUE);
}


/******************************************************************************
 ******************************************************************************
 * Created:	9.3.99
 * Module:	baseshader
 * Purpose:	base shaders for Phenomenon writers
 *
 * Exports:
 *	mib_geo_add_uv_texsurf
 *	mib_geo_add_uv_texsurf_version
 *
 * History:
 *
 * Description:
 *	This shader returns a copy of the input object, which must be
 *	of freeform-surface type. It loops over all faces and adds a
 *	Bezier texture surface of degree 1 to each face as the last
 *	texture surface. The parameters and control points of the texture
 *	surfaces are chosen in such a way that there is an exact mapping
 *	of the uv parametric coordinates of the geometric approximation to
 *	the texture vertex coordinates, i.e. the texture coordinates of the
 *	triangle vertices are the uv coordinates of the triangle vertex
 *	positions.
 *	The Bezier-1 basis is always added to the copy of the object.
 *****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include <shader.h>
#include <geoshader.h>

/* should rewrite this without need for integration API. Should use mi_api_. */
extern "C" void *mi_scene_create(miTag * const tag, const miScene_types type, ...);

typedef struct {
	miTag		object;
} miMIBAddTex;

/*
 * This function initializes a bezier degree 1 texture surface for
 * a geometric surface. It uses the parameter range of the face
 * for texture vertex assignment. The face is updated since a
 * surface was added.
 */

static void create_uvtexsurface(
	miSurface	*rsurf,		/* result surface */
	miFace		*face,		/* in: face */
	miSurface	*surf,		/* in: geometric surface 0 */
	miGeoScalar	*scalars,	/* surface scalars */
	miGeoIndex	scalar_idx,	/* start index for texsurf scalars */
	int		b1index)	/* basis index to be used */
{
	miGeoScalar	*ctls;
	miGeoScalar	*params;
	miUchar		offset = 1;
	miVertex_content  svi =  face->gap_vert_info;
	miVertex_content *rvi = &face->gap_vert_info;

	memcpy(&rsurf->approx, &surf->approx, sizeof(miApprox));
	memcpy(&rsurf->disp_approx, &surf->disp_approx, sizeof(miApprox));

	rsurf->no_parms[miU]  = 2;
	rsurf->no_parms[miV]  = 2;
	rsurf->no_ctls	      = 4;
	rsurf->no_specpnts    = 0;
	rsurf->scalar_idx     = scalar_idx;
	rsurf->specpnt_idx    = 0;
	rsurf->basis_idx[miU] = b1index;
	rsurf->basis_idx[miV] = b1index;
	rsurf->type	      = miSURFACE_TEXTURE_2D;
	rsurf->degree[miU]    = 1;
	rsurf->degree[miV]    = 1;
	rsurf->ctl_dim	      = 3;

	memset(rvi, 0, sizeof(miVertex_content));
	if (svi.normal_offset)
		rvi->normal_offset = offset++;
	if (svi.motion_offset)
		rvi->motion_offset = offset++;
	if (svi.derivs_offset) {
		rvi->derivs_offset = offset;
		offset += 2;
	}
	if (svi.derivs2_offset) {
		rvi->derivs2_offset = offset;
		offset += 3;
	}
	rvi->texture_offset = offset;
	rvi->no_textures = svi.no_textures + 1;
	offset += rvi->no_textures;
	if (svi.bump_offset) {
		rvi->bump_offset = offset;
		rvi->no_bumps	 = rvi->no_textures * 2;
		offset += rvi->no_textures * 2;
	}
	if (svi.user_offset) {
		rvi->user_offset = offset;
		rvi->no_users	 = svi.no_users;
		offset += rvi->no_users;
	}
	rvi->sizeof_vertex = offset;

	params = scalars + scalar_idx;

	*params++ = face->range[miU].min;
	*params++ = face->range[miU].max;
	*params++ = face->range[miV].min;
	*params   = face->range[miV].max;

	ctls = scalars + miSURFACE_CTLS(rsurf);

	*ctls++ = face->range[miU].min;
	*ctls++ = face->range[miU].max;
	*ctls++ = face->range[miU].min;
	*ctls++ = face->range[miU].max;

	*ctls++ = face->range[miV].min;
	*ctls++ = face->range[miV].min;
	*ctls++ = face->range[miV].max;
	*ctls++ = face->range[miV].max;

	*ctls++ = 0;
	*ctls++ = 0;
	*ctls++ = 0;
	*ctls   = 0;
}

/*
 * See description above.
 */

extern "C" DLLEXPORT int mib_geo_add_uv_texsurf_version(void)

extern "C" DLLEXPORT miBoolean mib_geo_add_uv_texsurf(
	miTag		*result,
	miState		*state,
	miMIBAddTex	*paras)
{
	miTag		src_obj_tag = *mi_eval_tag(&paras->object);
	miObject	*src_obj, *object;
	miTag		obj_tag;
	int		i, k;
	miFace_list	*fl, *src_fl;
	miFace		*faces;
	miSurface	*surfaces, *src_surfaces;
	miBasis_list	*basis_list, *src_basis_list;
	miBasis		*bbasis;
	miGeoScalar	*surf_scalars, *src_surf_scalars;
	int		cur_surf=0, add_scalars, b1index, sbase;

	src_obj = (miObject*)mi_db_access(src_obj_tag);
	if (src_obj->type != miOBJECT_FACES) {
		mi_error("mib_geo_add_uv_texsurf: wrong input object type");
		mi_db_unpin(src_obj_tag);
		return(miFALSE);
	}
	src_fl = &src_obj->geo.face_list;
	if (!src_fl->faces || !src_fl->surfaces || !src_fl->basis_list ||
	    !src_fl->surf_scalars) {
		mi_error("mib_geo_add_uv_texsurf: invalid object");
		mi_db_unpin(src_obj_tag);
		return(miFALSE);
	}

	/* create object */
	object = (miObject*)mi_scene_create(&obj_tag, miSCENE_OBJECT);
	memcpy(object, src_obj, sizeof(miObject));
	fl = &object->geo.face_list;

	/* copy constant geometry */
	if (src_fl->curves)
		fl->curves = mi_db_copy(src_fl->curves);
	if (src_fl->specpnts)
		fl->specpnts = mi_db_copy(src_fl->specpnts);
	if (src_fl->curve_scalars)
		fl->curve_scalars = mi_db_copy(src_fl->curve_scalars);

	/* some fields in each face will be edited later below */
	fl->faces = mi_db_copy(src_fl->faces);

	/* ----- one basis added at the end */
	src_basis_list = (miBasis_list*)mi_db_access(src_fl->basis_list);
	basis_list = (miBasis_list*)mi_scene_create(&fl->basis_list,
						miSCENE_BASIS_LIST,
						src_basis_list->no_bases + 1,
						src_basis_list->no_scalars);
	b1index = src_basis_list->no_bases;
	memcpy(basis_list->bases, src_basis_list->bases,
		src_basis_list->no_bases * sizeof(miBasis) +
		src_basis_list->no_scalars * sizeof(miGeoScalar));
	mi_db_unpin(src_fl->basis_list);
	bbasis = &basis_list->bases[b1index];
	bbasis->type = miBASIS_BEZIER;
	bbasis->degree = 1;
	mi_scene_edit_end(fl->basis_list);

	/* ----- several texture surfaces added (one for each face) */
	src_surfaces = (miSurface*)mi_db_access(src_fl->surfaces);
	surfaces = (miSurface*)mi_scene_create(&fl->surfaces,
						miSCENE_SURFACE,
						src_fl->no_surfaces +
						src_fl->no_faces);
	fl->no_surfaces += src_fl->no_faces;

	/* ----- several scalars for the texsurfaces added at the end */
	/* 4 parameter scalars + 4 3d control point scalar vectors */
	add_scalars = fl->no_faces * (4 + 4 * 3);
	src_surf_scalars = (miGeoScalar*)mi_db_access(src_fl->surf_scalars);
	surf_scalars = (miGeoScalar*)mi_scene_create(&fl->surf_scalars,
						miSCENE_GEOSCALAR,
						src_fl->no_surf_scalars +
						add_scalars);
	memcpy(surf_scalars, src_surf_scalars,
		sizeof(miGeoScalar) * src_fl->no_surf_scalars);
	mi_db_unpin(src_fl->surf_scalars);
	/* base index for new scalars */
	sbase = src_fl->no_surf_scalars;
	fl->no_surf_scalars += add_scalars;

	faces = (miFace*)mi_scene_edit(fl->faces);

	for (i=0; i < src_fl->no_faces; i++) {
		miFace *face = &faces[i];
		/* copy original surfaces */
		int f1 = cur_surf;
		for (k=0; k < face->no_surfaces; k++) {
			memcpy(&surfaces[cur_surf++],
			       &src_surfaces[face->surface_idx+k],
			       sizeof(miSurface));
		}
		face->surface_idx = f1;
		/* create a texture surface for the first (geometric)
		   surface */
		create_uvtexsurface(&surfaces[cur_surf++],	/* result */
				face,				/* modify */
				&surfaces[face->surface_idx],	/* input  */
				surf_scalars,			/* result */
				sbase+i*(4+4*3),		/* input  */
				b1index);			/* input  */
		face->no_surfaces += 1;
	}

	mi_scene_edit_end(fl->faces);
	mi_scene_edit_end(fl->surf_scalars);
	mi_scene_edit_end(fl->surfaces);
	mi_db_unpin(src_fl->surfaces);

	mi_scene_edit_end(obj_tag);
	mi_db_unpin(src_obj_tag);

	*result = obj_tag;
	return(miTRUE);
}


/******************************************************************************
 ******************************************************************************
 * Created:	9.3.99
 * Module:	baseshader
 * Purpose:	base shaders for Phenomenon writers
 *
 * Exports:
 *	mib_geo_add_uv_texsurf
 *	mib_geo_add_uv_texsurf_version
 *
 * History:
 *
 * Description:
 *	This shader returns a copy of the input object, which must be
 *	of freeform-surface type. It loops over all faces and adds a
 *	Bezier texture surface of degree 1 to each face as the last
 *	texture surface. The parameters and control points of the texture
 *	surfaces are chosen in such a way that there is an exact mapping
 *	of the uv parametric coordinates of the geometric approximation to
 *	the texture vertex coordinates, i.e. the texture coordinates of the
 *	triangle vertices are the uv coordinates of the triangle vertex
 *	positions.
 *	The Bezier-1 basis is always added to the copy of the object.
 *****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include <shader.h>
#include <geoshader.h>

/* should rewrite this without need for integration API. Should use mi_api_. */
extern "C" void *mi_scene_create(miTag * const tag, const miScene_types type, ...);

typedef struct {
	miTag		object;
} miMIBAddTex;

/*
 * This function initializes a bezier degree 1 texture surface for
 * a geometric surface. It uses the parameter range of the face
 * for texture vertex assignment. The face is updated since a
 * surface was added.
 */

static void create_uvtexsurface(
	miSurface	*rsurf,		/* result surface */
	miFace		*face,		/* in: face */
	miSurface	*surf,		/* in: geometric surface 0 */
	miGeoScalar	*scalars,	/* surface scalars */
	miGeoIndex	scalar_idx,	/* start index for texsurf scalars */
	int		b1index)	/* basis index to be used */
{
	miGeoScalar	*ctls;
	miGeoScalar	*params;
	miUchar		offset = 1;
	miVertex_content  svi =  face->gap_vert_info;
	miVertex_content *rvi = &face->gap_vert_info;

	memcpy(&rsurf->approx, &surf->approx, sizeof(miApprox));
	memcpy(&rsurf->disp_approx, &surf->disp_approx, sizeof(miApprox));

	rsurf->no_parms[miU]  = 2;
	rsurf->no_parms[miV]  = 2;
	rsurf->no_ctls	      = 4;
	rsurf->no_specpnts    = 0;
	rsurf->scalar_idx     = scalar_idx;
	rsurf->specpnt_idx    = 0;
	rsurf->basis_idx[miU] = b1index;
	rsurf->basis_idx[miV] = b

	rsurf->type	      = miSURFACE_TEXTURE_2D;
	rsurf->degree[miU]    = 1;
	rsurf->degree[miV]    = 1;
	rsurf->ctl_dim	      = 3;

	memset(rvi, 0, sizeof(miVertex_content));
	if (svi.normal_offset)
		rvi->normal_offset = offset++;
	if (svi.motion_offset)
		rvi->motion_offset = offset++;
	if (svi.derivs_offset) {
		rvi->derivs_offset = offset;
		offset += 2;
	}
	if (svi.derivs2_offset) {
		rvi->derivs2_offset = offset;
		offset += 3;
	}
	rvi->texture_offset = offset;
	rvi->no_textures = svi.no_textures + 1;
	offset += rvi->no_textures;
	if (svi.bump_offset) {
		rvi->bump_offset = offset;
		rvi->no_bumps	 = rvi->no_textures * 2;
		offset += rvi->no_textures * 2;
	}
	if (svi.user_offset) {
		rvi->user_offset = offset;
		rvi->no_users	 = svi.no_users;
		offset += rvi->no_users;
	}
	rvi->sizeof_vertex = offset;

	params = scalars + scalar_idx;

	*params++ = face->range[miU].min;
	*params++ = face->range[miU].max;
	*params++ = face->range[miV].min;
	*params   = face->range[miV].max;

	ctls = scalars + miSURFACE_CTLS(rsurf);

	*ctls++ = face->range[miU].min;
	*ctls++ = face->range[miU].max;
	*ctls++ = face->range[miU].min;
	*ctls++ = face->range[miU].max;

	*ctls++ = face->range[miV].min;
	*ctls++ = face->range[miV].min;
	*ctls++ = face->range[miV].max;
	*ctls++ = face->range[miV].max;

	*ctls++ = 0;
	*ctls++ = 0;
	*ctls++ = 0;
	*ctls   = 0;
}

/*
 * See description above.
 */

extern "C" DLLEXPORT int mib_geo_add_uv_texsurf_version(void) { return(1); }

extern "C" DLLEXPORT miBoolean mib_geo_add_uv_texsurf(
	miTag		*result,
	miState		*state,
	miMIBAddTex	*paras)
{
	miTag		src_obj_tag = *mi_eval_tag(&paras->object);
	miObject	*src_obj, *object;
	miTag		obj_tag;
	int		i, k;
	miFace_list	*fl, *src_fl;
	miFace		*faces;
	miSurface	*surfaces, *src_surfaces;
	miBasis_list	*basis_list, *src_basis_list;
	miBasis		*bbasis;
	miGeoScalar	*surf_scalars, *src_surf_scalars;
	int		cur_surf=0, add_scalars, b1index, sbase;

	src_obj = (miObject*)mi_db_access(src_obj_tag);
	if (src_obj->type != miOBJECT_FACES) {
		mi_error("mib_geo_add_uv_texsurf: wrong input object type");
		mi_db_unpin(src_obj_tag);
		return(miFALSE);
	}
	src_fl = &src_obj->geo.face_list;
	if (!src_fl->faces || !src_fl->surfaces || !src_fl->basis_list ||
	    !src_fl->surf_scalars) {
		mi_error("mib_geo_add_uv_texsurf: invalid object");
		mi_db_unpin(src_obj_tag);
		return(miFALSE);
	}

	/* create object */
	object = (miObject*)mi_scene_create(&obj_tag, miSCENE_OBJECT);
	memcpy(object, src_obj, sizeof(miObject));
	fl = &object->geo.face_list;

	/* copy constant geometry */
	if (src_fl->curves)
		fl->curves = mi_db_copy(src_fl->curves);
	if (src_fl->specpnts)
		fl->specpnts = mi_db_copy(src_fl->specpnts);
	if (src_fl->curve_scalars)
		fl->curve_scalars = mi_db_copy(src_fl->curve_scalars);

	/* some fields in each face will be edited later below */
	fl->faces = mi_db_copy(src_fl->faces);

	/* ----- one basis added at the end */
	src_basis_list = (miBasis_list*)mi_db_access(src_fl->basis_list);
	basis_list = (miBasis_list*)mi_scene_create(&fl->basis_list,
						miSCENE_BASIS_LIST,
						src_basis_list->no_bases + 1,
						src_basis_list->no_scalars);
	b1index = src_basis_list->no_bases;
	memcpy(basis_list->bases, src_basis_list->bases,
		src_basis_list->no_bases * sizeof(miBasis) +
		src_basis_list->no_scalars * sizeof(miGeoScalar));
	mi_db_unpin(src_fl->basis_list);
	bbasis = &basis_list->bases[b1index];
	bbasis->type = miBASIS_BEZIER;
	bbasis->degree = 1;
	mi_scene_edit_end(fl->basis_list);

	/* ----- several texture surfaces added (one for each face) */
	src_surfaces = (miSurface*)mi_db_access(src_fl->surfaces);
	surfaces = (miSurface*)mi_scene_create(&fl->surfaces,
						miSCENE_SURFACE,
						src_fl->no_surfaces +
						src_fl->no_faces);
	fl->no_surfaces += src_fl->no_faces;

	/* ----- several scalars for the texsurfaces added at the end */
	/* 4 parameter scalars + 4 3d control point scalar vectors */
	add_scalars = fl->no_faces * (4 + 4 * 3);
	src_surf_scalars = (miGeoScalar*)mi_db_access(src_fl->surf_scalars);
	surf_scalars = (miGeoScalar*)mi_scene_create(&fl->surf_scalars,
						miSCENE_GEOSCALAR,
						src_fl->no_surf_scalars +
						add_scalars);
	memcpy(surf_scalars, src_surf_scalars,
		sizeof(miGeoScalar) * src_fl->no_surf_scalars);
	mi_db_unpin(src_fl->surf_scalars);
	/* base index for new scalars */
	sbase = src_fl->no_surf_scalars;
	fl->no_surf_scalars += add_scalars;

	faces = (miFace*)mi_scene_edit(fl->faces);

	for (i=0; i < src_fl->no_faces; i++) {
		miFace *face = &faces[i];
		/* copy original surfaces */
		int f1 = cur_surf;
		for (k=0; k < face->no_surfaces; k++) {
			memcpy(&surfaces[cur_surf++],
			       &src_surfaces[face->surface_idx+k],
			       sizeof(miSurface));
		}
		face->surface_idx = f1;
		/* create a texture surface for the first (geometric)
		   surface */
		create_uvtexsurface(&surfaces[cur_surf++],	/* result */
				face,				/* modify */
				&surfaces[face->surface_idx],	/* input  */
				surf_scalars,			/* result */
				sbase+i*(4+4*3),		/* input  */
				b1index);			/* input  */
		face->no_surfaces += 1;
	}

	mi_scene_edit_end(fl->faces);
	mi_scene_edit_end(fl->surf_scalars);
	mi_scene_edit_end(fl->surfaces);
	mi_db_unpin(src_fl->surfaces);

	mi_scene_edit_end(obj_tag);
	mi_db_unpin(src_obj_tag);

	*result = obj_tag;
	return(miTRUE);
}


#include "ngraph/factory.hpp"

#include <mutex>

#include "ngraph/node.hpp"
#include "ngraph/ops.hpp"

using namespace std;

namespace ngraph {

{
    static mutex registry_mutex;
    return registry_mutex;
}
}  // namespace ngraph


{
    static mutex registry_mutex;
    return registry_mutex;
}

#include "ngraph/factory.hpp"

#include <mutex>

#include "ngraph/node.hpp"
#include "ngraph/ops.hpp"

using namespace std;

namespace ngraph {
mutex& get_registry_mutex()


#include "ngraph/factory.hpp"

#include <mutex>

#include "ngraph/node.hpp"
#include "ngraph/ops.hpp"

using namespace std;

namespace ngraph {
mutex& get_registry_mutex() {
    static mutex registry_mutex;
    retur