TEST_P(MatrixMultTest, Identity)
{
    angle::Mat4 testMatrix;
    angle::Mat4 outputMatrix;

    glMultMatrixf(testMatrix.data());
    EXPECT_GL_NO_ERROR();

    glGetFloatv(GL_MODELVIEW_MATRIX, outputMatrix.data());
    EXPECT_GL_NO_ERROR();

    EXPECT_EQ(angle::Mat4(), outputMatrix);
}

// MatrixMultTest.cpp: Tests basic usage of glMultMatrix(f|x).

#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"

#include "common/matrix_utils.h"
#include "util/random_utils.h"

#include <stdint.h>

using namespace angle;

class MatrixMultTest : public ANGLETest
{
  protected:
    MatrixMultTest()
    {
        setWindowWidth(32);
        setWindowHeight(32);
        setConfigRedBits(8);
        setConfigGreenBits(8);
        setConfigBlueBits(8);
        setConfigAlphaBits(8);
        setConfigDepthBits(24);
    }
};

// Multiplies identity matrix with itself.

// Multiplies translation matrix and checks matrix underneath in stack is not affected.
TEST_P(MatrixMultTest, Translation)
{
    glPushMatrix();

    angle::Mat4 testMatrix = angle::Mat4::Translate(angle::Vector3(1.0f, 0.0f, 0.0f));

    angle::Mat4 outputMatrix;

    glMultMatrixf(testMatrix.data());
    EXPECT_GL_NO_ERROR();

    glMultMatrixf(testMatrix.data());
    EXPECT_GL_NO_ERROR();

    glGetFloatv(GL_MODELVIEW_MATRIX, outputMatrix.data());
    EXPECT_GL_NO_ERROR();

    EXPECT_EQ(angle::Mat4::Translate(angle::Vector3(2.0f, 0.0f, 0.0f)), outputMatrix);

    glPopMatrix();

    glGetFloatv(GL_MODELVIEW_MATRIX, outputMatrix.data());
    EXPECT_GL_NO_ERROR();

    EXPECT_EQ(angle::Mat4(), outputMatrix);
}

ANGLE_INSTANTIATE_TEST(MatrixMultTest, ES1_D3D11(), ES1_OPENGL(), ES1_OPENGLES(), ES1_VULKAN());


virtual void SetUp()
	{
		// Code	here will be called	immediately	after the constructor (right
		// before each test).
	}

/**
 * @file  FileTest.cpp
 *
 * @brief Implementation for File Compare testcase.
 */

#include "pch.h"
#include "Resource.h"

namespace
{

using namespace GUITestUtils;

class FileTest : public CommonTest
{
public:
	FileTest()
	{

	}

	virtual ~FileTest()
	{
		// You can do clean-up work	that doesn't throw exceptions here.
		PostMessage(m_hwndWinMerge, WM_CLOSE, 0, 0);
		waitUntilProcessExit(m_hwndWinMerge);
	}

	static void SetUpTestCase()
	{
		// You can do set-up work for each test	here.
	}

	static void TearDownTestCase()
	{
	}

	// If	the	constructor	and	destructor are not enough for setting up
	// and cleaning up each test, you can define the following methods:

	virtual void TearDown()
	{
		// Code	here will be called	immediately	after each test	(right
		// before the destructor).
		PostMessage(m_hwndWinMerge, WM_CLOSE, 0, 0);
		waitUntilProcessExit(m_hwndWinMerge);
	}

	void SetUp3WayCompare()
	{
		std::string dir1 = (getModuleFolder() / "..\\..\\Data\\Compare\\Dir1\\file123_diffsize1.txt").u8string();
		std::string dir2 = (getModuleFolder() / "..\\..\\Data\\Compare\\Dir2\\file123_diffsize1.txt").u8string();
		std::string dir3 = (getModuleFolder() / "..\\..\\Data\\Compare\\Dir3\\file123_diffsize1.txt").u8string();
		std::string lang = std::to_string(GetParam());
		m_hwndWinMerge = execWinMerge(("/noprefs /maximize /cfg Locale/LanguageId=" + lang + " /r " + dir1 + " " + dir2 + " " + dir3).c_str());
	}

	void SetUp2WayCompare()
	{
		std::string dir1 = (getModuleFolder() / "..\\..\\Data\\Compare\\Dir1\\file123_diffsize1.txt").u8string();
		std::string dir2 = (getModuleFolder() / "..\\..\\Data\\Compare\\Dir2\\file123_diffsize1.txt").u8string();
		std::string lang = std::to_string(GetParam());
		m_hwndWinMerge = execWinMerge(("/noprefs /maximize /cfg Locale/LanguageId=" + lang + " /r " + dir1 + " " + dir2).c_str());
	}

	void SetUp3WayCompareFiles()
	{
		std::string dir1 = (getModuleFolder() / "..\\..\\Data\\Compare1\\Dir1\\").u8string();
		std::string dir2 = (getModuleFolder() / "..\\..\\Data\\Compare1\\Dir2\\").u8string();
		std::string dir3 = (getModuleFolder() / "..\\..\\Data\\Compare1\\Dir3\\").u8string();
		std::string lang = std::to_string(GetParam());
		m_hwndWinMerge = execWinMerge(("/noprefs /maximize /cfg Locale/LanguageId=" + lang + " /r " + dir1 + " " + dir2 + " " + dir3).c_str());
	}
	// Objects declared here can be used by all tests in the test case for Foo.
};

TEST_P(FileTest, ViewSwapPanes3Way)
{
	SetUp3WayCompare();
	Sleep(200);
	selectMenuAndSaveWindowImage(ID_SWAPPANES_SWAP12);
	Sleep(200);
	selectMenuAndSaveWindowImage(ID_SWAPPANES_SWAP23);
	Sleep(200);
	selectMenuAndSaveWindowImage(ID_SWAPPANES_SWAP13);
	Sleep(200);
}

TEST_P(FileTest, ViewSwapPanes2Way)
{
	SetUp2WayCompare();
	Sleep(200);
	selectMenuAndSaveWindowImage(ID_SWAPPANES_SWAP12);
	Sleep(200);
}

TEST_P(FileTest, ViewSwapPanes2WayInvalid)
{
	SetUp2WayCompare();
	Sleep(200);
	selectMenuAndSaveWindowImage(ID_SWAPPANES_SWAP12);
	Sleep(200);
	// The next 2 options are disabled, nothing should happen, no exceptions etc.
	selectMenuAndSaveWindowImage(ID_SWAPPANES_SWAP23);
	Sleep(200);
	selectMenuAndSaveWindowImage(ID_SWAPPANES_SWAP13);
	Sleep(200);
}

TEST_P(FileTest, ViewFileNavigation3Way)
{
	SetUp3WayCompareFiles();
	Sleep(200);
	selectMenuAndSaveWindowImage(ID_MERGE_COMPARE);
	Sleep(200);
	selectMenuAsync(ID_LASTFILE);
	Sleep(2000);
	//The confirmation message box in test mode seems to come up irrespective of saved options
	selectYesOnFileNavConfirmationMessageBox();
	Sleep(200);

	selectMenuAsync(ID_FIRSTFILE);
	Sleep(2000);
	selectYesOnFileNavConfirmationMessageBox();
	Sleep(200);

	selectMenuAsync(ID_NEXTFILE);
	Sleep(2000);
	selectYesOnFileNavConfirmationMessageBox();
	Sleep(200);

	selectMenuAsync(ID_PREVFILE);
	Sleep(2000);
	selectYesOnFileNavConfirmationMessageBox();
	Sleep(200);

}

}

INSTANTIATE_TEST_SUITE_P(FileTestInstance,
	FileTest,
	testing::ValuesIn(GUITestUtils::languages()));


void get_handler( const shared_ptr< Session > session )
{
    session->close( 200, "Hello, World!", { { "Content-Length", "13" }, { "Connection", "close" } } );
}

//System Includes
#include <map>
#include <thread>
#include <string>
#include <memory>
#include <ciso646>
#include <stdexcept>
#include <functional>

//Project Includes
#include <restbed>

//External Includes
#include <catch.hpp>

//System Namespaces
using std::stoi;
using std::thread;
using std::string;
using std::multimap;
using std::to_string;
using std::shared_ptr;
using std::make_shared;

//Project Namespaces
using namespace restbed;

//External Namespaces

SCENARIO( "establish connection to reachable server", "[client]" )
{
    auto resource = make_shared< Resource >( );
    resource->set_path( "/resource" );
    resource->set_method_handler( "GET", get_handler );

    auto settings = make_shared< Settings >( );
    settings->set_port( 1984 );

    shared_ptr< thread > worker = nullptr;

    Service service;
    service.publish( resource );
    service.set_ready_handler( [ &worker ]( Service & service )
    {
        worker = make_shared< thread >( [ &service ] ( )
        {
            GIVEN( "I have started a service" )
            {
                WHEN( "I perform a HTTP 'GET' request" )
                {
                    auto request = make_shared< Request >( );
                    request->set_port( 1984 );
                    request->set_host( "localhost" );
                    request->set_method( "GET" );
                    request->set_path( "/resource" );

                    auto response = Http::sync( request );

                    THEN( "I should see a '200' (OK) status code" )
                    {
                        REQUIRE( 200 == response->get_status_code( ) );
                    }

                    AND_THEN( "I should see a response body of 'Hello, World!'" )
                    {
                        auto actual = Http::fetch( 13, response );
                        Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' };
                        REQUIRE( actual == expectation );
                    }

                    multimap< string, string > headers = response->get_headers( );

                    AND_THEN( "I should see a 'Connection' header value of 'close'" )
                    {
                        auto header = headers.find( "Connection" );
                        REQUIRE( header not_eq headers.end( ) );
                        REQUIRE( "close" == headers.find( "Connection" )->second );
                    }

                    AND_THEN( "I should see a 'Content-Length' header value of '13'" )
                    {
                        auto header = headers.find( "Content-Length" );
                        REQUIRE( header not_eq headers.end( ) );
                        REQUIRE( "13" == headers.find( "Content-Length" )->second );
                    }
                }

                service.stop( );
            }
        } );
    } );

    service.start( settings );
    worker->join( );
}

SCENARIO( "establish connection to unreachable server", "[client]" )
{
    GIVEN( "I have not started a service" )
    {
        WHEN( "I perform a HTTP 'GET' request" )
        {
            auto request = make_shared< Request >( );
            request->set_port( 1984 );
            request->set_protocol( "HTTP" );
            request->set_version( 1.1 );
            request->set_host( "localhost" );
            request->set_method( "GET" );
            request->set_path( "/resource" );

            auto response = Http::sync( request );

            THEN( "I should see a '0' (Error) status code" )
            {
                REQUIRE( 0 == response->get_status_code( ) );
                REQUIRE( "Error" == response->get_status_message( ) );
            }

            AND_THEN( "I should see a 'HTTP' response protocol" )
            {
                REQUIRE( "HTTP" == response->get_protocol( ) );
            }

            AND_THEN( "I should see a '1.1' response version" )
            {
                REQUIRE( 1.1 == response->get_version( ) );
            }

            multimap< string, string > headers = response->get_headers( );

            AND_THEN( "I should see a 'Content-Type' header value of 'text/plain; utf-8'" )
            {
                auto header = headers.find( "Content-Type" );
                REQUIRE( header not_eq headers.end( ) );
                REQUIRE( "text/plain; utf-8" == headers.find( "Content-Type" )->second );
            }

            AND_THEN( "I should see a 'Content-Length' header value greater than zero" )
            {
                auto header = headers.find( "Content-Length" );
                REQUIRE( header not_eq headers.end( ) );
                REQUIRE( stoi( headers.find( "Content-Length" )->second ) > 0 );
            }

            AND_THEN( "I should see a response body detailing the error" )
            {
                REQUIRE( response->get_body( ).size( ) > 0 );
            }
        }
    }
}


NIVissimSingleTypeParser_VWunschentscheidungsdefinition::NIVissimSingleTypeParser_VWunschentscheidungsdefinition(NIImporter_Vissim& parent)
    : NIImporter_Vissim::VissimSingleTypeParser(parent) {}

/****************************************************************************/
// Eclipse SUMO, Simulation of Urban MObility; see http://example.com
/****************************************************************************/
/// @file    NIVissimSingleTypeParser_VWunschentscheidungsdefinition.cpp
/// @author  Daniel Krajzewicz
/// @author  Michael Behrisch
/// @date    Wed, 18 Dec 2002
/****************************************************************************/
#include <config.h>

#include <iostream>
#include <vector>
#include <cassert>
#include <utils/common/StringUtils.h>
#include "../NIImporter_Vissim.h"
#include "../tempstructs/NIVissimEdge.h"
#include "../tempstructs/NIVissimConnection.h"
#include "NIVissimSingleTypeParser_VWunschentscheidungsdefinition.h"

// method definitions

NIVissimSingleTypeParser_VWunschentscheidungsdefinition::~NIVissimSingleTypeParser_VWunschentscheidungsdefinition() {}

bool
NIVissimSingleTypeParser_VWunschentscheidungsdefinition::parse(std::istream& from) {
    std::string tag;
    from >> tag; // id
    from >> tag; // name
    tag = readName(from);
    tag = overrideOptionalLabel(from);
    from >> tag; // strecke
    std::string edgeid;
    from >> edgeid;
    from >> tag; // spur
    std::string lane;
    from >> lane;
    from >> tag; // bei
    std::string pos;
    from >> pos;
    from >> tag; // fahrzeugklasse
    from >> tag; // <fahrzeugklasse>
    from >> tag; // vwunsch
    std::string vwunsch;
    from >> vwunsch; // vwunsch
    std::vector<std::string> tmp;
    tmp.push_back("zeit");
    tmp.push_back("fahrzeugklasse");
    tag = readEndSecure(from, tmp);
    while (tag != "DATAEND" && tag != "zeit") {
        from >> tag;
        from >> tag;
        from >> tag;
        tag = myRead(from);
    }
    if (tag == "zeit") {
        from >> tag;
        from >> tag;
        from >> tag;
        from >> tag;
    }
    int numid = StringUtils::toInt(edgeid);
    int numlane = StringUtils::toInt(lane) - 1;
    int numv = StringUtils::toInt(vwunsch);
    NIVissimEdge* e = NIVissimEdge::dictionary(numid);
    if (e == nullptr) {
        NIVissimConnection* c = NIVissimConnection::dictionary(numid);
        const std::vector<int>& lanes = c->getToLanes();
        e = NIVissimEdge::dictionary(c->getToEdgeID());
        for (std::vector<int>::const_iterator j = lanes.begin(); j != lanes.end(); j++) {
            e->setSpeed((*j), numv);
        }
        assert(e != 0);
    } else {
        e->setSpeed(numlane, numv);
    }
    return true;
}

/****************************************************************************/


while (tag != "DATAEND" && tag != "zeit") {
        from >> tag;
        from >> tag;
        from >> tag;
        tag = myRead(from);
    }

/****************************************************************************/
// Eclipse SUMO, Simulation of Urban MObility; see http://example.com
/****************************************************************************/
/// @file    NIVissimSingleTypeParser_VWunschentscheidungsdefinition.cpp
/// @author  Daniel Krajzewicz
/// @author  Michael Behrisch
/// @date    Wed, 18 Dec 2002
/****************************************************************************/
#include <config.h>

#include <iostream>
#include <vector>
#include <cassert>
#include <utils/common/StringUtils.h>
#include "../NIImporter_Vissim.h"
#include "../tempstructs/NIVissimEdge.h"
#include "../tempstructs/NIVissimConnection.h"
#include "NIVissimSingleTypeParser_VWunschentscheidungsdefinition.h"

// method definitions
NIVissimSingleTypeParser_VWunschentscheidungsdefinition::NIVissimSingleTypeParser_VWunschentscheidungsdefinition(NIImporter_Vissim& parent)
    : NIImporter_Vissim::VissimSingleTypeParser(parent) {}

NIVissimSingleTypeParser_VWunschentscheidungsdefinition::~NIVissimSingleTypeParser_VWunschentscheidungsdefinition() {}

bool
NIVissimSingleTypeParser_VWunschentscheidungsdefinition::parse(std::istream& from) {
    std::string tag;
    from >> tag; // id
    from >> tag; // name
    tag = readName(from);
    tag = overrideOptionalLabel(from);
    from >> tag; // strecke
    std::string edgeid;
    from >> edgeid;
    from >> tag; // spur
    std::string lane;
    from >> lane;
    from >> tag; // bei
    std::string pos;
    from >> pos;
    from >> tag; // fahrzeugklasse
    from >> tag; // <fahrzeugklasse>
    from >> tag; // vwunsch
    std::string vwunsch;
    from >> vwunsch; // vwunsch
    std::vector<std::string> tmp;
    tmp.push_back("zeit");
    tmp.push_back("fahrzeugklasse");
    tag = readEndSecure(from, tmp);

    if (tag == "zeit") {
        from >> tag;
        from >> tag;
        from >> tag;
        from >> tag;
    }
    int numid = StringUtils::toInt(edgeid);
    int numlane = StringUtils::toInt(lane) - 1;
    int numv = StringUtils::toInt(vwunsch);
    NIVissimEdge* e = NIVissimEdge::dictionary(numid);
    if (e == nullptr) {
        NIVissimConnection* c = NIVissimConnection::dictionary(numid);
        const std::vector<int>& lanes = c->getToLanes();
        e = NIVissimEdge::dictionary(c->getToEdgeID());
        for (std::vector<int>::const_iterator j = lanes.begin(); j != lanes.end(); j++) {
            e->setSpeed((*j), numv);
        }
        assert(e != 0);
    } else {
        e->setSpeed(numlane, numv);
    }
    return true;
}

/****************************************************************************/


switch (mode)
        {
            case Image::BitmapData::writeOnly:  DataReleaser<Dummy,  Writer>::initialise (frameBuffer, bitmapData, x, y); break;
            case Image::BitmapData::readOnly:   DataReleaser<Reader, Dummy> ::initialise (frameBuffer, bitmapData, x, y); break;
            case Image::BitmapData::readWrite:  DataReleaser<Reader, Writer>::initialise (frameBuffer, bitmapData, x, y); break;
            default:                            jassertfalse; break;
        }

/*
  ==============================================================================

   This file is part of the JUCE library.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   Privacy Policy: www.juce.com/juce-privacy-policy

   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

class OpenGLFrameBufferImage   : public ImagePixelData
{
public:
    OpenGLFrameBufferImage (OpenGLContext& c, int w, int h)
        : ImagePixelData (Image::ARGB, w, h),
          context (c),
          pixelStride (4),
          lineStride (width * pixelStride)
    {
    }

    bool initialise()
    {
        if (! frameBuffer.initialise (context, width, height))
            return false;

        frameBuffer.clear (Colours::transparentBlack);
        return true;
    }

    std::unique_ptr<LowLevelGraphicsContext> createLowLevelContext() override
    {
        sendDataChangeMessage();
        return createOpenGLGraphicsContext (context, frameBuffer);
    }

    std::unique_ptr<ImageType> createType() const override     { return std::make_unique<OpenGLImageType>(); }

    ImagePixelData::Ptr clone() override
    {
        std::unique_ptr<OpenGLFrameBufferImage> im (new OpenGLFrameBufferImage (context, width, height));

        if (! im->initialise())
            return ImagePixelData::Ptr();

        Image newImage (im.release());
        Graphics g (newImage);
        g.drawImageAt (Image (*this), 0, 0, false);

        return ImagePixelData::Ptr (newImage.getPixelData());
    }

    void initialiseBitmapData (Image::BitmapData& bitmapData, int x, int y, Image::BitmapData::ReadWriteMode mode) override
    {
        bitmapData.pixelFormat = pixelFormat;
        bitmapData.lineStride  = lineStride;
        bitmapData.pixelStride = pixelStride;

        if (mode != Image::BitmapData::readOnly)
            sendDataChangeMessage();
    }

    OpenGLContext& context;
    OpenGLFrameBuffer frameBuffer;

private:
    int pixelStride, lineStride;

    struct Dummy
    {
        Dummy (OpenGLFrameBuffer&, int, int, int, int) noexcept {}
        static void read (OpenGLFrameBuffer&, Image::BitmapData& , int, int) noexcept {}
        static void write (const PixelARGB*) noexcept {}
    };

    struct Reader
    {
        static void read (OpenGLFrameBuffer& frameBuffer, Image::BitmapData& bitmapData, int x, int y)
        {
            frameBuffer.readPixels ((PixelARGB*) bitmapData.data,
                                    Rectangle<int> (x, frameBuffer.getHeight() - (y + bitmapData.height), bitmapData.width, bitmapData.height));

            verticalRowFlip ((PixelARGB*) bitmapData.data, bitmapData.width, bitmapData.height);
        }

        static void verticalRowFlip (PixelARGB* const data, const int w, const int h)
        {
            HeapBlock<PixelARGB> tempRow (w);
            auto rowSize = (size_t) w * sizeof (PixelARGB);

            for (int y = 0; y < h / 2; ++y)
            {
                PixelARGB* const row1 = data + y * w;
                PixelARGB* const row2 = data + (h - 1 - y) * w;
                memcpy (tempRow, row1, rowSize);
                memcpy (row1, row2, rowSize);
                memcpy (row2, tempRow, rowSize);
            }
        }
    };

    struct Writer
    {
        Writer (OpenGLFrameBuffer& fb, int x, int y, int w, int h) noexcept
            : frameBuffer (fb), area (x, y, w, h)
        {}

        void write (const PixelARGB* const data) const noexcept
        {
            HeapBlock<PixelARGB> invertedCopy (area.getWidth() * area.getHeight());
            auto rowSize = (size_t) area.getWidth() * sizeof (PixelARGB);

            for (int y = 0; y < area.getHeight(); ++y)
                memcpy (invertedCopy + area.getWidth() * y,
                        data + area.getWidth() * (area.getHeight() - 1 - y), rowSize);

            frameBuffer.writePixels (invertedCopy, area);
        }

        OpenGLFrameBuffer& frameBuffer;
        const Rectangle<int> area;

        JUCE_DECLARE_NON_COPYABLE (Writer)
    };

    template <class ReaderType, class WriterType>
    struct DataReleaser  : public Image::BitmapData::BitmapDataReleaser
    {
        DataReleaser (OpenGLFrameBuffer& fb, int x, int y, int w, int h)
            : data ((size_t) (w * h)),
              writer (fb, x, y, w, h)
        {}

        ~DataReleaser()
        {
            writer.write (data);
        }

        static void initialise (OpenGLFrameBuffer& frameBuffer, Image::BitmapData& bitmapData, int x, int y)
        {
            auto* r = new DataReleaser (frameBuffer, x, y, bitmapData.width, bitmapData.height);
            bitmapData.dataReleaser.reset (r);

            bitmapData.data = (uint8*) r->data.get();
            bitmapData.lineStride = (bitmapData.width * bitmapData.pixelStride + 3) & ~3;

            ReaderType::read (frameBuffer, bitmapData, x, y);
        }

        HeapBlock<PixelARGB> data;
        WriterType writer;
    };

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLFrameBufferImage)
};

OpenGLImageType::OpenGLImageType() {}
OpenGLImageType::~OpenGLImageType() {}

int OpenGLImageType::getTypeID() const
{
    return 3;
}

ImagePixelData::Ptr OpenGLImageType::create (Image::PixelFormat, int width, int height, bool /*shouldClearImage*/) const
{
    OpenGLContext* currentContext = OpenGLContext::getCurrentContext();
    jassert (currentContext != nullptr); // an OpenGL image can only be created when a valid context is active!

    std::unique_ptr<OpenGLFrameBufferImage> im (new OpenGLFrameBufferImage (*currentContext, width, height));

    if (! im->initialise())
        return ImagePixelData::Ptr();

    return *im.release();
}

OpenGLFrameBuffer* OpenGLImageType::getFrameBufferFrom (const Image& image)
{
    if (OpenGLFrameBufferImage* const glImage = dynamic_cast<OpenGLFrameBufferImage*> (image.getPixelData()))
        return &(glImage->frameBuffer);

    return nullptr;
}

} // namespace juce


Matrix<N, T> operator()() const { return Matrix<N, T>(); }

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

#include <quda.h>
#include <host_utils.h>
#include <misc.h>
#include <hisq_force_reference.h>

extern int Z[4];
extern int V;
extern int Vh;

#define CADD(a, b, c)                                                                                \
  {                                                                                \
    (c).real = (a).real + (b).real;                                                                                \
    (c).imag = (a).imag + (b).imag;                                                                                \
  }
#define CMUL(a, b, c)                                                                                \
  {                                                                                \
    (c).real = (a).real * (b).real - (a).imag * (b).imag;                                                              \
    (c).imag = (a).real * (b).imag + (a).imag * (b).real;                                                              \
  }
#define CSUM(a, b)                                                                                \
  {                                                                                \
    (a).real += (b).real;                                                                                \
    (a).imag += (b).imag;                                                                                \
  }

/* c = a* * b */
#define CMULJ_(a, b, c)                                                                                \
  {                                                                                \
    (c).real = (a).real * (b).real + (a).imag * (b).imag;                                                              \
    (c).imag = (a).real * (b).imag - (a).imag * (b).real;                                                              \
  }

/* c = a * b* */
#define CMUL_J(a, b, c)                                                                                \
  {                                                                                \
    (c).real = (a).real * (b).real + (a).imag * (b).imag;                                                              \
    (c).imag = (a).imag * (b).real - (a).real * (b).imag;                                                              \
  }

#define CONJG(a, b)                                                                                \
  {                                                                                \
    (b).real = (a).real;                                                                                \
    (b).imag = -(a).imag;                                                                                \
  }

typedef struct {
  float real;
  float imag;
} fcomplex;

/* specific for double complex */
typedef struct {
  double real;
  double imag;
} dcomplex;

typedef struct {
  fcomplex e[3][3];
} fsu3_matrix;
typedef struct {
  fcomplex c[3];
} fsu3_vector;
typedef struct {
  dcomplex e[3][3];
} dsu3_matrix;
typedef struct {
  dcomplex c[3];
} dsu3_vector;

typedef struct {
  fcomplex m01, m02, m12;
  float m00im, m11im, m22im;
  float space;
} fanti_hermitmat;

typedef struct {
  dcomplex m01, m02, m12;
  double m00im, m11im, m22im;
  double space;
} danti_hermitmat;

typedef struct {
  fsu3_vector h[2];
} fhalf_wilson_vector;
typedef struct {
  dsu3_vector h[2];
} dhalf_wilson_vector;

template <typename su3_matrix> su3_matrix *get_su3_matrix(int gauge_order, su3_matrix *p, int idx, int dir)
{
  if (gauge_order == QUDA_MILC_GAUGE_ORDER) {
    return (p + 4 * idx + dir);
  } else if (gauge_order == QUDA_QDP_GAUGE_ORDER) { // This is nasty!
    su3_matrix *data = ((su3_matrix **)p)[dir];
    return data + idx;
  } else {
    errorQuda("get_su3_matrix: unsupported ordering scheme!\n");
  }
  return NULL;
}

template <typename su3_vector, typename su3_matrix> void su3_projector(su3_vector *a, su3_vector *b, su3_matrix *c)
{
  for (int i = 0; i < 3; i++)
    for (int j = 0; j < 3; j++) CMUL_J(a->c[i], b->c[j], c->e[i][j]);
}

template <typename half_wilson_vector, typename su3_matrix>
void computeLinkOrderedOuterProduct(half_wilson_vector *src, su3_matrix *dest, int gauge_order)
{
  int dx[4];
  for (int i = 0; i < V; ++i) {
    for (int dir = 0; dir < 4; dir++) {
      dx[3] = dx[2] = dx[1] = dx[0] = 0;
      dx[dir] = 1;
      int nbr_idx = neighborIndexFullLattice(i, dx[3], dx[2], dx[1], dx[0]);
      half_wilson_vector *hw = src + nbr_idx;
      su3_matrix *p = get_su3_matrix(gauge_order, dest, i, dir);
      su3_projector(&(hw->h[0]), &src[i].h[0], p);
    } // dir
  }   // i
}

template <typename half_wilson_vector, typename su3_matrix>
void computeLinkOrderedOuterProduct(half_wilson_vector *src, su3_matrix *dest, size_t nhops, int gauge_order)
{
  int dx[4];
  for (int i = 0; i < V; ++i) {
    for (int dir = 0; dir < 4; ++dir) {
      dx[3] = dx[2] = dx[1] = dx[0] = 0;
      dx[dir] = nhops;
      int nbr_idx = neighborIndexFullLattice(i, dx[3], dx[2], dx[1], dx[0]);
      half_wilson_vector *hw = src + nbr_idx;
      su3_matrix *p = get_su3_matrix(gauge_order, dest, i, dir);
      su3_projector(&(hw->h[0]), &src[i].h[0], p);
    } // dir
  }   // i
}

void computeLinkOrderedOuterProduct(void *src, void *dst, QudaPrecision precision, int gauge_order)
{
  if (precision == QUDA_SINGLE_PRECISION) {
    computeLinkOrderedOuterProduct((fhalf_wilson_vector *)src, (fsu3_matrix *)dst, gauge_order);
  } else {
    computeLinkOrderedOuterProduct((dhalf_wilson_vector *)src, (dsu3_matrix *)dst, gauge_order);
  }
}

void computeLinkOrderedOuterProduct(void *src, void *dst, QudaPrecision precision, size_t nhops, int gauge_order)
{
  if (precision == QUDA_SINGLE_PRECISION) {
    computeLinkOrderedOuterProduct((fhalf_wilson_vector *)src, (fsu3_matrix *)dst, nhops, gauge_order);
  } else {
    computeLinkOrderedOuterProduct((dhalf_wilson_vector *)src, (dsu3_matrix *)dst, nhops, gauge_order);
  }
}

#define RETURN_IF_ERR                                                                                \
  if (err) return;

extern int gauge_order;
extern int Vh;
extern int Vh_ex;

template <int N> struct Sign {
  static const int result = 1;
};

template <> struct Sign<1> {
  static const int result = -1;
};

template <class T, class U> struct Promote {
  typedef T Type;
};

template <> struct Promote<int, float> {
  typedef float Type;
};

template <> struct Promote<float, int> {
  typedef float Type;
};

template <> struct Promote<int, double> {
  typedef double Type;
};

template <> struct Promote<double, int> {
  typedef double Type;
};

template <> struct Promote<float, double> {
  typedef double Type;
};

template <> struct Promote<double, float> {
  typedef double Type;
};

template <> struct Promote<int, std::complex<float>> {
  typedef std::complex<float> Type;
};

template <> struct Promote<std::complex<float>, int> {
  typedef std::complex<float> Type;
};

template <> struct Promote<float, std::complex<float>> {
  typedef std::complex<float> Type;
};

template <> struct Promote<int, std::complex<double>> {
  typedef std::complex<double> Type;
};

template <> struct Promote<std::complex<double>, int> {
  typedef std::complex<double> Type;
};

template <> struct Promote<float, std::complex<double>> {
  typedef std::complex<double> Type;
};

template <> struct Promote<std::complex<double>, float> {
  typedef std::complex<double> Type;
};

template <> struct Promote<double, std::complex<double>> {
  typedef std::complex<double> Type;
};

template <> struct Promote<std::complex<double>, double> {
  typedef std::complex<double> Type;
};

template <int N, class T> class Matrix
{
private:
  T data[N][N];

public:
  Matrix(); // default constructor
  Matrix(const Matrix<N, T> &) = default;
  Matrix(Matrix<N, T> &&) = default;
  Matrix &operator=(const Matrix<N, T> &) = default;
  Matrix &operator=(Matrix<N, T> &&) = default;
  Matrix &operator+=(const Matrix<N, T> &mat);
  Matrix &operator-=(const Matrix<N, T> &mat);
  const T &operator()(int i, int j) const;
  T &operator()(int i, int j);
};

template <int N, class T> Matrix<N, T>::Matrix()
{
  for (int i = 0; i < N; ++i) {
    for (int j = 0; j < N; ++j) { data[i][j] = static_cast<T>(0); }
  }
}

template <int N, class T> T &Matrix<N, T>::operator()(int i, int j) { return data[i][j]; }

template <int N, class T> const T &Matrix<N, T>::operator()(int i, int j) const { return data[i][j]; }

template <int N, class T> Matrix<N, T> &Matrix<N, T>::operator+=(const Matrix<N, T> &mat)
{
  for (int i = 0; i < N; ++i) {
    for (int j = 0; j < N; ++j) { data[i][j] += mat.data[i][j]; }
  }
  return *this;
}

template <int N, class T> Matrix<N, T> &Matrix<N, T>::operator-=(const Matrix<N, T> &mat)
{
  for (int i = 0; i < N; ++i) {
    for (int j = 0; j < N; ++j) { data[i][j] -= mat.data[i][j]; }
  }
  return *this;
}

template <int N, class T> Matrix<N, T> operator+(const Matrix<N, T> &a, const Matrix<N, T> &b)
{
  Matrix<N, T> result(a);
  result += b;
  return result;
}

template <int N, class T> Matrix<N, T> operator-(const Matrix<N, T> &a, const Matrix<N, T> &b)
{
  Matrix<N, T> result(a);
  result -= b;
  return result;
}

template <int N, class T> Matrix<N, T> operator*(const Matrix<N, T> &a, const Matrix<N, T> &b)
{
  Matrix<N, T> result;
  for (int i = 0; i < N; ++i) {
    for (int j = 0; j < N; ++j) {
      result(i, j) = static_cast<T>(0);
      for (int k = 0; k < N; ++k) { result(i, j) += a(i, k) * b(k, j); }
    }
  }
  return result;
}

template <int N, class T> Matrix<N, std::complex<T>> conj(const Matrix<N, std::complex<T>> &mat)
{
  Matrix<N, std::complex<T>> result;
  for (int i = 0; i < N; ++i) {
    for (int j = 0; j < N; ++j) { result(i, j) = std::conj(mat(j, i)); }
  }
  return result;
}

template <int N, class T> Matrix<N, T> transpose(const Matrix<N, std::complex<T>> &mat)
{
  Matrix<N, T> result;
  for (int i = 0; i < N; ++i) {
    for (int j = 0; j < N; ++j) { result(i, j) = mat(j, i); }
  }
  return result;
}

template <int N, class T, class U>
Matrix<N, typename Promote<T, U>::Type> operator*(const Matrix<N, T> &mat, const U &scalar)
{
  typedef typename Promote<T, U>::Type return_type;
  Matrix<N, return_type> result;

  for (int i = 0; i < N; ++i) {
    for (int j = 0; j < N; ++j) { result(i, j) = scalar * mat(i, j); }
  }
  return result;
}

template <int N, class T, class U>
Matrix<N, typename Promote<T, U>::Type> operator*(const U &scalar, const Matrix<N, T> &mat)
{
  return mat * scalar;
}

template <int N, class T> struct Identity {
  Matrix<N, T> operator()() const
  {
    Matrix<N, T> id;
    for (int i = 0; i < N; ++i) { id(i, i) = static_cast<T>(1); }
    return id;
  } // operator()
};

template <int N, class T> struct Zero {
  // the default constructor zeros all matrix elements

};

template <int N, class T> std::ostream &operator<<(std::ostream &os, const Matrix<N, T> &m)
{
  for (int i = 0; i < N; ++i) {
    for (int j = 0; j < N; ++j) { os << m(i, j) << " "; }
    if (i < N - 1) os << std::endl;
  }
  return os;
}

template <class Real> class LoadStore
{
private:
  const int volume;
  const int half_volume;

public:
  LoadStore(int vol) : volume(vol), half_volume(vol / 2) { }

  void loadMatrixFromField(const Real *const field, int oddBit, int half_lattice_index,
                           Matrix<3, std::complex<Real>> *const mat) const;

  void loadMatrixFromField(const Real *const field, int oddBit, int dir, int half_lattice_index,
                           Matrix<3, std::complex<Real>> *const mat) const;

  void storeMatrixToField(const Matrix<3, std::complex<Real>> &mat, int oddBit, int half_lattice_index,
                          Real *const field) const;

  void addMatrixToField(const Matrix<3, std::complex<Real>> &mat, int oddBit, int half_lattice_index, Real coeff,
                        Real *const) const;

  void addMatrixToField(const Matrix<3, std::complex<Real>> &mat, int oddBit, int dir, int half_lattice_index,
                        Real coeff, Real *const) const;

  void storeMatrixToMomentumField(const Matrix<3, std::complex<Real>> &mat, int oddBit, int dir, int half_lattice_index,
                                  Real coeff, Real *const) const;
  Real getData(const Real *const field, int idx, int dir, int oddBit, int offset, int hfv) const;
  void addData(Real *const field, int idx, int dir, int oddBit, int offset, Real, int hfv) const;
  int half_idx_conversion_ex2normal(int half_lattice_index, const int *dim, int oddBit) const;
  int half_idx_conversion_normal2ex(int half_lattice_index, const int *dim, int oddBit) const;
};

template <class Real>
int LoadStore<Real>::half_idx_conversion_ex2normal(int half_lattice_index_ex, const int *dim, int oddBit) const
{
  int X1 = dim[0];
  int X2 = dim[1];
  int X3 = dim[2];

  int E1 = dim[0] + 4;
  int E2 = dim[1] + 4;
  int E3 = dim[2] + 4;
  int E1h = E1 / 2;

  int sid = half_lattice_index_ex;

  int za = sid / E1h;
  int x1h = sid - za * E1h;
  int zb = za / E2;
  int x2 = za - zb * E2;
  int x4 = zb / E3;
  int x3 = zb - x4 * E3;
  int x1odd = (x2 + x3 + x4 + oddBit) & 1;
  int x1 = 2 * x1h + x1odd;

  int idx = ((x4 - 2) * X3 * X2 * X1 + (x3 - 2) * X2 * X1 + (x2 - 2) * X1 + (x1 - 2)) / 2;
  return idx;
}

template <class Real>
int LoadStore<Real>::half_idx_conversion_normal2ex(int half_lattice_index, const int *dim, int oddBit) const
{
  int X1 = dim[0];
  int X2 = dim[1];
  int X3 = dim[2];
  int X1h = X1 / 2;

  int E1 = dim[0] + 4;
  int E2 = dim[1] + 4;
  int E3 = dim[2] + 4;

  int sid = half_lattice_index;

  int za = sid / X1h;
  int x1h = sid - za * X1h;
  int zb = za / X2;
  int x2 = za - zb * X2;
  int x4 = zb / X3;
  int x3 = zb - x4 * X3;
  int x1odd = (x2 + x3 + x4 + oddBit) & 1;
  int x1 = 2 * x1h + x1odd;

  int idx = ((x4 + 2) * E3 * E2 * E1 + (x3 + 2) * E2 * E1 + (x2 + 2) * E1 + (x1 + 2)) / 2;

  return idx;
}

template <class Real>
Real LoadStore<Real>::getData(const Real *const field, int idx, int dir, int oddBit, int offset, int hfv) const
{
  if (gauge_order == QUDA_MILC_GAUGE_ORDER) {
    return field[(4 * hfv * oddBit + 4 * idx + dir) * 18 + offset];
  } else { // QDP format
    return ((Real **)field)[dir][(hfv * oddBit + idx) * 18 + offset];
  }
}
template <class Real>
void LoadStore<Real>::addData(Real *const field, int idx, int dir, int oddBit, int offset, Real v, int hfv) const
{
  if (gauge_order == QUDA_MILC_GAUGE_ORDER) {
    field[(4 * hfv * oddBit + 4 * idx + dir) * 18 + offset] += v;
  } else { // QDP format
    ((Real **)field)[dir][(hfv * oddBit + idx) * 18 + offset] += v;
  }
}

template <class Real>
void LoadStore<Real>::loadMatrixFromField(const Real *const field, int oddBit, int half_lattice_index,
                                          Matrix<3, std::complex<Real>> *const mat) const
{
#ifdef MULTI_GPU
  int hfv = Vh_ex;
#else
  int hfv = Vh;
#endif

  int offset = 0;
  for (int i = 0; i < 3; ++i) {
    for (int j = 0; j < 3; ++j) {
      (*mat)(i, j) = (*(field + (oddBit * hfv + half_lattice_index) * 18 + offset++));
      (*mat)(i, j) += std::complex<Real>(0, *(field + (oddBit * hfv + half_lattice_index) * 18 + offset++));
    }
  }
}

template <class Real>
void LoadStore<Real>::loadMatrixFromField(const Real *const field, int oddBit, int dir, int half_lattice_index,
                                          Matrix<3, std::complex<Real>> *const mat) const
{
#ifdef MULTI_GPU
  int hfv = Vh_ex;
#else
  int hfv = Vh;
#endif

  int offset = 0;
  for (int i = 0; i < 3; ++i) {
    for (int j = 0; j < 3; ++j) {
      (*mat)(i, j) = (getData(field, half_lattice_index, dir, oddBit, offset++, hfv));
      (*mat)(i, j) += std::complex<Real>(0, getData(field, half_lattice_index, dir, oddBit, offset++, hfv));
    }
  }
}

template <class Real>
void LoadStore<Real>::storeMatrixToField(const Matrix<3, std::complex<Real>> &mat, int oddBit, int half_lattice_index,
                                         Real *const field) const
{
#ifdef MULTI_GPU
  int hfv = Vh_ex;
#else
  int hfv = Vh;
#endif

  int offset = 0;
  for (int i = 0; i < 3; ++i) {
    for (int j = 0; j < 3; ++j) {
      *(field + (oddBit * hfv + half_lattice_index) * 18 + offset++) = (mat)(i, j).real();
      *(field + (oddBit * hfv + half_lattice_index) * 18 + offset++) = (mat)(i, j).imag();
    }
  }
}

template <class Real>
void LoadStore<Real>::addMatrixToField(const Matrix<3, std::complex<Real>> &mat, int oddBit, int half_lattice_index,
                                       Real coeff, Real *const field) const
{
#ifdef MULTI_GPU
  int hfv = Vh_ex;
#else
  int hfv = Vh;
#endif
  Real *const local_field = field + (oddBit * hfv + half_lattice_index) * 18;

  int offset = 0;
  for (int i = 0; i < 3; ++i) {
    for (int j = 0; j < 3; ++j) {
      local_field[offset++] += coeff * mat(i, j).real();
      local_field[offset++] += coeff * mat(i, j).imag();
    }
  }
}

template <class Real>
void LoadStore<Real>::addMatrixToField(const Matrix<3, std::complex<Real>> &mat, int oddBit, int dir,
                                       int half_lattice_index, Real coeff, Real *const field) const
{

#ifdef MULTI_GPU
  int hfv = Vh_ex;
#else
  int hfv = Vh;
#endif

  int offset = 0;
  for (int i = 0; i < 3; ++i) {
    for (int j = 0; j < 3; ++j) {
      addData(field, half_lattice_index, dir, oddBit, offset++, coeff * mat(i, j).real(), hfv);

      addData(field, half_lattice_index, dir, oddBit, offset++, coeff * mat(i, j).imag(), hfv);
    }
  }
}

template <class Real>
void LoadStore<Real>::storeMatrixToMomentumField(const Matrix<3, std::complex<Real>> &mat, int oddBit, int dir,
                                                 int half_lattice_index, Real coeff, Real *const field) const
{
  Real *const mom_field = field + ((oddBit * half_volume + half_lattice_index) * 4 + dir) * 10;
  mom_field[0] = (mat(0, 1).real() - mat(1, 0).real()) * 0.5 * coeff;
  mom_field[1] = (mat(0, 1).imag() + mat(1, 0).imag()) * 0.5 * coeff;

  mom_field[2] = (mat(0, 2).real() - mat(2, 0).real()) * 0.5 * coeff;
  mom_field[3] = (mat(0, 2).imag() + mat(2, 0).imag()) * 0.5 * coeff;

  mom_field[4] = (mat(1, 2).real() - mat(2, 1).real()) * 0.5 * coeff;
  mom_field[5] = (mat(1, 2).imag() + mat(2, 1).imag()) * 0.5 * coeff;

  const Real temp = (mat(0, 0).imag() + mat(1, 1).imag() + mat(2, 2).imag()) * 0.3333333333333333333;
  mom_field[6] = (mat(0, 0).imag() - temp) * coeff;
  mom_field[7] = (mat(1, 1).imag() - temp) * coeff;
  mom_field[8] = (mat(2, 2).imag() - temp) * coeff;
  mom_field[9] = 0.0;
}

template <int oddBit> struct Locator {
private:
  int local_dim[4];
  int volume;
  int half_index;
  int full_index;
  int full_coord[4];
  void getCoordsFromHalfIndex(int half_index, int coord[4]);
  void getCoordsFromFullIndex(int full_index, int coord[4]);
  void cache(int half_lattice_index); // caches the half-lattice index, full-lattice index,
                                      // and full-lattice coordinates

public:
  Locator(const int dim[4]);
  int getFullFromHalfIndex(int half_lattice_index);
  int getNeighborFromFullIndex(int full_lattice_index, int dir, int *err = NULL);
};

template <int oddBit> Locator<oddBit>::Locator(const int dim[4]) : half_index(-1), full_index(-1)
{
  volume = 1;
  for (int dir = 0; dir < 4; ++dir) {
    local_dim[dir] = dim[dir];
    volume *= local_dim[dir];
  }
}

// Store the half_lattice index, works out and stores the full lattice index
// and the coordinates.
template <int oddBit> void Locator<oddBit>::getCoordsFromHalfIndex(int half_lattice_index, int coord[4])
{
#ifdef MULTI_GPU
  int E1 = local_dim[0] + 4;
  int E2 = local_dim[1] + 4;
  int E3 = local_dim[2] + 4;
  int E1h = E1 / 2;

  int z1 = half_lattice_index / E1h;
  int x1h = half_lattice_index - z1 * E1h;
  int z2 = z1 / E2;
  coord[1] = z1 - z2 * E2;
  coord[3] = z2 / E3;
  coord[2] = z2 - coord[3] * E3;
  int x1odd = (coord[1] + coord[2] + coord[3] + oddBit) & 1;
  coord[0] = 2 * x1h + x1odd;
#else
  int half_dim_0 = local_dim[0] / 2;
  int z1 = half_lattice_index / half_dim_0;
  int x1h = half_lattice_index - z1 * half_dim_0;
  int z2 = z1 / local_dim[1];
  coord[1] = z1 - z2 * local_dim[1];
  coord[3] = z2 / local_dim[2];
  coord[2] = z2 - coord[3] * local_dim[2];
  int x1odd = (coord[1] + coord[2] + coord[3] + oddBit) & 1;
  coord[0] = 2 * x1h + x1odd;
#endif
}

template <int oddBit> void Locator<oddBit>::getCoordsFromFullIndex(int full_lattice_index, int coord[4])
{
#ifdef MULTI_GPU
  int D1 = local_dim[0] + 4;
  int D2 = local_dim[1] + 4;
  int D3 = local_dim[2] + 4;
#else
  int D1 = local_dim[0];
  int D2 = local_dim[1];
  int D3 = local_dim[2];
#endif

  int z1 = full_lattice_index / D1;
  coord[0] = full_lattice_index - z1 * D1;
  int z2 = z1 / D2;
  coord[1] = z1 - z2 * D2;
  coord[3] = z2 / D3;
  coord[2] = z2 - coord[3] * D3;
}

template <int oddBit> void Locator<oddBit>::cache(int half_lattice_index)
{
  half_index = half_lattice_index;
  getCoordsFromHalfIndex(half_lattice_index, full_coord);
  int x1odd = (full_coord[1] + full_coord[2] + full_coord[3] + oddBit) & 1;
  full_index = 2 * half_lattice_index + x1odd;
}

template <int oddBit> int Locator<oddBit>::getFullFromHalfIndex(int half_lattice_index)
{
  if (half_index != half_lattice_index) cache(half_lattice_index);
  return full_index;
}

// From full index return the neighbouring full index
template <int oddBit> int Locator<oddBit>::getNeighborFromFullIndex(int full_lattice_index, int dir, int *err)
{
  if (err) *err = 0;

  int coord[4];
  int neighbor_index;
  getCoordsFromFullIndex(full_lattice_index, coord);
#ifdef MULTI_GPU
  int E1 = local_dim[0] + 4;
  int E2 = local_dim[1] + 4;
  int E3 = local_dim[2] + 4;
  int E4 = local_dim[3] + 4;
  switch (dir) {
  case 0: //+X
    neighbor_index = full_lattice_index + 1;
    if (err && (coord[0] == E1 - 1)) *err = 1;
    break;
  case 1: //+Y
    neighbor_index = full_lattice_index + E1;
    if (err && (coord[1] == E2 - 1)) *err = 1;
    break;
  case 2: //+Z
    neighbor_index = full_lattice_index + E2 * E1;
    if (err && (coord[2] == E3 - 1)) *err = 1;
    break;
  case 3: //+T
    neighbor_index = full_lattice_index + E3 * E2 * E1;
    if (err && (coord[3] == E4 - 1)) *err = 1;
    break;
  case 7: //-X
    neighbor_index = full_lattice_index - 1;
    if (err && (coord[0] == 0)) *err = 1;
    break;
  case 6: //-Y
    neighbor_index = full_lattice_index - E1;
    if (err && (coord[1] == 0)) *err = 1;
    break;
  case 5: //-Z
    neighbor_index = full_lattice_index - E2 * E1;
    if (err && (coord[2] == 0)) *err = 1;
    break;
  case 4: //-T
    neighbor_index = full_lattice_index - E3 * E2 * E1;
    if (err && (coord[3] == 0)) *err = 1;
    break;
  default:
    errorQuda("Neighbor index could not be determined\n");
    exit(1);
    break;
  } // switch(dir)

#else
  switch (dir) {
  case 0:
    neighbor_index = (coord[0] == local_dim[0] - 1) ? full_lattice_index + 1 - local_dim[0] : full_lattice_index + 1;
    break;
  case 1:
    neighbor_index = (coord[1] == local_dim[1] - 1) ? full_lattice_index + local_dim[0] * (1 - local_dim[1]) :
                                                      full_lattice_index + local_dim[0];
    break;
  case 2:
    neighbor_index = (coord[2] == local_dim[2] - 1) ?
      full_lattice_index + local_dim[0] * local_dim[1] * (1 - local_dim[2]) :
      full_lattice_index + local_dim[0] * local_dim[1];
    break;
  case 3:
    neighbor_index = (coord[3] == local_dim[3] - 1) ?
      full_lattice_index + local_dim[0] * local_dim[1] * local_dim[2] * (1 - local_dim[3]) :
      full_lattice_index + local_dim[0] * local_dim[1] * local_dim[2];
    break;
  case 7: neighbor_index = (coord[0] == 0) ? full_lattice_index - 1 + local_dim[0] : full_lattice_index - 1; break;
  case 6:
    neighbor_index
      = (coord[1] == 0) ? full_lattice_index - local_dim[0] * (1 - local_dim[1]) : full_lattice_index - local_dim[0];
    break;
  case 5:
    neighbor_index = (coord[2] == 0) ? full_lattice_index - local_dim[0] * local_dim[1] * (1 - local_dim[2]) :
                                       full_lattice_index - local_dim[0] * local_dim[1];
    break;
  case 4:
    neighbor_index = (coord[3] == 0) ?
      full_lattice_index - local_dim[0] * local_dim[1] * local_dim[2] * (1 - local_dim[3]) :
      full_lattice_index - local_dim[0] * local_dim[1] * local_dim[2];
    break;
  default:
    errorQuda("Neighbor index could not be determined\n");
    exit(1);
    break;
  } // switch(dir)
  if (err) *err = 0;
#endif
  return neighbor_index;
}

// Can't typedef a template
template <class Real> struct ColorMatrix {
  typedef Matrix<3, std::complex<Real>> Type;
};

template <class Real, int oddBit>
void computeOneLinkSite(
#ifdef MULTI_GPU
  const int dim[4],
#else
  const int[],
#endif
  int half_lattice_index, const Real *const oprod, int sig, Real coeff, const LoadStore<Real> &ls, Real *const output)
{
  if (GOES_FORWARDS(sig)) {
    typename ColorMatrix<Real>::Type colorMatW;
#ifdef MULTI_GPU
    int idx = ls.half_idx_conversion_normal2ex(half_lattice_index, dim, oddBit);
#else
    int idx = half_lattice_index;
#endif
    ls.loadMatrixFromField(oprod, oddBit, sig, idx, &colorMatW);
    ls.addMatrixToField(colorMatW, oddBit, sig, idx, coeff, output);
  }
}

template <class Real>
void computeOneLinkField(const int dim[4], const Real *const oprod, int sig, Real coeff, Real *const output)
{
  int volume = 1;
  for (int dir = 0; dir < 4; ++dir) volume *= dim[dir];
  const int half_volume = volume / 2;
  LoadStore<Real> ls(volume);
  for (int site = 0; site < half_volume; ++site) {
    computeOneLinkSite<Real, 0>(dim, site, oprod, sig, coeff, ls, output);
  }
  // Loop over odd lattice sites
  for (int site = 0; site < half_volume; ++site) {
    computeOneLinkSite<Real, 1>(dim, site, oprod, sig, coeff, ls, output);
  }
}

// middleLinkKernel compiles for now, but lots of debugging to be done
template <class Real, int oddBit>
void computeMiddleLinkSite(int half_lattice_index, // half_lattice_index to better match the GPU code.
                           const int dim[4], const Real *const oprod, const Real *const Qprev, const Real *const link,
                           int sig, int mu, Real coeff,
                           const LoadStore<Real> &ls, // pass a function object to read from and write to matrix fields
                           Real *const Pmu, Real *const P3, Real *const Qmu, Real *const newOprod)
{
  const bool mu_positive = (GOES_FORWARDS(mu)) ? true : false;
  const bool sig_positive = (GOES_FORWARDS(sig)) ? true : false;

  Locator<oddBit> locator(dim);
  int point_b, point_c, point_d;
  int ad_link_nbr_idx, ab_link_nbr_idx, bc_link_nbr_idx;
  int X = locator.getFullFromHalfIndex(half_lattice_index);

  int err;
  int new_mem_idx = locator.getNeighborFromFullIndex(X, OPP_DIR(mu), &err);
  RETURN_IF_ERR;
  point_d = new_mem_idx >> 1;
  // getNeighborFromFullIndex will work on any site on the lattice, odd or even
  new_mem_idx = locator.getNeighborFromFullIndex(new_mem_idx, sig, &err);
  RETURN_IF_ERR;
  point_c = new_mem_idx >> 1;

  new_mem_idx = locator.getNeighborFromFullIndex(X, sig);
  RETURN_IF_ERR;
  point_b = new_mem_idx >> 1;

  ad_link_nbr_idx = (mu_positive) ? point_d : half_lattice_index;
  bc_link_nbr_idx = (mu_positive) ? point_c : point_b;
  ab_link_nbr_idx = (sig_positive) ? half_lattice_index : point_b;

  typename ColorMatrix<Real>::Type ab_link, bc_link, ad_link;
  typename ColorMatrix<Real>::Type colorMatW, colorMatX, colorMatY, colorMatZ;

  if (sig_positive) {
    ls.loadMatrixFromField(link, oddBit, sig, ab_link_nbr_idx, &ab_link);
  } else {
    ls.loadMatrixFromField(link, 1 - oddBit, OPP_DIR(sig), ab_link_nbr_idx, &ab_link);
  }

  if (mu_positive) {
    ls.loadMatrixFromField(link, oddBit, mu, bc_link_nbr_idx, &bc_link);
  } else {
    ls.loadMatrixFromField(link, 1 - oddBit, OPP_DIR(mu), bc_link_nbr_idx, &bc_link);
  }

  if (Qprev == NULL) {
    if (sig_positive) {
      ls.loadMatrixFromField(oprod, 1 - oddBit, sig, point_d, &colorMatY);
    } else {
      ls.loadMatrixFromField(oprod, oddBit, OPP_DIR(sig), point_c, &colorMatY);
      colorMatY = conj(colorMatY);
    }
  } else { // Qprev != NULL
    ls.loadMatrixFromField(oprod, oddBit, point_c, &colorMatY);
  }

  colorMatW = (!mu_positive) ? bc_link * colorMatY : conj(bc_link) * colorMatY;
  if (Pmu) ls.storeMatrixToField(colorMatW, 1 - oddBit, point_b, Pmu);

  colorMatY = (sig_positive) ? ab_link * colorMatW : conj(ab_link) * colorMatW;
  ls.storeMatrixToField(colorMatY, oddBit, half_lattice_index, P3);

  if (mu_positive) {
    ls.loadMatrixFromField(link, 1 - oddBit, mu, ad_link_nbr_idx, &ad_link);
  } else {
    ls.loadMatrixFromField(link, oddBit, OPP_DIR(mu), ad_link_nbr_idx, &ad_link);
    ad_link = conj(ad_link);
  }

  if (Qprev == NULL) {
    if (sig_positive) colorMatY = colorMatW * ad_link;
    if (Qmu) ls.storeMatrixToField(ad_link, oddBit, half_lattice_index, Qmu);
  } else { // Qprev != NULL
    if (Qmu || sig_positive) {
      ls.loadMatrixFromField(Qprev, 1 - oddBit, point_d, &colorMatY);
      colorMatX = colorMatY * ad_link;
    }
    if (Qmu) ls.storeMatrixToField(colorMatX, oddBit, half_lattice_index, Qmu);
    if (sig_positive) colorMatY = colorMatW * colorMatX;
  }

  if (sig_positive) ls.addMatrixToField(colorMatY, oddBit, sig, half_lattice_index, coeff, newOprod);
} // computeMiddleLinkSite

template <class Real>
void computeMiddleLinkField(const int dim[4], const Real *const oprod, const Real *const Qprev, const Real *const link,
                            int sig, int mu, Real coeff, Real *const Pmu, Real *const P3, Real *const Qmu,
                            Real *const newOprod)
{

  int volume = 1;
  for (int dir = 0; dir < 4; ++dir) volume *= dim[dir];
#ifdef MULTI_GPU
  const int loop_count = Vh_ex;
#else
  const int loop_count = volume / 2;
#endif
  // loop over the lattice volume
  // To keep the code as close to the GPU code as possible, we'll
  // loop over the even sites first and then the odd sites
  LoadStore<Real> ls(volume);
  for (int site = 0; site < loop_count; ++site) {
    computeMiddleLinkSite<Real, 0>(site, dim, oprod, Qprev, link, sig, mu, coeff, ls, Pmu, P3, Qmu, newOprod);
  }
  // Loop over odd lattice sites
  for (int site = 0; site < loop_count; ++site) {
    computeMiddleLinkSite<Real, 1>(site, dim, oprod, Qprev, link, sig, mu, coeff, ls, Pmu, P3, Qmu, newOprod);
  }
}

template <class Real, int oddBit>
void computeSideLinkSite(int half_lattice_index, // half_lattice_index to better match the GPU code.
                         const int dim[4], const Real *const P3,
                         const Real *const Qprod, // why?
                         const Real *const link, int sig, int mu, Real coeff, Real accumu_coeff,
                         const LoadStore<Real> &ls, // pass a function object to read from and write to matrix fields
                         Real *const shortP, Real *const newOprod)
{

  const bool mu_positive = (GOES_FORWARDS(mu)) ? true : false;
  const bool sig_positive = (GOES_FORWARDS(sig)) ? true : false;

  Locator<oddBit> locator(dim);
  int point_d;
  int ad_link_nbr_idx;
  int X = locator.getFullFromHalfIndex(half_lattice_index);

  int err;
  int new_mem_idx = locator.getNeighborFromFullIndex(X, OPP_DIR(mu), &err);
  RETURN_IF_ERR;
  point_d = new_mem_idx >> 1;
  ad_link_nbr_idx = (mu_positive) ? point_d : half_lattice_index;

  typename ColorMatrix<Real>::Type ad_link;
  typename ColorMatrix<Real>::Type colorMatW, colorMatX, colorMatY;

  ls.loadMatrixFromField(P3, oddBit, half_lattice_index, &colorMatY);

  if (shortP) {
    if (mu_positive) {
      ad_link_nbr_idx = point_d;
      ls.loadMatrixFromField(link, 1 - oddBit, mu, ad_link_nbr_idx, &ad_link);
    } else {
      ad_link_nbr_idx = half_lattice_index;
      ls.loadMatrixFromField(link, oddBit, OPP_DIR(mu), ad_link_nbr_idx, &ad_link);
    }
    colorMatW = (mu_positive) ? ad_link * colorMatY : conj(ad_link) * colorMatY;
    ls.addMatrixToField(colorMatW, 1 - oddBit, point_d, accumu_coeff, shortP);
  } // if(shortP)

  Real mycoeff = ((sig_positive && oddBit) || (!sig_positive && !oddBit)) ? coeff : -coeff;

  if (Qprod) {
    ls.loadMatrixFromField(Qprod, 1 - oddBit, point_d, &colorMatX);
    if (mu_positive) {
      colorMatW = colorMatY * colorMatX;
      if (!oddBit) { mycoeff = -mycoeff; }
      ls.addMatrixToField(colorMatW, 1 - oddBit, mu, point_d, mycoeff, newOprod);
    } else {
      colorMatW = conj(colorMatX) * conj(colorMatY);
      if (oddBit) { mycoeff = -mycoeff; }
      ls.addMatrixToField(colorMatW, oddBit, OPP_DIR(mu), half_lattice_index, mycoeff, newOprod);
    }
  }

  if (!Qprod) {
    if (mu_positive) {
      if (!oddBit) { mycoeff = -mycoeff; }
      ls.addMatrixToField(colorMatY, 1 - oddBit, mu, point_d, mycoeff, newOprod);
    } else {
      if (oddBit) { mycoeff = -mycoeff; }
      colorMatW = conj(colorMatY);
      ls.addMatrixToField(colorMatW, oddBit, OPP_DIR(mu), half_lattice_index, mycoeff, newOprod);
    }
  } // if !(Qprod)

} // computeSideLinkSite

// Maybe change to computeSideLinkField
template <class Real>
void computeSideLinkField(const int dim[4], const Real *const P3,
                          const Real *const Qprod, // why?
                          const Real *const link, int sig, int mu, Real coeff, Real accumu_coeff, Real *const shortP,
                          Real *const newOprod)
{
  // Need some way of setting half_volume
  int volume = 1;
  for (int dir = 0; dir < 4; ++dir) volume *= dim[dir];
#ifdef MULTI_GPU
  const int loop_count = Vh_ex;
#else
  const int loop_count = volume / 2;
#endif
  LoadStore<Real> ls(volume);

  for (int site = 0; site < loop_count; ++site) {
    computeSideLinkSite<Real, 0>(site, dim, P3, Qprod, link, sig, mu, coeff, accumu_coeff, ls, shortP, newOprod);
  }

  for (int site = 0; site < loop_count; ++site) {
    computeSideLinkSite<Real, 1>(site, dim, P3, Qprod, link, sig, mu, coeff, accumu_coeff, ls, shortP, newOprod);
  }
}

template <class Real, int oddBit>
void computeAllLinkSite(int half_lattice_index, // half_lattice_index to better match the GPU code.
                        const int dim[4], const Real *const oprod, const Real *const Qprev, const Real *const link,
                        int sig, int mu, Real coeff, Real accumu_coeff,
                        const LoadStore<Real> &ls, // pass a function object to read from and write to matrix fields
                        Real *const shortP, Real *const newOprod)
{

  const bool mu_positive = (GOES_FORWARDS(mu)) ? true : false;
  const bool sig_positive = (GOES_FORWARDS(sig)) ? true : false;

  typename ColorMatrix<Real>::Type ab_link, bc_link, ad_link;
  typename ColorMatrix<Real>::Type colorMatW, colorMatX, colorMatY, colorMatZ;

  int ab_link_nbr_idx, point_b, point_c, point_d;

  Locator<oddBit> locator(dim);
  int X = locator.getFullFromHalfIndex(half_lattice_index);

  int err;
  int new_mem_idx = locator.getNeighborFromFullIndex(X, OPP_DIR(mu), &err);
  RETURN_IF_ERR;
  point_d = new_mem_idx >> 1;

  new_mem_idx = locator.getNeighborFromFullIndex(new_mem_idx, sig, &err);
  RETURN_IF_ERR;
  point_c = new_mem_idx >> 1;

  new_mem_idx = locator.getNeighborFromFullIndex(X, sig, &err);
  RETURN_IF_ERR;
  point_b = new_mem_idx >> 1;
  ab_link_nbr_idx = (sig_positive) ? half_lattice_index : point_b;

  Real mycoeff = ((sig_positive && oddBit) || (!sig_positive && !oddBit)) ? coeff : -coeff;

  if (mu_positive) {
    ls.loadMatrixFromField(Qprev, 1 - oddBit, point_d, &colorMatX);
    ls.loadMatrixFromField(link, 1 - oddBit, mu, point_d, &ad_link);
    // compute point_c
    ls.loadMatrixFromField(oprod, oddBit, point_c, &colorMatY);
    ls.loadMatrixFromField(link, oddBit, mu, point_c, &bc_link);
    colorMatZ = conj(bc_link) * colorMatY; // okay

    if (sig_positive) {
      colorMatY = colorMatX * ad_link;
      colorMatW = colorMatZ * colorMatY;
      ls.addMatrixToField(colorMatW, oddBit, sig, half_lattice_index, Sign<oddBit>::result * mycoeff, newOprod);
    }

    if (sig_positive) {
      ls.loadMatrixFromField(link, oddBit, sig, ab_link_nbr_idx, &ab_link);
    } else {
      ls.loadMatrixFromField(link, 1 - oddBit, OPP_DIR(sig), ab_link_nbr_idx, &ab_link);
    }
    colorMatY = (sig_positive) ? ab_link * colorMatZ : conj(ab_link) * colorMatZ; // okay
    colorMatW = colorMatY * colorMatX;
    ls.addMatrixToField(colorMatW, 1 - oddBit, mu, point_d, -Sign<oddBit>::result * mycoeff, newOprod);
    colorMatW = ad_link * colorMatY;
    ls.addMatrixToField(colorMatW, 1 - oddBit, point_d, accumu_coeff, shortP); // Warning! Need to check this!
  } else {                                                                     // negative mu
    mu = OPP_DIR(mu);
    ls.loadMatrixFromField(Qprev, 1 - oddBit, point_d, &colorMatX);
    ls.loadMatrixFromField(link, oddBit, mu, half_lattice_index, &ad_link);
    ls.loadMatrixFromField(oprod, oddBit, point_c, &colorMatY);
    ls.loadMatrixFromField(link, 1 - oddBit, mu, point_b, &bc_link);

    if (sig_positive) colorMatW = colorMatX * conj(ad_link);
    colorMatZ = bc_link * colorMatY;
    if (sig_positive) {
      colorMatY = colorMatZ * colorMatW;
      ls.addMatrixToField(colorMatY, oddBit, sig, half_lattice_index, Sign<oddBit>::result * mycoeff, newOprod);
    }

    if (sig_positive) {
      ls.loadMatrixFromField(link, oddBit, sig, ab_link_nbr_idx, &ab_link);
    } else {
      ls.loadMatrixFromField(link, 1 - oddBit, OPP_DIR(sig), ab_link_nbr_idx, &ab_link);
    }

    colorMatY = (sig_positive) ? ab_link * colorMatZ : conj(ab_link) * colorMatZ; // 611
    colorMatW = conj(colorMatX) * conj(colorMatY);

    ls.addMatrixToField(colorMatW, oddBit, mu, half_lattice_index, Sign<oddBit>::result * mycoeff, newOprod);
    colorMatW = conj(ad_link) * colorMatY;
    ls.addMatrixToField(colorMatW, 1 - oddBit, point_d, accumu_coeff, shortP);
  } // end mu
} // allLinkKernel

template <class Real>
void computeAllLinkField(const int dim[4], const Real *const oprod, const Real *const Qprev, const Real *const link,
                         int sig, int mu, Real coeff, Real accumu_coeff, Real *const shortP, Real *const newOprod)
{
  int volume = 1;
  for (int dir = 0; dir < 4; ++dir) volume *= dim[dir];
#ifdef MULTI_GPU
  const int loop_count = Vh_ex;
#else
  const int loop_count = volume / 2;
#endif

  LoadStore<Real> ls(volume);
  for (int site = 0; site < loop_count; ++site) {

    computeAllLinkSite<Real, 0>(site, dim, oprod, Qprev, link, sig, mu, coeff, accumu_coeff, ls, shortP, newOprod);
  }

  for (int site = 0; site < loop_count; ++site) {
    computeAllLinkSite<Real, 1>(site, dim, oprod, Qprev, link, sig, mu, coeff, accumu_coeff, ls, shortP, newOprod);
  }
}

#define Pmu tempmat[0]
#define P3 tempmat[1]
#define P5 tempmat[2]
#define Pnumu tempmat[3]
#define Qmu tempmat[4]
#define Qnumu tempmat[5]

template <class Real> struct PathCoefficients {
  Real one;
  Real three;
  Real five;
  Real seven;
  Real naik;
  Real lepage;
};

template <class Real>
void doHisqStaplesForceCPU(const int dim[4], PathCoefficients<double> staple_coeff, Real *oprod, Real *link,
                           Real **tempmat, Real *newOprod)
{
  Real OneLink, ThreeSt, FiveSt, SevenSt, Lepage, coeff;

  OneLink = staple_coeff.one;
  ThreeSt = staple_coeff.three;
  FiveSt = staple_coeff.five;
  SevenSt = staple_coeff.seven;
  Lepage = staple_coeff.lepage;

  for (int sig = 0; sig < 4; ++sig) { computeOneLinkField(dim, oprod, sig, OneLink, newOprod); }

  // sig labels the net displacement of the staple
  for (int sig = 0; sig < 8; ++sig) {
    for (int mu = 0; mu < 8; ++mu) {
      if (mu == sig || mu == OPP_DIR(sig)) continue;

      computeMiddleLinkField<Real>(dim, oprod, NULL, link, sig, mu, -ThreeSt, Pmu, P3, Qmu, newOprod);

      for (int nu = 0; nu < 8; ++nu) {
        if (nu == mu || nu == OPP_DIR(mu) || nu == sig || nu == OPP_DIR(sig)) continue;

        computeMiddleLinkField<Real>(dim, Pmu, Qmu, link, sig, nu, staple_coeff.five, Pnumu, P5, Qnumu, newOprod);

        for (int rho = 0; rho < 8; ++rho) {
          if (rho == sig || rho == OPP_DIR(sig) || rho == mu || rho == OPP_DIR(mu) || rho == nu || rho == OPP_DIR(nu)) {
            continue;
          }

          if (FiveSt != 0)
            coeff = SevenSt / FiveSt;
          else
            coeff = 0;
          computeAllLinkField<Real>(dim, Pnumu, Qnumu, link, sig, rho, staple_coeff.seven, coeff, P5, newOprod);

        } // rho

        if (ThreeSt != 0)
          coeff = FiveSt / ThreeSt;
        else
          coeff = 0;
        computeSideLinkField<Real>(dim, P5, Qmu, link, sig, nu, -FiveSt, coeff, P3, newOprod);

      } // nu

      // lepage
      if (staple_coeff.lepage != 0.) {
        computeMiddleLinkField<Real>(dim, Pmu, Qmu, link, sig, mu, Lepage, NULL, P5, NULL, newOprod);

        if (ThreeSt != 0)
          coeff = Lepage / ThreeSt;
        else
          coeff = 0;
        computeSideLinkField<Real>(dim, P5, Qmu, link, sig, mu, -Lepage, coeff, P3, newOprod);
      } // lepage != 0

      computeSideLinkField<Real>(dim, P3, NULL, link, sig, mu, ThreeSt, 0., NULL, newOprod);
    } // mu
  }   // sig

  // Need also to compute the one-link contribution
}

#undef Pmu
#undef P3
#undef P5
#undef Pnumu
#undef Qmu
#undef Qnumu

void hisqStaplesForceCPU(const double *path_coeff, const QudaGaugeParam &param, cpuGaugeField &oprod,
                         cpuGaugeField &link, cpuGaugeField *newOprod)
{
  int volume = 1;
  for (int dir = 0; dir < 4; ++dir) volume *= param.X[dir];

#ifdef MULTI_GPU
  int len = Vh_ex * 2;
#else
  int len = volume;
#endif
  // allocate memory for temporary fields
  void *tempmat[6];
  for (int i = 0; i < 6; i++) { tempmat[i] = safe_malloc(len * 18 * param.cpu_prec); }

  PathCoefficients<double> act_path_coeff;
  act_path_coeff.one = path_coeff[0];
  act_path_coeff.naik = path_coeff[1];
  act_path_coeff.three = path_coeff[2];
  act_path_coeff.five = path_coeff[3];
  act_path_coeff.seven = path_coeff[4];
  act_path_coeff.lepage = path_coeff[5];

  if (param.cpu_prec == QUDA_DOUBLE_PRECISION) {
    doHisqStaplesForceCPU<double>(param.X, act_path_coeff, (double *)oprod.Gauge_p(), (double *)link.Gauge_p(),
                                  (double **)tempmat, (double *)newOprod->Gauge_p());

  } else if (param.cpu_prec == QUDA_SINGLE_PRECISION) {
    doHisqStaplesForceCPU<float>(param.X, act_path_coeff, (float *)oprod.Gauge_p(), (float *)link.Gauge_p(),
                                 (float **)tempmat, (float *)newOprod->Gauge_p());
  } else {
    errorQuda("Unsupported precision");
  }

  for (int i = 0; i < 6; ++i) { host_free(tempmat[i]); }
}

template <class Real, int oddBit>
void computeLongLinkSite(int half_lattice_index, const int dim[4], const Real *const oprod, const Real *const link,
                         int sig, Real coeff, const LoadStore<Real> &ls, Real *const output)
{
  if (GOES_FORWARDS(sig)) {

    Locator<oddBit> locator(dim);

    typename ColorMatrix<Real>::Type ab_link, bc_link, de_link, ef_link;
    typename ColorMatrix<Real>::Type colorMatU, colorMatV, colorMatW, colorMatX, colorMatY, colorMatZ;

    int point_a, point_b, point_c, point_d, point_e;
#ifdef MULTI_GPU
    int idx = ls.half_idx_conversion_normal2ex(half_lattice_index, dim, oddBit);
#else
    int idx = half_lattice_index;
#endif

    int X = locator.getFullFromHalfIndex(idx);
    point_c = idx;

    int new_mem_idx = locator.getNeighborFromFullIndex(X, sig);
    point_d = new_mem_idx >> 1;

    new_mem_idx = locator.getNeighborFromFullIndex(new_mem_idx, sig);
    point_e = new_mem_idx >> 1;

    new_mem_idx = locator.getNeighborFromFullIndex(X, OPP_DIR(sig));
    point_b = new_mem_idx >> 1;

    new_mem_idx = locator.getNeighborFromFullIndex(new_mem_idx, OPP_DIR(sig));
    point_a = new_mem_idx >> 1;

    ls.loadMatrixFromField(link, oddBit, sig, point_a, &ab_link);
    ls.loadMatrixFromField(link, 1 - oddBit, sig, point_b, &bc_link);
    ls.loadMatrixFromField(link, 1 - oddBit, sig, point_d, &de_link);
    ls.loadMatrixFromField(link, oddBit, sig, point_e, &ef_link);

    ls.loadMatrixFromField(oprod, oddBit, sig, point_c, &colorMatZ);
    ls.loadMatrixFromField(oprod, 1 - oddBit, sig, point_b, &colorMatY);
    ls.loadMatrixFromField(oprod, oddBit, sig, point_a, &colorMatX);

    colorMatV = de_link * ef_link * colorMatZ - de_link * colorMatY * bc_link + colorMatX * ab_link * bc_link;

    ls.addMatrixToField(colorMatV, oddBit, sig, point_c, coeff, output);
  }
}

template <class Real>
void computeLongLinkField(const int dim[4], const Real *const oprod, const Real *const link, int sig, Real coeff,
                          Real *const output)
{
  int volume = 1;
  for (int dir = 0; dir < 4; ++dir) volume *= dim[dir];
  const int half_volume = volume / 2;

  LoadStore<Real> ls(volume);
  for (int site = 0; site < half_volume; ++site) {
    computeLongLinkSite<Real, 0>(site, dim, oprod, link, sig, coeff, ls, output);
  }
  // Loop over odd lattice sites
  for (int site = 0; site < half_volume; ++site) {
    computeLongLinkSite<Real, 1>(site, dim, oprod, link, sig, coeff, ls, output);
  }
}

void hisqLongLinkForceCPU(double coeff, const QudaGaugeParam &param, cpuGaugeField &oprod, cpuGaugeField &link,
                          cpuGaugeField *newOprod)
{
  for (int sig = 0; sig < 4; ++sig) {
    if (param.cpu_prec == QUDA_SINGLE_PRECISION) {
      computeLongLinkField<float>(param.X, (float *)oprod.Gauge_p(), (float *)link.Gauge_p(), sig, coeff,
                                  (float *)newOprod->Gauge_p());
    } else if (param.cpu_prec == QUDA_DOUBLE_PRECISION) {
      computeLongLinkField<double>(param.X, (double *)oprod.Gauge_p(), (double *)link.Gauge_p(), sig, coeff,
                                   (double *)newOprod->Gauge_p());
    } else {
      errorQuda("Unrecognised precision\n");
    }
  } // sig
}

template <class Real, int oddBit>
void completeForceSite(int half_lattice_index,
#ifdef MULTI_GPU
                       const int dim[4],
#else
                       const int[],
#endif
                       const Real *const oprod, const Real *const link, int sig, const LoadStore<Real> &ls,
                       Real *const mom)
{

  typename ColorMatrix<Real>::Type colorMatX, colorMatY, linkW;

#ifdef MULTI_GPU
  int half_lattice_index_ex = ls.half_idx_conversion_normal2ex(half_lattice_index, dim, oddBit);
  int idx = half_lattice_index_ex;
#else
  int idx = half_lattice_index;
#endif
  ls.loadMatrixFromField(link, oddBit, sig, idx, &linkW);
  ls.loadMatrixFromField(oprod, oddBit, sig, idx, &colorMatX);

  const Real coeff = (oddBit) ? -1 : 1;
  colorMatY = linkW * colorMatX;

  ls.storeMatrixToMomentumField(colorMatY, oddBit, sig, half_lattice_index, coeff, mom);
}

template <class Real>
void completeForceField(const int dim[4], const Real *const oprod, const Real *const link, int sig, Real *const mom)
{
  int volume = dim[0] * dim[1] * dim[2] * dim[3];
  const int half_volume = volume / 2;
  LoadStore<Real> ls(volume);

  for (int site = 0; site < half_volume; ++site) { completeForceSite<Real, 0>(site, dim, oprod, link, sig, ls, mom); }
  for (int site = 0; site < half_volume; ++site) { completeForceSite<Real, 1>(site, dim, oprod, link, sig, ls, mom); }
}

void hisqCompleteForceCPU(const QudaGaugeParam &param, cpuGaugeField &oprod, cpuGaugeField &link, cpuGaugeField *mom)
{
  for (int sig = 0; sig < 4; ++sig) {
    if (param.cpu_prec == QUDA_SINGLE_PRECISION) {
      completeForceField<float>(param.X, (float *)oprod.Gauge_p(), (float *)link.Gauge_p(), sig, (float *)mom->Gauge_p());
    } else if (param.cpu_prec == QUDA_DOUBLE_PRECISION) {
      completeForceField<double>(param.X, (double *)oprod.Gauge_p(), (double *)link.Gauge_p(), sig,
                                 (double *)mom->Gauge_p());
    } else {
      errorQuda("Unrecognised precision\n");
    }
  } // loop over sig
}


TRichEditPrintout::~TRichEditPrintout()
{
  // Flush cached format information
  if (FlushCache)
  {
    const auto ok = RichEdit.GetHandle() != 0;
    WARN(!ok, _T("TRichEditPrintout::~TRichEditPrintout: Terminating due to failed precondition."));
    if (!ok) std::terminate();

    RichEdit.FormatRange();
  }
}

// ObjectWindows
/// \file
/// Implementation of support classes for Print/PrintPreview of TRichEdits
#include <owl/pch.h>
#include <owl/window.h>
#include <owl/control.h>
#include <owl/edit.h>
#include <owl/editsear.h>
#include <owl/editfile.h>
#include <owl/richedit.h>
#include <owl/richedpr.h>

#if defined(__BORLANDC__)
# pragma option -w-ccc // Disable "Condition is always true/false"
#endif

namespace owl {

OWL_DIAGINFO;

/// Constructs a Printout object which represent a RICHEDIT's document.
TRichEditPrintout::TRichEditPrintout(TPrinter&  printer,
                                     TRichEdit& richEdit,
                                     LPCTSTR    title)
:
  TPrintout(title),
 Printer(printer),
  RichEdit(richEdit),
  SizePhysPage(0),
  SizePhysInch(0),
  Margins(0),
  FlushCache(false),
  TextLen(0),
  PageCount(0),
  PageIndices(5)
{
  PRECONDITION(HWND(RichEdit));

  // Initialize data members
  PageIndices.Add(0);
}

/// String-aware overload
TRichEditPrintout::TRichEditPrintout(TPrinter&  printer, TRichEdit& richEdit, const tstring& title)
:
  TPrintout(title),
  Printer(printer),
  RichEdit(richEdit),
  SizePhysPage(0),
  SizePhysInch(0),
  Margins(0),
  FlushCache(false),
  TextLen(0),
  PageCount(0),
  PageIndices(5)
{
  PRECONDITION(HWND(RichEdit));

  // Initialize data members
  PageIndices.Add(0);
}

// Destructor of 'RichEdit PrintOut' - Flushes any cached formatting
// information

/// This method is invoked by the printer or print-preview classes to allow the
/// printout to update the page range information.
void
TRichEditPrintout::GetDialogInfo(int& minPage, int& maxPage,
                                 int& selFromPage, int& selToPage)
{
  if (PageCount && TextLen) {
    minPage = 1;
    maxPage = PageCount;

    CHECK(HWND(RichEdit));
    int startSel, endSel;
    RichEdit.GetSelection(startSel, endSel);

    // !BB Update to support multiple preview pages
    selFromPage = startSel != endSel ? PageOfOffset(startSel) : 1;
    selToPage = PageCount > selFromPage ? selFromPage+1 : 0;
  }
  else {
    minPage = maxPage = 0;
    selFromPage = selToPage = 0;
  }
}

/// This method is invoked by the printer or print-preview objects to hand the
/// printout object the target device context and the size of its pages.
void
TRichEditPrintout::SetPrintParams(TPrintDC* dc, TSize pageSize)
{
  PRECONDITION(HWND(RichEdit));

  // Chain call base class' version
  TPrintout::SetPrintParams(dc, pageSize);

  // Update size info about printer DC
  SizePhysInch = TSize(dc->GetDeviceCaps(LOGPIXELSX),
                       dc->GetDeviceCaps(LOGPIXELSY));
  SizePhysPage = TSize(dc->GetDeviceCaps(PHYSICALWIDTH),
                       dc->GetDeviceCaps(PHYSICALHEIGHT));
  if (!SizePhysPage.cx)
    SizePhysPage.cx = dc->GetDeviceCaps(HORZRES);
  if (!SizePhysPage.cy)
    SizePhysPage.cy = dc->GetDeviceCaps(VERTRES);

  // Set up the target and render DCs
  // NOTE: In the case of PrintPreview, the two HDCs differ.
  //       When we're really printing, the attribute DC and
  //       GetHDC() return the same HDC.
  FmtRange.SetTargetDC(dc->GetAttributeHDC()); // Format Device
  FmtRange.SetRenderDC(dc->GetHDC());          // Render Device

  // Set the page and render rectangles in twips
  TRect rect(0, 0, MulDiv(SizePhysPage.cx, 1440, SizePhysInch.cx),
                   MulDiv(SizePhysPage.cy, 1440, SizePhysInch.cy));
  if (Margins.cx) {
    ;
  }
  if (Margins.cy) {
    ;
  }
  FmtRange.SetPageRect(rect);
  FmtRange.SetRenderRect(rect);

  // Retrieve length of document and set range
  TextLen = RichEdit.GetTextLength();

  // Compute number of pages and cache their offsets
  if (TextLen > 0) {
    PageCount = 1;                  // Pages are not zero-based
    FmtRange.SetRange(0, TextLen);  // Format entire range.....
    int endOfRange;
    do {
      endOfRange = RichEdit.FormatRange(FmtRange, false);

      if(PageCount >= (int)PageIndices.Size())
        PageIndices.Add(endOfRange);
      else
        PageIndices[PageCount++] = endOfRange;

      FmtRange.chrg.cpMin = endOfRange;
      FmtRange.SetRenderRect(rect); // Ctl seems to corrupt the 'rc' member!
    } while (FmtRange.chrg.cpMin < TextLen);

    PageCount--;

    // Free cached formatting information
    RichEdit.FormatRange();
  }
  else {
    PageCount = 0;
  }
}

/// This routine is invoked to inform the printout that a printing operation has started.
void
TRichEditPrintout::BeginPrinting()
{
}

/// This routine is invoked to inform the printout that the printint operation has ended.
void
TRichEditPrintout::EndPrinting()
{
}

/// This routine is invoked to request the printout object to print a page.
void
TRichEditPrintout::PrintPage (int page, TRect& bandRect, uint)
{
  PRECONDITION(page > 0);
  PRECONDITION(page <= PageCount);

  // Check control's handle is !0
  PRECONDITION(HWND(RichEdit));

  // These should have been set via a prior call to 'SetPrintParams'
  PRECONDITION(FmtRange.hdcTarget !=0);
  PRECONDITION(FmtRange.hdc != 0);

  // Check whether we're in 'real' printing mode or just print preview.
  // In print preview mode the target does not match the render DC.....
  if (FmtRange.hdc != FmtRange.hdcTarget) {

    int saveId = SaveDC(FmtRange.hdc);
    if (FmtRange.hdc) {
      TDC dc(FmtRange.hdc);

      // Make window DC match target's logical size
      dc.SetMapMode(MM_ANISOTROPIC);
      dc.SetWindowExt(TSize(MulDiv(SizePhysPage.cx,
                                  dc.GetDeviceCaps(LOGPIXELSX),
                                  SizePhysInch.cx),
                            MulDiv(SizePhysPage.cy,
                                  dc.GetDeviceCaps(LOGPIXELSY),
                                  SizePhysInch.cy)));
      dc.SetViewportExt(TSize(bandRect.Width(), bandRect.Height()));

      // Update character range for specified page
      FmtRange.SetRange(PageIndices[page-1], PageIndices[page]);

      // Have Edit control draw into window
      RichEdit.FormatRange(FmtRange);

      // Flag that we need to reset control
      FlushCache = true;
    }
    // Restore the DC
    RestoreDC(FmtRange.hdc, saveId);
  }
  else {
    // We're really printing! Update character range for specified page
    FmtRange.SetRange(PageIndices[page-1], PageIndices[page]);

    // Have Edit control format data
    RichEdit.FormatRange(FmtRange, false);

    // Flag that we need to reset control
    FlushCache = true;

    // Have Edit control display to render rectangle of device
    RichEdit.DisplayBand(*((TRect*)&FmtRange.rc));
  }
}

/// This routine is invoked to asked the printout object whether it has necessary
/// information to print the specified page.
bool
TRichEditPrintout::HasPage (int pageNumber)
{
  return pageNumber > 0 && pageNumber <= PageCount;
}

/// Returns the index of the page at the particular offset within the buffer of an
int
TRichEditPrintout::PageOfOffset(int offset)
{
  PRECONDITION(offset <= TextLen);

  int page = 0;
  if (PageCount > 0) {
    for (int i=1; i<=PageCount; i++) {
      if (PageIndices[i] >= offset) {
        if (PageIndices[i-1] <= offset) {
          page = i;
          break;
        }
      }
    }
  }
  return page;
}

/// Constructor of RichEdit PagePreview object.
TRichEditPagePreview::TRichEditPagePreview(TWindow* parent,
                                           TPrintout& printout,
                                           TPrintDC& prndc,
                                           TSize& printExtent,
                                           int pagenum)
:
  TPreviewPage(parent, printout, prndc, printExtent, pagenum)
{
}

/// WM_PAINT handler of RichEdit PagePreview window. Displays a preview of the page
/// if the printout can handle it. Otherwise, simply fills the window with a white
void
TRichEditPagePreview::Paint(TDC& dc, bool, TRect& /*clip*/)
{
  TRect client;
  GetClientRect(client);

  TPreviewDCBase pdc(dc, PrintDC);
  Printout.SetPrintParams(&pdc, PrintExtent);

  if (Printout.HasPage(PageNum)) {
    Printout.BeginPrinting();
    Printout.BeginDocument(PageNum, PageNum, pfBoth);
    Printout.PrintPage(PageNum, client, pfBoth);
    Printout.EndDocument();
    Printout.EndPrinting();
  }
  else
    dc.PatBlt(client, WHITENESS);
}

/// Constructor of a RichEdit Preview Frame.
TRichEditPreviewFrame::TRichEditPreviewFrame(TWindow* parentWindow,
                                             TPrinter& printer,
                                             TPrintout& printout,
                                             TRichEdit& richEdit,
                                             LPCTSTR title,
                                             TLayoutWindow* client)
:
  TPreviewWin(parentWindow, printer, printout, richEdit, title, client)
{
}

/// String-aware overload
TRichEditPreviewFrame::TRichEditPreviewFrame(
  TWindow* parentWindow,
  TPrinter& printer,
  TPrintout& printout,
  TRichEdit& richEdit,
  const tstring& title,
  TLayoutWindow* client
  )
  : TPreviewWin(parentWindow, printer, printout, richEdit, title, client)
{}

/// Returns pointer to a preview page object.
TPreviewPage*
TRichEditPreviewFrame::GetNewPreviewPage(TWindow* parent,
                                         TPrintout& printout,
                                         TPrintDC&  prndc,
                                         TSize&     printExtent,
                                         int        pagenum)
{
  return new TRichEditPagePreview(parent, printout, prndc,
                                  printExtent, pagenum);
}

} // OWL namespace
/* ========================================================================== */


switch (nCode)
			{
				case HCBT_CREATEWND:

					NST_ASSERT( lParam );

					if (const CREATESTRUCT* const createStruct = reinterpret_cast<const CBT_CREATEWND*>(lParam)->lpcs)
					{
						NST_VERIFY( wParam );
						global.hooks.OnCreate( *createStruct, reinterpret_cast<HWND>(wParam) );
					}

					return 0;

				case HCBT_DESTROYWND:

					NST_VERIFY( wParam );

					if (wParam)
						global.hooks.OnDestroy( reinterpret_cast<HWND>(wParam) );

					return 0;
			}

// Nestopia - NES/Famicom emulator written in C++
// This file is part of Nestopia.

#include "language/resource.h"
#include "NstResourceCursor.hpp"
#include "NstResourceVersion.hpp"
#include "NstApplicationException.hpp"
#include "NstApplicationLanguage.hpp"
#include <CommCtrl.h>
#include <ObjBase.h>
#include <Shlobj.h>
#include <Shellapi.h>

#if NST_MSVC
#pragma comment(lib,"comctl32")
#endif

namespace Nestopia
{
	namespace Application
	{
		const wchar_t Instance::appClassName[] = L"Nestopia";

		struct Instance::Global
		{
			Global();

			struct Paths
			{
				explicit Paths(HINSTANCE);

				Path exePath;
				Path wrkPath;
				Path configPath;//bg configuration files path
			};

			struct Hooks
			{
				explicit Hooks(HINSTANCE);
				~Hooks();

				void OnCreate(const CREATESTRUCT&,HWND);
				void OnDestroy(HWND);

				static LRESULT CALLBACK CBTProc(int,WPARAM,LPARAM);

				enum
				{
					CHILDREN_RESERVE = 8
				};

				typedef Collection::Vector<HWND> Children;

				HHOOK const handle;
				HWND window;
				Children children;
			};

			const Paths paths;
			const Resource::Version version;
			Language language;
			Hooks hooks;
			IconStyle iconStyle;
		};

		Instance::Global Instance::global;
		Instance::Events::Callbacks Instance::Events::callbacks;

		Instance::Global::Paths::Paths(HINSTANCE const hInstance)
		{
			uint length;

			do
			{
				exePath.Reserve( exePath.Capacity() + 255 );
				length = ::GetModuleFileName( hInstance, exePath.Ptr(), exePath.Capacity() );
			}
			while (length == exePath.Capacity());

			exePath.ShrinkTo( length );
			exePath.MakePretty( false );
			exePath.Defrag();

			wrkPath = ".";
			wrkPath.MakeAbsolute( true );
			wrkPath.Defrag();

			configPath.Reserve(configPath.Capacity() + 255);//bg

			//bg
			bool isLua = false;

			//bg check -lua between command line options
			int nArgs;
			LPWSTR *szArglist = ::CommandLineToArgvW(::GetCommandLineW(), &nArgs);
			if( szArglist != NULL) {
				for(int i=0; i<nArgs; i++) {
					isLua = ::wcscmp(szArglist[i],L"-lua") == 0;
					if (isLua) break;
				}

			   ::LocalFree(szArglist);
			}

			//bg END check

			//bg set configPath
			if(isLua &&

					SUCCEEDED(SHGetFolderPath(NULL,
								 CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE,
								 NULL,
								 0,
								 configPath.Ptr()))) {
									 configPath.ShrinkTo( std::wcslen(configPath.Ptr()) );
									 configPath.MakePretty( false );
									 configPath.Append (L"\\Nestopia",9);
									 configPath.MakePretty( false );
									 configPath.Defrag();
									 ::CreateDirectory(configPath.Ptr(), NULL);
				}

			else {
				::GetModuleFileName( hInstance, configPath.Ptr(), configPath.Capacity() );
				configPath.ShrinkTo(configPath.FindLastOf('\\',configPath.Capacity()));//bg fix issue #265
			};

			configPath.Defrag();
		}

		Instance::Global::Hooks::Hooks(HINSTANCE const hInstance)
		: handle(::SetWindowsHookEx( WH_CBT, CBTProc, hInstance, ::GetCurrentThreadId() ))
		{
			children.Reserve( CHILDREN_RESERVE );
		}

		Instance::Global::Hooks::~Hooks()
		{
			if (handle)
				::UnhookWindowsHookEx( handle );
		}

		#ifdef NST_MSVC_OPTIMIZE
		#pragma optimize("t", on)
		#endif

		void Instance::Global::Hooks::OnCreate(const CREATESTRUCT& createStruct,HWND const hWnd)
		{
			NST_ASSERT( hWnd );

			if (window == createStruct.hwndParent || !window)
			{
				{
					static const wchar_t menuClassName[] = L"#32768"; // documented on MSDN

					enum
					{
						MAX_LENGTH = NST_MAX( sizeof(array(appClassName)) - 1,
                                     NST_MAX( sizeof(array(STATUSCLASSNAME)) - 1, sizeof(array(menuClassName)) - 1 ))
					};

					String::Stack<MAX_LENGTH,wchar_t> name;
					name.ShrinkTo( ::GetClassName( hWnd, name.Ptr(), MAX_LENGTH+1 ) );

					if (name.Equal( menuClassName ) || name.Equal( STATUSCLASSNAME ) || name.Equal( L"IME" ))
						return;

					if (window)
					{
						children.PushBack( hWnd );
						Events::Signal( EVENT_SYSTEM_BUSY );
					}
					else if (name.Equal( appClassName ))
					{
						window = hWnd;
					}
					else
					{
						return;
					}
				}

				const Events::WindowCreateParam param =
				{
					hWnd,
					createStruct.cx > 0 ? createStruct.cx : 0,
					createStruct.cy > 0 ? createStruct.cy : 0
				};

				Events::Signal( EVENT_WINDOW_CREATE, &param );
			}
		}

		void Instance::Global::Hooks::OnDestroy(HWND const hWnd)
		{
			NST_ASSERT( hWnd );

			Children::Iterator const child = children.Find( hWnd );

			if (child)
			{
				Events::Signal( EVENT_SYSTEM_BUSY );
			}
			else if (window != hWnd)
			{
				return;
			}

			{
				const Events::WindowDestroyParam param = { hWnd };
				Events::Signal( EVENT_WINDOW_DESTROY, &param );
			}

			if (child)
				children.Erase( child );
			else
				window = NULL;
		}

		LRESULT CALLBACK Instance::Global::Hooks::CBTProc(const int nCode,const WPARAM wParam,const LPARAM lParam)
		{
			NST_COMPILE_ASSERT( HCBT_CREATEWND >= 0 && HCBT_DESTROYWND >= 0 );

			return nCode < 0 ? ::CallNextHookEx( global.hooks.handle, nCode, wParam, lParam ) : 0;
		}

		#ifdef NST_MSVC_OPTIMIZE
		#pragma optimize("", on)
		#endif

		Instance::Global::Global()
		:
		paths     ( ::GetModuleHandle(NULL) ),
		version   ( paths.exePath.Ptr(), Resource::Version::PRODUCT ),
		hooks     ( ::GetModuleHandle(NULL) ),
		iconStyle ( ICONSTYLE_NES )
		{
		}

		Instance::Language& Instance::GetLanguage()
		{
			return global.language;
		}

		const String::Heap<char>& Instance::GetVersion()
		{
			return global.version;
		}

		const Path& Instance::GetExePath()
		{
			return global.paths.exePath;
		}

		const Path Instance::GetExePath(const GenericString file)
		{
			return Path( global.paths.exePath.Directory(), file );
		}

		const Path Instance::GetConfigPath(const GenericString file)//bg
		{
			return Path( global.paths.configPath, file );
		}

		const Path Instance::GetFullPath(const GenericString relPath)
		{
			Path curPath( "." );
			curPath.MakeAbsolute( true );

			const BOOL succeeded = ::SetCurrentDirectory( global.paths.wrkPath.Ptr() );

			Path wrkPath( relPath );
			wrkPath.MakeAbsolute();

			if (succeeded)
				::SetCurrentDirectory( curPath.Ptr() );

			return wrkPath;
		}

		const Path Instance::GetLongPath(wcstring const shortPath)
		{
			Path longPath;

			longPath.Reserve( 255 );
			uint length = ::GetLongPathName( shortPath, longPath.Ptr(), longPath.Capacity() + 1 );

			if (length > longPath.Capacity() + 1)
			{
				longPath.Reserve( length - 1 );
				length = ::GetLongPathName( shortPath, longPath.Ptr(), longPath.Capacity() + 1 );
			}

			if (!length)
				return shortPath;

			const wchar_t slashed = GenericString(shortPath).Back();

			longPath.ShrinkTo( length );
			longPath.MakePretty( slashed == '\\' || slashed == '/' );

			return longPath;
		}

		const Path Instance::GetTmpPath(GenericString file)
		{
			Path path;
			path.Reserve( 255 );

			if (uint length = ::GetTempPath( path.Capacity() + 1, path.Ptr() ))
			{
				if (length > path.Capacity() + 1)
				{
					path.Reserve( length - 1 );
					length = ::GetTempPath( path.Capacity() + 1, path.Ptr() );
				}

				if (length)
					path = GetLongPath( path.Ptr() );
			}

			if (path.Length())
				path.MakePretty( true );
			else
				path << ".\\";

			if (file.Empty())
				file = L"nestopia.tmp";

			path << file;

			return path;
		}

		Instance::Events::Callbacks::~Callbacks()
		{
			NST_VERIFY( Empty() );
		}

		void Instance::Events::Add(const Callback& callback)
		{
			NST_ASSERT( bool(callback) && !callbacks.Find( callback ) );
			callbacks.PushBack( callback );
		}

		void Instance::Events::Signal(const Event event,const void* param)
		{
			for (Callbacks::ConstIterator it(callbacks.Begin()), end(callbacks.End()); it != end; ++it)
				(*it)( event, param );
		}

		void Instance::Events::Remove(const void* const instance)
		{
			for (Callbacks::Iterator it(callbacks.Begin()); it != callbacks.End(); ++it)
			{
				if (it->VoidPtr() == instance)
				{
					callbacks.Erase( it );
					break;
				}
			}
		}

		Window::Generic Instance::GetMainWindow()
		{
			return global.hooks.window;
		}

		uint Instance::NumChildWindows()
		{
			return global.hooks.children.Size();
		}

		Window::Generic Instance::GetChildWindow(uint i)
		{
			return global.hooks.children[i];
		}

		Window::Generic Instance::GetActiveWindow()
		{
			HWND hWnd = ::GetActiveWindow();
			return hWnd ? hWnd : global.hooks.window;
		}

		Instance::IconStyle Instance::GetIconStyle()
		{
			return global.iconStyle;
		}

		void Instance::SetIconStyle(IconStyle style)
		{
			global.iconStyle = style;
		}

		void Instance::ShowChildWindows(bool show)
		{
			const uint state = (show ? SW_SHOWNA : SW_HIDE);

			for (Global::Hooks::Children::ConstIterator it(global.hooks.children.Begin()), end(global.hooks.children.End()); it != end; ++it)
				::ShowWindow( *it, state );
		}

		Instance::Waiter::Waiter()
		: hCursor(::SetCursor(Resource::Cursor::GetWait())) {}

		Instance::Locker::Locker()
		: hWnd(GetMainWindow()), enabled(::IsWindowEnabled(hWnd))
		{
			::EnableWindow( hWnd, false );
			::LockWindowUpdate( hWnd );
		}

		bool Instance::Locker::CheckInput(int vKey) const
		{
			if (::GetAsyncKeyState( vKey ) & 0x8000U)
			{
				while (::GetAsyncKeyState( vKey ) & 0x8000U)
					::Sleep( 10 );

				return true;
			}

			return false;
		}

		Instance::Locker::~Locker()
		{
			::LockWindowUpdate( NULL );
			::EnableWindow( hWnd, enabled );
			for (MSG msg;::PeekMessage( &msg, NULL, 0, 0, PM_REMOVE|PM_QS_INPUT ););
		}

		Instance::Instance()
		{
			if (!static_cast<const Configuration&>(cfg)["preferences"]["application"]["allow-multiple-instances"].Yes())
			{
				::CreateMutex( NULL, true, L"Nestopia Mutex" );

				if (::GetLastError() == ERROR_ALREADY_EXISTS)
				{
					if (Window::Generic window = Window::Generic::Find( appClassName ))
					{
						window.Activate();

						if (const uint length = cfg.GetStartupFile().Length())
						{
							COPYDATASTRUCT cds;

							cds.dwData = COPYDATA_OPENFILE_ID;
							cds.cbData = (length + 1) * sizeof(wchar_t);
							cds.lpData = const_cast<wchar_t*>(cfg.GetStartupFile().Ptr());

							window.Send( WM_COPYDATA, 0, &cds );
						}
					}

					throw Exception();
				}
			}

			if (global.paths.exePath.Empty())
				throw Exception( L"unicows.dll file is missing!" );

			global.language.Load( cfg );

			if (global.hooks.handle == NULL)
				throw Exception( IDS_ERR_FAILED, L"SetWindowsHookEx()" );

			if (FAILED(::CoInitializeEx( NULL, COINIT_APARTMENTTHREADED )))
				throw Exception( IDS_ERR_FAILED, L"CoInitializeEx()" );

			Object::Pod<INITCOMMONCONTROLSEX> initCtrlEx;

			initCtrlEx.dwSize = sizeof(initCtrlEx);
			initCtrlEx.dwICC = ICC_WIN95_CLASSES;
			::InitCommonControlsEx( &initCtrlEx );
		}

		Instance::~Instance()
		{
			::CoUninitialize();
		}

		void Instance::Save()
		{
			global.language.Save( cfg );
		}
	}
}